65.9K
CodeProject 正在变化。 阅读更多。
Home

递归互联

starIconstarIconstarIconstarIconstarIcon

5.00/5 (19投票s)

2016年6月15日

CPOL

39分钟阅读

viewsIcon

21183

downloadIcon

126

面向未来网络发展的方向

引言

如今,网络正在不断发展。虚拟化技术或多或少地出现在了各种技术中,而对更具动态性和可扩展架构的需求促使了软件定义网络(SDN)的发展。这些方法已经建立并部署在了一个经过充分测试和巩固的架构上,该架构在过去30年里不断打补丁以适应新的需求:IP技术。

但是,如果这种“古老而经典”的架构不足以应对未来的挑战怎么办?如果它只是一个更普遍的视图的一个特例,这个视图现在(并且过去也)已经开始出现,但仅仅作为一个有趣的用例而存在?

我将在本文中以非常实用的方式介绍递归互联(Recursive Internetworking)的思想。最初的想法来自John Day,他是一位计算机科学家、网络工程师和地图绘制师,是早期网络先驱之一。如果您想对这个网络分支获得更详细和理论性的信息,我强烈建议您参考《网络架构模式:回归基础》(PNA)一书,这是我为开发这个项目获取灵感的第一个来源。

我提前向该领域的少数专家道歉,如果本文显得过于简单或简化了架构思想的某些细节。本文的范围是介绍这个思想,而不是对其进行深入的理论讲解。

背景

我始终认为,在阅读本文时保持开放的心态非常重要。需要具备一般的网络知识,但由于我将从基础开始讨论架构本身,因此讲解应该是普遍适用的,以便每个人都能理解。

互联的经典视图,TCP/IP栈

TCP/IP栈,也称为互联网协议套件,是一种旨在提供端到端、健壮通信的网络模型。它通过分层方法实现这些功能,定义了一系列层(确切地说,是五层),这些层通过使用不同的协议提供服务。每一层都会封装来自上一层的数据,并提供自身领域内的附加服务。这些层按定义如下:

  • 应用层,其领域涵盖用户定义的应用程序。此服务利用下层来提供客户端和服务器实例之间可靠或不可靠的通信“管道”。
  • 传输层,负责主机到主机的通信,其领域涵盖整个主机。这意味着该层的域比应用层要大,因为它为所有主机用户的所有应用程序提供服务。
  • 网络层,负责在网络边界之间交换数据报。该层具备路由和转发能力,从而允许数据“使用”中间的其他节点来到达目的地。
  • 链路层,负责将信号实际传输到特定技术并对其进行解码的操作。其域仅限于两台物理机器之间,并且没有对整个通信的“宏观视图”。
  • 物理层,即数据传输的“电缆”。这一层不在此讨论之列,因为它无法在逻辑上更改(您需要在现实中手动修改它)。

由于概念可能令人困惑,让我们以我在维基百科上找到的一个例子(参见:https://en.wikipedia.org/wiki/Internet_protocol_suite)为例。您也可以通过阅读该页面上的TCP/IP栈信息来加深您的知识。

在以下示例中,我们有一个客户端通过UDP/IP提供的服务与服务器通信。由于服务器不在本地机器上,而是位于通过一个(或多个)路由器连接的主机上,数据包在从源到目的地的路线上将经过这些节点。

经典视图是对正在发生的事情的一种概括,它通常混合了网络模型的不同层。由于我们讨论的是不同的层,因此我们也应该问自己想从哪个层视图来看。正如您在此图中所见,每一层都有相同的通信视图,顶部右侧框内总结了维基百科示例中的内容。数据报从应用层流向链路层(图中从上到下),并且在整个遍历过程中,它将改变其所在的域。

当数据包在层之间切换并向下传递到下一层时,它会被另一个包含该域相关信息并是其他主机/路由器理解该数据包所必需的信息的头部进行封装。

如果我们观察信息的整个传输过程,我们可以绘制以下图表(我们将其称为“侧视图”,因为它从侧面观察通信)。

如果您沿着从客户端开始并到达服务器的箭头,我们可以一步一步地看到数据发生了什么。每次数据包更改域并从上层流向下层时,都会在旧数据前面加上一个头部,并附加该层正确处理数据所需的信息。这在网络层尤为明显,其中间节点使用IP目标地址字段来确定数据包是否是给它们的,如果不是,则确定到达真实目的地的下一跳是谁。

链路层也执行相同的操作(好吧,每一层都像这样),但当数据包向上移动(到网络层)时,它会去除其信息,并在数据包需要移动到下一跳时重新封装。这是因为网络层的范围比链路层更大;事实上,它位于其之上!

如果我们现在查看数据(data-view图),我们可以通过计算其前面附加了多少层头部来确定数据包正在遍历多少层。

在路由器上的上升/下降过程中,绿色部分(与链路层相关)是不断变化(或更新)的部分,因为即使它保持在同一级别,其域也会改变。我们并非总是使用相同的链路对,否则数据报将始终保留在同一链路上而不会改变(因此永远无法到达目的地)。当数据由网络层处理时,以太网头部的源和目标地址会发生变化,当这些地址发生变化时,就意味着它将使用一种不同类型的链路,这种链路可能有不同的域。

正如您在此图中所见,每一层的域都定义得相当好。在链路层,我们有3个不同的域,这使得数据包有可能到达目的地。由于链路层是最低层,您可以将这些层理想地匹配到以太网电缆本身:它具有一对明确定义的接口,并且在电缆上传输的信息仅对该电缆有意义(因为如果您更换电缆,接口也会随之改变)。网络层(高一层)的域更广,因为它提供了对在该小型通信示例中连接所有节点的链路的抽象。这就是互联网(例如,IPv4),它使您的数据包能够移动到人类至今为止建造的最复杂的架构中。在传输层,您可以找到一个更受限的域,它被限制在单个主机内,并允许您瞄准主机内的特定服务(因为主机可以提供不止一种服务)。

我希望您仍然能跟上我的思路… :)

