DECnet 网络协议
DECnet 由 Digital 设计,旨在互连其系列产品。在其于 1983 年发布的 Phase IV 实现中,它可以支持 63 个区域,每个区域包含 1023 个节点。DECnet Phase IV 的规范是免费提供的(请参阅“资源”),这使得其他厂商可以在诸如 Sun 的 Sunlink DNI 和 Linux 等产品中提供 DECnet 连接。
在网络术语中,DECnet 是一个旧标准。其有限的地址空间远小于 TCP/IP,并且不具备更现代网络标准的先进特性。DECnet 仍然广泛应用于遗留系统中,而 Linux DECnet 项目的目的是允许这些系统与基于 Linux 的解决方案集成。
Linux DECnet 项目计划仅支持 Phase IV DECnet,因为目前使用的早期版本数量非常有限。LAT 是 Digital 设计的另一种网络协议,将不被支持,因为它受专利保护且其规范未免费提供。在本文中,我们将使用术语“DECnet”来指代 DECnet Phase IV 协议族。
DECnet 可以通过各种不同的数据链路层传输。最初,Linux 内核 DECnet 层将仅支持以太网链路层;稍后将添加对其他链路层(如 PPP、DDCMP 和 X.25)的支持。PPP 链路层在 RFC1762 中描述,其他链路层在 DECnet 文档中描述(请参阅“资源”)。
像许多网络协议一样,DECnet 可以被视为由多个软件层组成。更多详细信息包含在名为“内核源代码导览”的部分中。
堆栈的顶部是应用层,其中包括日常使用的所有程序。这些程序使用系统库和系统调用来创建与其他节点的连接。内核套接字层接口和系统库包含 DECnet 标准中称为会话控制层的内容。它执行的功能与 TCP/IP 的库和系统调用基本相同。更下层是网络服务协议 (NSP),其功能与 TCP 非常接近。再往下是路由层,它不仅仅是路由;它是一种 IP 和 ARP 的结合体。最底层是实际传输数据的设备。
每台机器(在 DECnet 网络中称为节点)都由一个地址标识,该地址由一个 6 位区域号和一个 10 位节点号组成。这两个数字用点分隔书写,因此 1.2 是区域 1 中节点号为 2 的计算机。与 TCP/IP 不同,地址指的是计算机,而不是进行通信的接口。
有两组不同的补丁可用于向内核添加 DECnet 支持。当前可用的代码基于 Eduardo Serrat 为 2.0.xx 版本内核编写的补丁,用于充当终端节点。与此同时,作者之一 Steve Whitehouse 也在编写一个 DECnet 层,重点是创建路由器实现。
结果是,您现在可以获得 Eduardo Serrat 编写的用于 2.0.xx 内核的原始补丁(相同代码移植到 2.1.xx 内核系列的某个版本),以及另一个补丁,该补丁由 Steve 修改,以利用 2.1.xx 内核系列中较新的支持函数。最后一个补丁将作为 2.2.xx 内核系列的附加组件分发,并在稍后集成到下一个开发系列中。
我们在此处说的大部分内容同样适用于所有版本的内核补丁。我们将指出我们遇到的差异。
首先,您需要检索适用于您内核的正确补丁。一般来说,最好的方法是获取您打算使用的任何内核系列中的最新内核副本。然后,您可以下载最新的补丁并将其应用于内核源代码,如下所述。我还强烈建议您研究您拥有的内核版本的发行说明,因为配置过程可能会发生变化。
要应用 Linux DECnet 补丁,请像往常一样将内核源代码解压缩到 /usr/src/linux 中。然后获取此内核版本的正确补丁,并在顶级 Linux 源代码目录 (/usr/src/,在本例中) 上面的目录中解压缩它。然后键入
$ patch -p0 < patch-file
patch-file 是您要应用的补丁的名称。接下来,您需要以通常的方式编译内核,务必对关于是否包含 DECnet 支持的问题回答 Y 或 N。根据您拥有的补丁版本,以下概述的某些选项可能可用。
DECnet 节点的两种主要类型是终端节点和路由器;后者又细分为 Level 1 和 Level 2 类型。在撰写本文时,仅终端节点支持可用。
当 DECnet 路由器支持可用时,您需要在编译时启用该选项。此外,在模块加载或启动时,您必须将其打开——编译了路由器支持的内核将能够充当终端节点和路由器。
DECnet 原始套接字 选项允许在比用户通常需要的更低的级别上读取和写入 DECnet 数据包。它对于调试和监视活动非常有用,并且可能是未来用户级路由守护程序所需要的。使用它而不是 PF_PACKET 套接字的主要优点是允许程序利用内核对无效 DECnet 数据包的过滤。
为了使用 DECnet 内核层,您还必须告诉内核要使用的 DECnet 地址。这是唯一一点指令取决于所使用的内核版本。对于 2.0.xx 版本内核,您需要 startnet 程序,该程序通常在系统的启动脚本中运行。对于 2.1.xx 版本内核及更高版本,DECnet 地址可以在命令行中使用 decnet=1,2 选项设置,也可以在加载模块时设置。(前一句中 1 和 2 之间是逗号,而不是点。)
熟悉 TCP/IP 的人会记得,ARP 协议用于允许机器发现连接到网络的其他节点的以太网地址。DECnet 中不存在此协议的等效协议;节点必须根据其 DECnet 节点地址设置其以太网地址。
为了计算出要使用的以太网地址,您需要取 DECnet 协议指定的四字节“hiord”前缀,并将其与两个字节 xx 和 yy 连接,这两个字节分别从您正在配置的节点的 DECnet 节点地址派生而来。
AA:00:04:00:
xx 和 yy 分别是 16 位 DECnet 地址的最低有效字节和最高有效字节。字节按这种方式排序是因为实现 DECnet 的原始系统具有小端 CPU。因此,地址为 1.1 的节点将具有 AA:00:04:00:01:04 的以太网地址,而地址为 1.2 的节点将具有 AA:00:04:00:02:04 的以太网地址。
在启动网卡之前,需要在您的网卡中设置此地址。在 Red Hat 系统上,这很容易。您只需添加以下行
MACADDR=AA:00:04:00:02:04
到文件 /etc/sysconfig/network-scripts/ifcfg-eth0 或任何与您希望使用的以太网卡对应的文件。如果您不是在 Red Hat 系统上,您可能必须查看启动脚本以找到相关接口的 ifconfig 命令,并在合适的位置添加 hw ether AA:00:04:00:02:04 选项。如果您使用的是 Slackware,则 /etc/rc.d/rc.inet1 是要修改的正确文件。
如果这看起来太复杂,可以使用名为 dn2ethaddr 的实用程序来打印出给定命令行上的 DECnet 地址的节点的以太网地址。它也可以在脚本中使用;手册页中给出了一个示例。
大多数用户将看到的 DECnet 层的前端是文件实用程序,这是一组使用内核套接字层来实现文件传输和其他有用应用程序的程序。Eduardo Serrat 的原始内核补丁附带了一些示例应用程序,这些应用程序已被 Patrick Caulfield 接管并在最近几个月进行了增强。
大多数为 DECnet 提供的应用程序都使用 DAP(数据访问协议),该协议执行与 TCP/IP 中的 FTP 协议类似的功能。DAP 是在 DECnet 之上实现的许多高级协议之一;cterm 是另一个,它以类似于 TCP/IP 上的 TELNET 的方式提供终端访问。
应用程序使用 OpenVMS 透明 DECnet 文件名格式来引用远程机器上的文件。OpenVMS 用户应该熟悉此语法,尽管 Linux 用户可能会觉得它有点奇怪。
nodename"
例如
tramp"patrick mypass"::[docs.html]art.html眼尖的人会注意到,在 bash shell 中键入此文件名会导致各种问题,因为 shell 对引号和方括号具有特殊含义。为了解决这个问题,我们必须将整个文件规范用单引号括起来
dncopy 'tramp"patrick mypass"::[docs.html]art.html'\ art.html此命令将文件从 OpenVMS 系统复制到我们的 Linux 机器。如果您担心密码在命令行上可见,请阅读关于 DECnet 代理的侧边栏。尽管您键入的每个 DECnet 文件名不一定都包含特殊的 shell 字符,但养成使用单引号的习惯是个好主意,这样当您需要它们时,如果您忘记它们,就不会产生意外的影响。
OpenVMS 机器上的文件名的语法也与 Linux 中的语法略有不同。目录包含在方括号中,并用点分隔。文件名在点的两侧最多可以有 39 个字符,并且两者都不区分大小写。OpenVMS 以大写形式显示它们,但也可以用小写形式引用它们。Linux 文件实用程序将始终为您将文件名转换为小写,因为这对于 Linux 用户来说更方便。
OpenVMS 将文件视为记录集合,而不是字节流。它想知道如何分隔记录,它们是固定长度还是可变长度,以及如何在屏幕上显示它们(回车控制)。
版本 0.10 中可用的文件实用程序是
dncopy:在 OpenVMS 和 Linux 系统之间复制文件。
dntype:在标准输出上显示 OpenVMS 文件的内容。
dndir:显示目录列表。
dndel:删除 OpenVMS 文件。
dntask:在 OpenVMS 系统上执行命令。
dncopy
dncopy 是所有实用程序中最复杂的:它使用了可能看起来令人眼花缭乱的选项列表。Linux 上的文件只是字节集合,而 OpenVMS 具有非常丰富的文件系统。文件可以具有不同的组织、记录格式和属性(请参阅“OpenVMS 文件类型和属性”)。
dncopy 必须应对在 Linux 的“文件就是文件”态度和更复杂的 OpenVMS 系统之间进行合理转换的任务。当从 OpenVMS 复制文件到 Linux 时,OpenVMS 会将有关文件的所有信息作为网络协议的一部分提供,因此此操作很少需要用户理解远程文件的性质。
当将文件复制到 OpenVMS 时,情况更为复杂。dncopy 必须告诉 OpenVMS 它要创建的文件类型、记录格式以及可能需要的任何其他可选属性。我们已尽力使默认设置尽可能有用,以便如果您将 Linux 文件复制到 OpenVMS,您将获得一个有用的文件。OpenVMS 具有与 Linux 文件类似的 SEQUENTIAL STREAMLF 文件类型。这是一个顺序文件,您可以使用换行符分隔的记录进行查找:当您使用 dncopy 将文件发送到 OpenVMS 时,这通常是您将获得的。实际上,dncopy 比这更进一步,并且在发送文件时实际上会在您的文件中查找记录,以使其对 OpenVMS 有意义。
STREAMLF 文件很好,但通常您希望发送块结构的数据文件或已备份或从 Internet 下载的 OpenVMS saveset,或者您可能希望您的文本文件采用更常见的 OpenVMS 文本文件格式。这就是 dncopy 中所有复杂选项旨在帮助您的原因。
一些示例可能有助于说明。正常的 OpenVMS 文本文件具有可变长度记录和隐含的回车控制。要像这样从 Linux 发送文件,我们将键入
dncopy -rvar -acr myfile.txt \ 'tramp"patrick mypassword"::'
选项 -rvar 指示 dncopy 告诉 OpenVMS 生成的文件将具有可变长度记录。-acr 表示记录具有隐含的(回车)回车控制。还要注意,生成的文件名已被省略。在本例中,dncopy 将使用源文件的基本名称 (myfile.txt)。
或者,如果您要发送一个文件以在 FORTRAN 程序中使用,OpenVMS 具有 FORTRAN 回车控制属性,其中每个记录的第一个字节说明是否开始新行、新页等。
dncopy -rvar -aftn fortfile.txt \ 'tramp"patrick mypassword"::'
如果您想发送 OpenVMS saveset(有点像 Linux tar 文件),您将发送具有固定长度记录的文件。dncopy 的正常模式是发送记录,因为记录是 OpenVMS 所期望的。二进制文件没有真正的记录结构,因此我们必须告诉 dncopy 发送字节块以及这些块的大小。saveset 文件的常用大小是 8192 块,因此我们可以使用以下命令将 saveset 文件从 Linux 发送到 OpenVMS
dncopy -mblock -b8192 saveset.bck\ 'tramp"patrick mypassword"::'dncopy 对 Linux 和 OpenVMS 文件名都采用通配符。(OpenVMS 通配符必须用于 OpenVMS 文件:% 代表单个字符,* 代表多个字符。)因此,您可以一次复制整个目录。它还可以通过使用标准输入和标准输出作为目标文件(以连字符作为文件名)来重定向。通过这种方式,您可以将 OpenVMS 文件嵌入到 Linux shell 脚本和管道中。
dncopy 的一个“特性”是您可能永远不需要的,但它从其面向对象的设计中发展而来,它还可以执行 Linux 到 Linux 和 OpenVMS 到 OpenVMS 的复制。请注意,如果您执行 OpenVMS 到 OpenVMS 的复制,所有数据都将通过您的 Linux 机器。
dntype
dntype 实际上只是 dncopy 的符号链接,它强制 dncopy 将文件发送到标准输出;它实际上只是为了提供一致性并节省打字时间。
dndir
dndir 是一个目录命令(非常像 Linux 中的 ls)。它以类似于 ls 命令的格式显示 OpenVMS 目录。它采用一些开关来自定义格式,尽管 -l 可能是最常用的,因为它显示了大部分有用的信息。
与 ls 不同的两个字段是文件大小和保护信息。文件大小以 512 字节块显示,文件保护信息以 OpenVMS 格式而不是 Linux 格式显示。我选择以这种方式保留保护显示,因为 OpenVMS 比 Linux 具有更多的文件保护位,并且能够看到所有信息通常很有帮助。
dndel
dndel 删除 OpenVMS 文件。与 dncopy 和 dndir 一样,它可以采用 OpenVMS 通配符文件名来删除多个文件。使用 -i 选项,系统将提示您是否真的要删除文件。
dntask
dntask 是这些程序中唯一一个不使用 DAP 协议的程序;相反,它与任意 DECnet 对象通信。OpenVMS 上 DECnet 的一个很少使用的功能是,通过使用语法 TASK=filename,命令 filename.COM 将作为命令过程(OpenVMS 中相当于 shell 脚本)运行,并且输出可以重定向回调用任务。发行版中提供了三个示例任务。一个任务只是发出 SHOW SYSTEM 命令,该命令将其输出发送到 Linux 机器(使用命令 dntask tramp::show_system)。此输出类似于 Linux ps 命令。另一个任务向 dntask 发送 -i(交互式)标志,以允许用户与 OpenVMS 机器上的 shell 交互。但是,以下示例是 dntask 存在的主要原因。
编写 DECnet 内核层的 Eduardo Serrat 确保它与 X11R6 兼容。这意味着,如果您的 X 服务器中编译了 DECnet 支持(有关具有 DECnet 支持的预构建 X 服务器,请参阅 http://linux.dreamtime.org/decnet/Xservers.html),您可以在 OpenVMS 机器上启动 X Window 系统应用程序,并在 Linux 机器上显示它们。这是一种为 OpenVMS 系统提供 X 终端支持的廉价有效方法。dntask 程序可以发出命令来启动任何 X 程序以在 Linux 机器上显示,前提是您编写了合适的远程命令过程。下面的示例显示了一个 DECterm 的启动(我个人经常使用),但它也可以用于更复杂的事情,例如当用户登录到 Linux 并启动 X 时启动完整的 CDE 会话。
dntask 'tramp::decterm'
Eduardo 还提供了以下有用的 DECnet 实用程序
sethost 提供对 OpenVMS 机器的终端访问,类似于 TELNET。
ctermd 是一个守护程序,提供相反的服务,允许 OpenVMS 用户 SET HOST(或如果您愿意,可以使用 TELNET)连接到 Linux 机器。
dnmirror 和 dnping 是测试实用程序,用于检查软件是否正确安装并验证与特定 OpenVMS 节点的连接。
以上所有实用程序和 X 服务器都依赖于 libdnet,libdnet 可从我们的网站获得(请参阅“资源”)。
对于内核黑客来说,这是对相关源文件的快速浏览。本节仅适用于较新的内核补丁(即,对于 2.1.xx 系列及更高版本),因为我们希望您会发现这些补丁最有趣和有用。

图 1. DECnet 软件层图
图 1 显示了 Linux DECnet 层的总体布局图。在我们描述 DECnet 协议的地方,我们只深入到足以了解内核代码每个部分的主要功能的程度。如果您想了解更多关于内核代码工作方式的信息,您需要阅读 DECnet 规范的副本(请参阅“资源”),然后直接查看源代码。
主源文件是 af_decnet.c。此文件包含套接字层接口以及 DECnet NSP 和会话控制层代码的一部分。由于 DECnet 分层模型与套接字代码的映射不完全一致,因此会话控制部分由内核提供,部分由名为 libdnet.so 的用户空间库提供。
dn_raw.c 包含实现原始套接字层的代码。它是最早编写的内容之一,因为它对于调试以查看“幕后”发生的事情非常有用。它也是如何编写最简单的套接字层接口的一个很好的例子。该文件仅在配置原始套接字选项时编译。
NSP 层的其余部分分为两个部分:一部分用于处理出站数据包,dn_nsp_out.c,另一部分用于处理入站数据包,dn_nsp_in.c。图 2 显示了 NSP 层的状态表。我们在此处不会过多地讨论该图,但将其与内核代码结合使用时,它应该是一个有用的辅助工具。
路由层相当成问题。它已被划分为多个文件,因为路由层实际上不仅仅执行路由。dn_route.c 是处理传入和传出数据包的主文件,dn_dev.c 提供对设备特定功能的支持。
dn_neigh.c 具有双重个性。当节点作为终端节点运行时,它提供 DECnet 规范中描述的以太网上缓存;对于路由器,它提供邻接数据库。由于它们非常相似,因此决定合并这两个功能以保持代码小巧。
实际的路由功能(仅在节点配置为路由器时编译)位于 dn_fib.c 中。此时,此文件中的代码非常实验性,因为关于应该在用户空间中完成多少路由以及在内核空间中完成多少路由的决策仍在制定中。
代码中更晦涩和重要的部分之一是传出数据包的主要路径。DECnet 层使用 Alexey Kuznetsov 编写的协议无关目标缓存,以及 Alexey Kuznetsov 和 Pedro Roque 编写的邻居表代码。这些最初旨在执行 IPv4 和 IPv6 网络协议所需的一些通用处理,目的是让其他协议在稍后开始使用它们。
这两段代码究竟做了什么?我们将首先描述邻居表。这背后的想法很简单,就是保留直接连接的已知节点列表,以及协议用于与它们通信的某些信息。在 TCP/IP 的情况下,这意味着 ARP 子系统;对于 DECnet,它用于保存两件事之一。对于终端节点,它保存直接连接网络上的已知节点列表,这些节点可以建立通信,称为以太网上缓存。对于路由节点,它保存规范描述的邻接数据库。在这两种情况下,功能相同,但实际操作方法略有不同。
在终端节点情况下,路由器每十秒向所有直接连接的终端节点发送 hello 消息。终端节点使用这些消息在以太网上缓存中创建条目。如果在一定时间内(通常为一分钟)未收到 hello 消息,则该条目将从列表中删除。默认路由器是直接连接的节点,如果终端节点未直接连接到目标,则应将数据包发送到该节点。默认路由器由接收到的 hello 消息中的信息确定。
对于路由节点,从终端节点和其他路由器接收 hello 消息,并用于更新邻接表。在这种情况下,如果在一段时间内未收到 hello 消息(指定为 hello 消息中记录的时间长度的倍数),则会删除条目。目前,邻居表不支持每个不同邻居的不同超时时间。这个问题正在解决中,并且在您阅读本文时可能会得到解决。
邻居表中保存的另一条信息是路由层在传输 NSP 数据时要使用的标头格式。有两种格式,一种用于广播链路(长格式),包括以太网,另一种用于点对点链路(短格式)。这是通过在创建表条目时设置指向正确例程 dn_long_outout 或 dn_short_output 的函数指针来完成的。
目标缓存基于与邻居表类似的原则。但是,目标是保存每个目标所需的信息。当要将数据包发送到特定目标时,会在目标缓存中查找它是否存在。如果存在,则使用该条目;如果不存在,则必须调用路由算法以发现正确的目的地。
路由算法还取决于节点的路由或非路由类型。路由器的算法尚未正确实现,但到时将驻留在 dn_fib.c 文件中。对于终端节点,该算法只是直接发送到以太网上缓存中的任何节点,如果不在缓存中,则发送到默认路由器,如果不存在默认路由器,则直接发送。
同样,每个目标条目中都有一个函数指针,指向一个例程,该例程会将特定于目标的信息添加到传出数据包,然后调用邻居的输出例程。
关于内核代码的主要功能就介绍到这里。当然,还有很多内容没有在这里提及,但我们希望我们的概述对您有所帮助,如果您计划添加功能或帮助进行调试。如果您有具体问题,我们很乐意尝试回答;但是,请先阅读文档,并记住我们可能并不总能立即发送答复。
希望我们为您提供了截至撰写本文时可用的 Linux/DECnet 连接性的良好概述。但是,我们仍在努力开发新功能和程序(见下文),其中一些功能和程序可能在本文印刷时已准备就绪。
dapfs 是 Linux 的一个文件系统层,它将允许您将 OpenVMS 文件系统挂载到您的 Linux 机器上。
fal 是 Linux 的一个文件监听器,它将允许 OpenVMS 机器上的用户访问网络 Linux 机器上的文件,而无需登录。
Router 路由器支持也在开发中。预计这将采取少量内核代码和用户级守护程序的形式。它将允许您将多个 DECnet 网络连接到您的 Linux 机器。