分层之必要

网络栈的定义结构在网络发展史上一直非常好地服务于我们,直到最近,网络对更多组织性的需求日益增长。可以看到,使用附加层的第一个尝试,例如,在引入了VPN等点对点协议时,甚至更早是在引入网络地址转换(NAT)时。

NAT的引入是为了解决IP网络层的一个大问题,即(现在仍然是)地址耗尽。由于IPv4的地址是32位宽的,这意味着在该层您“只能”拥有大约四亿个可能的独立节点。因此,引入了额外的域(在网络层),试图解决这个问题(但问题并未解决,只是被推迟到了今天)。

网络工程师基本上预留了一些地址,用于构成“私有”网络。在公共IP中,不允许使用这些地址(即经典的10.x.x.x和192.168.x.x,以及较少使用的172.16.x.x)进行分配。当流量流经您的网络到达公共IP时,其源地址会被路由器地址替换,路由器会记住这次交换。现在数据包以“公共IP”地址在网络中传输,并能够到达网络另一端的目的地。

另一种不同类型的附加层示例是虚拟专用网络(VPN)的引入。引入这项技术是因为需要从IP公共域连接到一个“私有网络”(因为它已经被NAT化以节省地址)。利用现有的IP网络为此虚拟网络提供服务,VPN允许主机连接到通常“从外部无法访问”的安全域(内部网络)。这通常是通过在内部头部之后添加其他头部来实现的,以便它能在公共网络中传输。由于公共网络不保证安全(您永远不知道数据包将转发到哪里),安全和加密机制也与附加头部绑定在一起。

还有许多其他协议/配置示例改变了TCP/IP栈的经典方法,使其不是解决特定问题的最佳选择。例如,可能存在与经典视图不兼容的新技术,需要附加的“层”来更好地组织通信(例如,无线技术的链路层被分为两层)。

尽管IP网络本身已经服务了我们很长时间,并且做得很好(否则它早就崩溃了),但现在我们正接近该思想的“极限”,我们需要新的、进化的东西。请注意,它不仅必须在保留旧配置的同时实现这一点,还必须包含正在出现的新网络概念。

递归网络

当我们谈论递归网络时,我们首先必须介绍两个必须提前了解的概念。在非常简短的介绍之后(要看非常理论的部分,请阅读PNA),我将开始展示经典栈的示例如何也原生适用于此类架构。

这些概念是:

  1. 互联是IPC通信!
    客户端和服务器(或任何组件)只是相互交换信息,而不考虑端点的本地性。它应该并且必须不关心它可以使用哪种类型的网络,而是注册到某个提供必要服务以到达端点的IPC。IPC将提供一系列QoS(服务质量),应用程序可以选择使用或不使用。
  2. IPC机制在不同层上递归地提供其服务。
    这意味着您只有一种机制,它与其他组件完全隔离,并且您可以拥有各种实例,这些实例组织成一个提供一系列服务的栈。与经典栈一样,每一层都为通信提供附加服务,您可以选择增强、组织或保护通信。

这背后还有很多内容,但我没有能力也没有耐心解释一切(文章将永无止境)。我花了一年时间才理解(并接受!)这种新的架构。

因此,让我们重新从经典TCP/IP网络栈的第一个示例开始。在顶层,细节变化很少:您仍然有两个应用程序(进程)希望通过使用某种QoS(可以是“尽力而为”或其他更精细的配置文件)来交换数据。为了做到这一点,它们都将使用相互连接的IPC进程(我们称之为“注册”操作),并提供所需的服务。

请注意,您不再需要了解您正在使用的IPC之下的细节。您不再关心它:只要IPC提供连接服务,通信就可以发生。这就像软件中的黑盒概念,允许您在不干扰栈配置的情况下更改/适应底层网络。

每个IPC都提供足够的通信功能来在其实际服务的网络中分发数据包,因此我们不再需要两个协议协同工作来提供服务(TCP没有IP就无法工作,因为其域仅限于主机)。这意味着一层足以瞄准特定端点并将其分发给它。

正如您在图中看到的,我安排了网络以实现类似于经典TCP/IP栈的案例:一个名为A的应用程序希望与另一个名为B的应用程序通信。为了能够交换数据,应用程序必须注册到黄色层的IPC A和IPC B,您可以将其视为连接到世界各地的IPC层(IP递归版本)。递归地,黄色层的IPC A也注册到绿色层的IPC A,以到达第一个路由器,这是其通信的下一跳。绿色层在此情况下提供了对以太网的抽象,因此具有设置数据报以供NIC处理的必要知识。

如果我们现在查看栈组织的“侧视图”,我们看不到与之前的经典栈有大的区别。请注意,由于我们使用递归,所以所有层之间都有IPC到IPC的通信,而不仅仅是TCP/IP栈的传输层或应用层。在每一层,它似乎直接与目的地通信(中间可能有转发,这在图中仅发生在黄色层)。

那么,通过这些递归层的数据发生了什么?基本上,每当数据包遍历一层(向上或向下)时,它就会被该层所属的IPC处理。在此操作过程中,会分析数据包的头部,以检查其目的地以及该目的地是否可达。如果可以,那么数据包将被封装在附加的层头部中,使其可以在该网络中合法传输,并分发到同一层的下一跳(例如,从黄色A到黄色B,数据包必须经过R1)。如果IPC抽象了某种技术,那么递归就会停止,因为数据被交给NIC或该技术的北向接口。如果下面还有更多层,那么过程会重复。

请注意,每一层都可以按照其认为最合适的方式来组织数据,并且只要数据以到达的方式返回,这种组织就不会影响任何上层或下层。例如,技术抽象层可以决定在数据的尾部添加CRC校验以检查一致性或恢复任何错误,只要数据在剥离附加头部后返回,这对黄色层来说是透明的。

该系统与TCP/IP非常相似。不,等等……它或多或少是相同的,只是现在更加通用了。出于简化的目的,给出的示例保持不变,但您可以根据需要添加任何层,而无需重新设计/修复/破解网络栈本身。这种行为现在是设计上支持的,并且实际上鼓励您利用此功能。给出的简单示例仅仅是一个特殊情况,当您想要一个跨越整个网络的单一、宽泛的层,而不是更精细的组织时。

那么,之前在TCP/IP栈中看到的NAT和VPN示例应该如何处理呢?
简而言之,它们合并成了一个情况,因为分层同时提供了私有环境(向底层隐藏上层组织),您可以在其中设置自己的节点集,以及一个私有命名域(您可以决定使用什么名称让底层IPC注册)。

不仅如此,递归网络还通过设计引入了一些目前新兴的概念,例如软件定义网络(SDN)。事实上,SDN旨在通过网络与数据平面分离来通过网络抽象管理网络服务。递归网络已经提供了通过分层来塑造和隔离网络的能力,因此您可以决定流量将通过哪些节点,只需创建一个私有层即可(通过注册IPC,使它们被视为邻居)。例如,您可以在前面示例的全局(黄色)网络之上构建一个网络,使用其中的一些节点。您注册各种元素的方式创建了节点之间的链接(注册表示邻居),从而创建了您想要的网络。基于这个“私有网络”创建的应用程序将不知道黄色层的组织方式,也不知道对其发生的任何更改(除了延迟或网络重新配置期间的服务中断)。

正如您所见,这个概念非常简单,并且所有简单的事物都留下了许多开放的选项。另一方面,谈论这种新的网络概念会让很多人头痛和产生疑问,因为除了一些非常实验性的原型可以帮助您了解之外,没有真正的解决方案可用。

一切都是纸上谈兵?

在说了这么多话之后,您心中可能有一个问题:“伙计,这是CodeProject……代码在哪儿?”,您可能是对的。但我来这里不仅仅是为了介绍这个想法,也是为了提供一个原型(一个中等规模的)。

该栈完全在内核模式下开发(正如所有网络栈应该的那样),并且可以作为内核模块加载,而无需使用特殊的Linux发行版(目前唯一支持此技术的操作系统是Linux)。

该项目在开发时考虑了简单性、可部署性和性能。因此,您可以在大约5分钟内克隆、构建并将其加载到您的内核中。查看“documentation”文件夹以获取更多信息,特别是关于它已经过开发和测试的系统(主要是Debian和Arch系统)。由于正如我所说,可移植性和性能是项目的重点,您也可以在低功耗设备(如Raspberry Pi)上运行此系统(整个项目已在Raspberries版本1、2和3以及Debian交换机之间进行了测试)。

正在进行中

由于我们谈论的是原型,而且是在内核级别,请确保在测试此软件时不要使用您的普通机器,而是使用虚拟机或专用机器。内核恐慌现在已经非常罕见了,但我是一个人,错误可能潜伏在我尚未考虑到的部分。

该架构已经实现了良好的性能并引入了最小的延迟,但仍未优化,我正在准备一些更新(未来),以便利用现有的技巧(TCP/IP栈使用的)来提高其效率。

编译和加载

首先,您应该将项目克隆到您的Linux系统。构建和运行此项目的前提条件非常少,它们列在documentation/install文件中。请注意,在编译阶段,目前项目假定项目根目录为/RNS。当然,您可以更改它,但您必须记住修复Makefiles以指向正确的项目根文件夹。

# Root directory.
ROOT := /RNS

此行位于项目每个Makefile的顶部。

您可以使用我随项目提供的一组脚本来加快操作速度:在linux/scripts项目子目录中,您会找到一组功能。如果您从shell调用compile.sh,您将能够立即准备好它(在Raspberry版本1上大约需要1分钟)。

要将其加载到您的内核中,您可以以类似的方式调用load.sh脚本。一旦加载步骤正确完成(您应该能够通过dmesg实用程序看到),一些服务消息将告知您一切正常运行。

此类输出的示例是:

RNS successfully loaded with status 0

Unload

当您想开始移除栈中的所有内容时,只需调用linux/scripts中的unload.sh脚本即可。如果出现问题,这也是您可能会遇到错误的时候,这些错误可能是沉默的,也可能不是。如果您引入新模块,最常见的错误之一是:

Memory use mismatch!

这通常表示在清理操作完成后,您遗留了一些内存,这些内存将丢失直到重启。如果出现这种情况,请再次检查是否每个分配都有相应的释放程序来释放保留的内存!

创建两个通信应用程序

要使两个或多个应用程序使用栈提供的服务,您必须先进行配置。要与栈进行交互,已经有一个工具可用,称为rnsctrl(RNS control)。请注意,正确使用此工具可能需要root权限,因为它需要与设备驱动程序交互才能将命令发送到内核级别。

该工具可以在存储库的linux/tools/bin子目录中找到,所有编译好的工具都存储在这里。如果您不带任何参数或使用--help选项调用该工具,您应该能够看到它可以对栈执行的操作。此工具目前为您提供了对内核栈的完全控制

您必须做的第一件事(在调用load.sh脚本后)是创建一个IPC管理器,这是一个栈容器。这意味着,从设计上讲,您也可以在同一台物理机上创建逻辑上分离的网络栈。

root@64:/# /RNS/linux/tools/bin/rnsctrl --create-ipcm
Going to create a new IPCM...
IPCM 1 successfully created...

一旦IPC管理器运行,您就必须创建一个IPC进程,该进程将为两个应用程序提供通信服务,这可以通过以下方式完成:

root@64:/# /RNS/linux/tools/bin/rnsctrl --create-ipcp 1 L 1 ""
Going to create a new IPCP...
IPCP 1 successfully created...

此命令将在IPC管理器1(新创建的那个)中创建一个IPC进程,将其命名为“A”,并指示它遵循模型1(由load.sh脚本加载),不带任何参数。在RNS中,模型是一种对栈本身触发的事件做出反应的方式。这样,您就可以发布各种模型,并决定新的IPC进程将如何表现。例如,抽象技术的IPC不需要更改栈本身,只需要发布一个新模型,这是一个独立的内核模块。

模型1是RNS IPC模型,它为IPC提供通用的递归行为。

好的,现在栈已经配置好了,您只需要创建应用程序。我已经随栈提供了一些用户空间工具,我使用它们来验证性能和机制是否正确。其中一个应用程序是RNS的原生ping,我们将使用它。一如既往,只需不带参数或使用--help选项运行应用程序即可获得有关其功能的更多信息。

对于服务器,请键入:

root@64:/# /RNS/linux/tools/bin/rnsping 1 1 a 2 --listen

这将 IPC 管理器 1(IPC 进程 1)上的 ping 实用程序,将其在栈内的名称指定为“a”,并要求使用 QoS 类 2(目前,QoS 的唯一重要字段是 ID)。它将保持静止,监听任何想与它交换消息的人。

对于客户端,请键入:

root@64:/# /RNS/linux/tools/bin/rnsping 1 1 b 2 a

这将在 IPC 管理器 1(IPC 进程 1)上运行 ping 实用程序。它将将其在栈内的名称指定为“b”,并要求使用 QoS 类 2。现在,而不是监听,您将提供您想通信的应用程序的名称(即服务器a)。如果一切成功,您将能够看到以下日志跟踪:

Starting client b(2)...
64 bytes from a: token=1, time=xxxx ms
64 bytes from a: token=2, time=xxxx ms
64 bytes from a: token=3, time=xxxx ms
64 bytes from a: token=4, time=xxxx ms

如果图表能使其更清晰,这里有一张图片显示了正在发生的事情。

添加更多层

如何使用栈取决于您,但由于我一直在谈论递归,所以最好展示一下RNS中的实际情况。在实践中,您只需重复之前使两个应用程序相互通信的操作。在栈配置期间,您只需要添加更多的IPC进程,始终使用相同的命令,例如:

root@64:/# /RNS/linux/tools/bin/rnsctrl --create-ipcp 1 A 1 ""
Going to create a new IPCP...
IPCP 1 successfully created...
root@64:/# /RNS/linux/tools/bin/rnsctrl --create-ipcp 1 A 1 ""
Going to create a new IPCP...
IPCP 2 successfully created...

现在您有了两个IPC进程,您只需要将一个注册到另一个,使用以下命令:

root@64:/# /RNS/linux/tools/bin/rnsctrl --register-ipcp 1 1 2
IPCM 1 is going to register IPCP 1 on 2...
IPCP 1 successfully registered on IPCP 2...

正如您所见,您总是需要提供IPC管理器上下文来进行操作,因为您需要指定操作在哪个逻辑栈中进行。我再次提醒您,不带参数或使用--help标志运行项目中的任何实用程序都将提供可用功能列表。

最基本的网络

由于让两个ping应用程序以简单的方式通信很无聊,让我们配置一个更复杂的栈,并进行点对点设置。使用现有的栈将允许您真正地模拟小型网络(因为IPC视图允许您抽象所使用的技术):您需要做的就是根据需要进行设置、注册和注册IPC。

假设我们希望两个应用程序通过单一层次进行通信:我们总共需要两个IPC(每个应用程序一个),以及一个提供“loopback”服务的底层。由于IPC现在位于同一层,您不能再为它们分配相同的编号,而是需要识别您自己的寻址方案。

如果您从一个干净加载的栈开始(因此卸载并重新加载它,以便进行完全清理),您可以执行以下操作:

root@64:/# /RNS/linux/tools/bin/rnsctrl --create-ipcm
Going to create a new IPCM...
IPCM 1 successfully created...

root@64:/# /RNS/linux/tools/bin/rnsctrl --create-ipcp 1 A 1 ""
Going to create a new IPCP...
IPCP 1 successfully created...

root@64:/# /RNS/linux/tools/bin/rnsctrl --create-ipcp 1 B 1 ""
Going to create a new IPCP...
IPCP 2 successfully created...

root@64:/# /RNS/linux/tools/bin/rnsctrl --create-ipcp 1 L 1 ""
Going to create a new IPCP...
IPCP 3 successfully created...

现在您创建了3个IPC进程:两个停留在同一层(A和B),最后一个(L)提供环回服务。下一步是将A和B放在L之上,这样它们就处于同一级别,然后只需让它们注册以开始交换转发信息(例如,谁是其他可达IPC,以及您可以通过它们到达哪些AE)。

进行此操作,您只需:

root@64:/# /RNS/linux/tools/bin/rnsctrl --register-ipcp 1 1 3
IPCM 1 is going to register IPCP 1 on 3...
IPCP 1 successfully registered on IPCP 3...

root@64:/# /RNS/linux/tools/bin/rnsctrl --register-ipcp 1 2 3
IPCM 1 is going to register IPCP 2 on 3...
IPCP 2 successfully registered on IPCP 3...

root@64:/# /RNS/linux/tools/bin/rnsctrl --enroll-to 1 1 B 3
IPCP 1 is going to enroll to B using 3...
IPCP 1 successfully enrolled to B...

最后一个命令将要求IPC进程1注册到“B”,使用IPC进程3(即环回一个)提供的服务。然后,您只需重复旧的ping应用程序操作,就应该能够让两个实例通过一个简单的“虚拟”链接进行通信。

所以,对于服务器:

root@64:/# /RNS/linux/tools/bin/rnsping 1 1 a 2 --listen

而对于客户端:

root@64:/# /RNS/linux/tools/bin/rnsping 1 2 b 2 a

请注意,第二个数字不同,因为应用程序将使用不同的IPC进程来访问通信服务。服务器将注册到IPC 1(即A),而客户端将使用IPC 2(即B)。

再次,如果图表能使其更清晰,这里有一张图片显示了正在发生的事情。

摆脱本地节点

正如我所说,您可以通过在本地PC中创建IPC进程,然后以正确的方式注册/注册它们来创建一个小型网络并模拟RNS的工作方式。但是,仅限于本地机器对于互联来说可能非常无聊且不太有用,这就是为什么RNS附带UDP IPC模型的原因。该模型允许您创建抽象UDP/IP技术的IPC进程,从而让您能够到达UDP本身可以到达的地方。

使用此模型,我和一位同事进行了一些跨大西洋ping实验(使用现有的IP网络),在延迟方面取得了与ICMP相媲美的性能。当然,它不可能更快,因为它建立在UDP之上,但至少栈不会在通信中引入大的性能损失(在这个级别上这并不容易做到)。

一如既往,使用提供的脚本加载一个干净的栈,然后在将要通信的两个机器上进行配置。这意味着机器1应配置为(记住调整IP地址以匹配您的配置):

root@64:/# /RNS/linux/tools/bin/rnsctrl --create-ipcm 
Going to create a new IPCM... 
IPCM 1 successfully created... 

root@64:/# /RNS/linux/tools/bin/rnsctrl --create-ipcp 1 A 2 "12345,B:192.168.1.2:12345" 
Going to create a new IPCP... 
IPCP 1 successfully created...

而机器2应配置为(同样,请修改IP地址以匹配您的设置):

root@64:/# /RNS/linux/tools/bin/rnsctrl --create-ipcm
Going to create a new IPCM...
IPCM 1 successfully created...

root@64:/# /RNS/linux/tools/bin/rnsctrl --create-ipcp 1 B 2 "12345,A:192.168.1.1:12345"
Going to create a new IPCP...
IPCP 1 successfully created...

这里发生的变化是:

  • IPC的模型现在不同了,因为我们不再使用类型1,而是使用类型2(这些ID是我在编译和加载IPC模型时决定的通用ID),它是UDP抽象IPC。
  • IPC的参数现在不是空的,而是包含使用抽象技术到达目的地所需的信息。首先,我们得到IPC将监听的UDP端口,即12345;然后我们得到IPC A在IP地址192.168.1.1,端口12345上可用的信息。我们需要这些信息,因为目前RNS没有名称解析器(DNS)。

剩下的就是将一个IPC与另一个IPC注册,以便它们创建一个“网络”并开始交换转发和路由信息。例如,在机器1上,您可以简单地发出命令:

root@64:/# /RNS/linux/tools/bin/rnsctrl --enroll-to 1 1 B -1 
IPCP 1 is going to enroll to B using -1... 
IPCP 1 successfully enrolled to B... 

由于我们下面没有其他IPC了(我们使用的是UDP),我们将最后一个数字,即用于执行注册的IPC编号,设置为-1(无效)。如果消息成功离开机器而没有错误,则会给出成功消息,但不能保证它会到达目的地,因为我们使用的是UDP。如果我们开始从已注册的IPC接收信息,那么我们就得到了IPC接受我们注册的确认(这个握手机制可以做得更健壮;目前对我来说已经足够了)。

所有其他过程几乎保持不变!这意味着顶层的应用程序仍然使用相同的命令行进行通信:实际上,它们不知道(也不关心)为它们提供服务的内容,只要它们遵守它们请求的QoS即可。

所以,对于服务器(在机器1上):

root@64:/# /RNS/linux/tools/bin/rnsping 1 1 a 2 --listen

而对于客户端(在机器2上):

root@64:/# /RNS/linux/tools/bin/rnsping 1 1 b 2 a

模型SDK

正如我所说,该项目在设计时就考虑到了简单性,因此它必须提供一种个性化通信的各个方面的方法,而无需修改核心栈本身。这是一个重要的方面,因为TCP/IP栈当前的一个限制是它的僵化,以及在需要执行特殊操作时必须直接修改它的需求。

RNS栈中的各种元素遵循模型执行操作,这些模型告诉它们在不同情况下的行为方式。您可以将模型(从技术角度看)视为一个独立的内核模块,它向栈提供一些回调函数。当您注册一个模型时,您需要为其分配一个ID(这就是为什么我将RNS IPC模型分配ID 1,将UDP IPC模型分配ID 2)。所有栈实例之间维护一个共享的目录,因此您可以为内核中的每个IPC管理器发布模型。

您可以个性化的元素是:

  • IPC,主要允许您抽象一项技术,并决定递归如何在栈中进行。IPC模型还决定如何将上层的QoS配置文件“翻译”到您的一个层上,以便为通信使用正确的配置文件。IPC的另一个决定是如何处理注册。为了简单起见,这两个操作合并在这里,但很可能在不久的将来会引入一个个人SDK。
  • 流量分配器,允许您决定在新流需要建立时执行什么操作。这是所有流的全局视图,因此可以决定如何重新分配它们,何时接受或拒绝通信。当前提供的标准分配器执行隐式流定义,因此在第一个数据包流经IPC时创建流。这是策略问题,您可以决定使用更显式的机制(您只需要自己编写)。在流创建过程中,您还可以决定将某个模型应用于某些流,例如,遵循特定QoS的流。
  • ,允许您决定如何以流粒度处理通信。这些非常重要,因为您可以在这里引入个性化和状态机,例如,如果您想为RNS构建一种TCP。在流操作期间,您还可以添加/删除自定义附加头部,或者您喜欢的任何其他内容:您对流数据拥有完全控制权。
  • Pdu转发信息器,允许您决定对数据报应用哪种类型的路由和转发决策。如果您想开发特定的路由策略(如Dijkstra、ECMP等),请在此处构建逻辑。这些模型还允许您格式化和解码转发信息更新消息。
  • RMT,允许您在RMT级别引入自定义调度算法,这意味着对每个转发的数据报应用决策。如果由于某种原因,模型不允许数据包通过,则会报告错误并调用调度事件。此外,您还可以为每个转发(传递到较低层)或处理(传递到较高层)的数据包应用某些决策。

QoS注册一样,未来可能会引入新的SDK系列,但目前,这些是您可以选择个性化的唯一工具。由于模型是RNS网络栈中执行某些操作的自定义方式,因此您可以自由使用您喜欢的策略来处理您的流量。与核心部分不同,您无需在存储库中共享它们,因为它们是独立的组件,可以包含专有/专利逻辑。

每个模型都独立于其他类型的模型,因此您可以选择用一个模型替换另一个模型,而无需更改整个IPC。当然,可能存在需要两个或多个组件协作以确保特定服务的情况,但在这种情况下,您需要自己将它们绑定在一起并确保IPC中没有混乱。这种情况的一个例子是流量分配器和流模型必须共存,其中FA需要将特定的流模型分配给具有特定QoS的流,或者RMT利用特定流模型放置的附加头部。

从经典TCP/IP栈到递归网络的平稳过渡

现在是时候对从经典TCP/IP栈视图到递归视图进行一些交易考虑了。在过去的几年里,成百上千的公司基于经典方法和IP技术(无论是软件还是硬件)开发了基础和高级的网络技术解决方案。所有这些努力都会被浪费吗?我们必须从头开始,否定所有已完成的解决方案吗?

我的个人答案是:不!

我们迄今为止取得的成就不能被摧毁。网络已经变得过于复杂,无法考虑重置所有功能以引入新技术。如果必须包含某些内容,则必须以平稳的方式进行,并且该技术必须保证与旧基础设施的兼容性。

如果您将架构想象成乐高积木,您必须能够取下每一层(积木)并按照您想要的方式或网络本身的要求进行组织。在这种观点下,TCP/IP层(由默认网络栈提供)只是您决定放在顶部或底部的积木。通过将其放置在不同的级别,您可以从技术中获得不同的服务,这些服务是:

  • 如果您将TCP/IP放在顶部,您将能够使用递归网络上的现有遗留IP应用程序。然后,您可以更好地组织(也可以分层组织)TCP/IP下方的网络,而无需TCP/IP意识到更改。您还可以编写一个抽象特定技术的IPC,让TCP/IP在任何技术上透明工作,而几乎不需要付出任何努力(记住,就像乐高积木一样,您在底部附加必要的逻辑)。

  • 如果您将TCP/IP放在底部,您可以将其用作提供全球连接的技术,并添加额外的自定义服务来处理流量或路由,以更好地塑造流动的流量。这就是示例中使用的技术,其中我们有原生RNS应用程序运行在UDP之上。

我还想指出,我以TCP/IP为例是为了清晰地呈现技术,但理想情况下,这可以与您喜欢的任何技术一起完成。

一切都是纸上谈兵……又一次?

又来了……我不是只提供未来项目的想法,而是提供可行的原型(好吧,我在这里说了一个矛盾)。除了RNS,您还可以找到一个工具,可以过滤所有IP流量并将其重定向到RNS子系统;然后,这些流量会穿过栈及其组织(您可以根据需要决定和个性化),并被传递到另一个主机,该主机将在有效的接口上接收有效的IP流量。这使得遗留应用程序可以无需任何更改,只需最少或无需重新配置,即可在递归网络技术上运行。

Balcora项目通过创建内核级应用程序提供这些功能,这些应用程序只有一个任务(类似于内核级别的守护进程),即过滤通过网络设备的IP流量并将其泵入RNS栈。当从递归(底层)层接收到此类流量时,它会被注入回经典网络栈。上层应用程序(如Web浏览器)不会意识到更改(除了少量的额外延迟),因此可以继续正常工作。

Balcora项目不是在栈的正常编译操作期间构建的,但您可以在linux/tools/balcora下找到它。只需调用Makefile即可构建它(一如既往,如果您将项目克隆到与/RNS不同的路径,请记住调整项目根文件夹)。

cd /RNS/linux/tools/balcora/
make
insmod ./balcora.ko

这将创建balcora.ko内核模块和一个名为balctrl的用户空间应用程序,它是您需要创建内核级AE并将其绑定到接口的控制实用程序。您现在可以配置RNS栈以匹配远程通信的配置,这是我们几章前已经看到的示例。

从干净加载开始,您应该将机器1配置为(修改IP地址以匹配您的设置):

/RNS/linux/tools/bin/rnsctrl --create-ipcm 
Going to create a new IPCM... 
IPCM 1 successfully created... 

/RNS/linux/tools/bin/rnsctrl --create-ipcp 1 A 2 "12345,B:192.168.1.2:12345" 
Going to create a new IPCP... 
IPCP 1 successfully created...

而机器2应配置为(同样,修改IP地址以匹配您的设置):

/RNS/linux/tools/bin/rnsctrl --create-ipcm
Going to create a new IPCM...
IPCM 1 successfully created...

/RNS/linux/tools/bin/rnsctrl --create-ipcp 1 B 2 "12345,A:192.168.1.1:12345"
Going to create a new IPCP...
IPCP 1 successfully created...

然后,为了在这些节点之间创建网络,您需要像这样注册它们:

/RNS/linux/tools/bin/rnsctrl --enroll-to 1 1 B -1
IPCP 1 is going to enroll to B using -1...
IPCP 1 successfully enrolled to B... 

到目前为止没有问题,这与以前完全相同。不同之处在于,您现在必须绑定一些接口,以便它们可以被IP过滤或注入来自RNS的传入流量。

由于您无法过滤您已经在两台机器之间使用的网络设备(否则,您将无法再通信),您需要另一个临时设备。如果您使用Virtual Box(或其他虚拟机),您可以在运行VM实例之前添加额外的接口,或者如果您使用的是Raspberry设备/真实PC,您可以使用TUN/TAP服务(balcora可与网络设备一起使用,因此TUN也受支持)。

对于机器1,请执行类似的操作:

ifconfig eth2 192.168.200.1 netmask 255.255.255.0

而对于机器2:

ifconfig eth2 192.168.200.2 netmask 255.255.255.0 

这个想法是让两台机器上的接口相互通信,就像它们直接对话一样,但实际上RNS在eth1上用于完成真正的通信。两个应用程序(图中为'c'和's')没有意识到数据流的重定向。最终,这就像一个用于递归网络的TUN/TAP设备,它在两种架构之间提供翻译。

如果您尝试在两个接口之间ping,则不会发生任何事情,因为机器上默认未激活路由规则或IPv4转发,因此栈无法解析流量应该去哪里(记住,您有两个额外的接口,但它们之间没有“电缆”)。

如果您像这样配置baclora实例,在机器1上:

/RNS/linux/tools/bin/balctrl --create eth2 1 1 B 2 A

而在机器2上:

/RNS/linux/tools/bin/balctrl --create eth2 1 1 A 2 B

您现在可以成功使用遗留ping、iperf和其他技术(如SSH)而没有任何问题(好吧,balcora处于Beta状态,因此在某些情况下性能可能会较低)。事实上,创建了两个应用程序实例,一个名为A在机器1上,另一个名为B在机器2上,它们简单地使用RNS网络上的QoS 2(请参阅baclctrl --help以获取有关命令行语法的更多信息)交换IP流量。

再次,需要一张图来更好地解释这里发生的事情。

由于我们谈论的是某种乐高积木,您现在可以将最底层的替换为纯以太网、纯WiFi、LTE技术或任何提供通信服务的技术,并且遗留的ping应用程序可以继续正常工作(因为它完全被抽象了)。您还可以选择在私有网络层附近插入更多层,即使这样也不会破坏服务(或者至少您不需要重新配置最顶层的元素)。

来自内核空间的反馈

由于整个栈驻留在内核空间,如果没有反馈机制报告其状态,您如何知道您做的事情是正确的?

由于我需要调试和改进它(而且我想以平稳的方式进行),有几种方法可以从用户空间获取反馈。如果您只想使用它,获取来自栈的数据的最佳位置是/sys/rns下的sysfs文件夹。该目录下的文件和文件夹结构将完全匹配内核中的情况,并提供一些额外信息,您可以使用这些信息来检查您的配置是否正确。

如果您想编写代码并将其加载到栈中,那么必须取消注释您可以在core/kal/linux文件夹中的debug.h文件开头找到的预处理器变量DEBUG_VERBOSE。记住在完成后将其注释掉,否则您会用大量详细的调试信息淹没您的机器(这是开发人员和测试人员的主要信息来源)。

结论

最后,文章结束了……它确实很大(对我来说),我通常更喜欢简短精确的文章。但由于这种架构非常少见,并且人们对它的了解不多,所以我不得不通过一个恰当的例子来介绍它。

您在这里看到的内容只是递归互联技术冰山一角;这里有一个新的世界等待着您去发现。您不再受制于庞大、单体的、难以处理的实现,而是可以根据需要(并且这是设计使然)扩展IPC的数量和网络复杂性。模型SDK允许您引入自定义策略来研究您的个人转发模式或网络上的拥塞控制逻辑的行为。

您不仅可以设置一个原生的递归网络,还可以利用它来增强现有IP网络栈的通信。您还可以将其用作抽象层,将现有网络迁移到需要复杂、临时实现的其他技术上。

如果您有任何技术问题(或侮辱),我将随时为您解答,并且我还会努力在项目存储库中创建更广泛的文档。您可以通过查看documentation/credits.txt文件(这是贡献者列表)找到我的电子邮件。

历史

  • 2016年5月25日 - 开始撰写文章
  • 2016年6月15日 - 发布文章的第一个版本
  • 2018年10月9日 - 由于Github项目被移除,附加了源代码文件
© . All rights reserved.