首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

内核中的 telnet 服务(1)

内核中的 telnet 服务(1)

内核中的 telnet 服务开发人员依赖串口对嵌入式 Linux 设备进行调试和开发。串口是开发人员与设备之间的操作界面。依靠它,开发人员获得与操作系统交互的能力。对于拥有网络界面的硬件设备来说, telnet 是另一个选择。通过 telnet 接入到设备将使开发人员获得与串口界面相同能力。
但是,当内核中的代码(如驱动)产生无限循环或其它耗时操作时,操作系统将无法调度和处理串口界面上的输入。串口界面将停止响应开发人员的操作。一些其它的异常也会造成串口无法工作。同时,出现这些问题时刻, telnet 服务也往往停止了工作。开发人员无法通过 telnet 界面来接入设备,也就无力进行任何调试操作了。
在实际的开发环境中,出现上述类似问题时,运行在设备上的 Linux 操作系统仍然可以响应外部的 PING 请求。这意味着操作系统的 ICMP 协议栈仍然可以正常工作。那么,如果可以将 telnet 服务植入 Linux 操作系统的 ICMP 协议栈中,我们就可以得到一个可靠性更高的接入界面。另一方面,由于 ICMP 协议栈是运行在 Linux 内核中,那么在协议栈中的 telnet 服务将不会受到任何限制,而获得访问内核的能力。通过这个接入界面,开发人员可以自由的访问内核中的数据,甚至调整内核中的某些参数和运行状态。这个方式要比 proc 界面方便了许多。
为了植入 telnet 服务,我们首先需要了解 Linux 的 ICMP 协议栈的工作原理。在这之前,简单的介绍 ICMP 协议是有必要的。
ICMP 是 IP 协议的一个组成部分。 ICMP 报文使用 IP 数据报 ( UDP ) 的方式传输。 ICMP 报文中所含的 IP 头部中需要注明协议类型。图 1 是 IP 头部的结构图。
图 1. IP 头部如图 1 所示,对于 ICMP 报文,位于  IP 头部第 10 个字节的“协议”字段必须为 1 。这指明了协议类型为 ICMP 。在内核代码中,这个类型被定义为 IPPROTO_ICMP 。 IP 头部后面的载荷为 ICMP 报文。 ICMP 报文的格式见图 2 。
图 2. ICMP 头部所有 ICMP 报文的前 4 个字节即为 ICMP 头部。它们总是相同的,即类型、代码和检验和。 ICMP 报文有多种类型。 PING 请求属于“请求回显”类型。这个类型的值被定义为 8 。在内核代码中则定义为 ICMP_ECHO 。在 PING 请求中,ICMP 头部之后就是净载荷。收到 PING 请求的主机通过将净载荷原封不动的回应回去来声明通信链路处于正常工作状态。回应的 ICMP 报文的类型为 ICMP_ECHOREPLY ,其值为 0 。
在 Linux 操作系统中,ICMP 协议栈的代码位于目录 /linux-2.6.x/net/ipv4 中的 icmp.c 文件。这部分代码属于 Linux 操作系统的网络部分。
Linux 对于 PING 请求的简明处理流程是这样的:
函数 icmp_rcv 在收到被封装到 SKB 结构中的报文后,获取到 ICMP 头部。根据头部中携带的类型,调用相对应的处理函数。代码如下:
1
2
3
4
5
6
int icmp_rcv(struct sk_buff *skb)
        {
               ……
               icmp_pointers[icmph->type].handler(skb);
               ……
        }




对于 PING 请求来说, icmph->type 的值为 ICMP_ECHO ,也就是 8 。对应的处理函数即是 icmp_echo 。 在 icmp_echo 函数中,修改收到的 SKB 结构中的 ICMP 头部,使其类型为 ICMP_ECHOREPLY 。然后,调用 icmp_reply 函数,回应收到的 PING 请求。
从整个流程来看, Linux 对于 PING 请求报文的处理是较为简单的。这就为我们植入 telnet 服务带来了便利。
RFC854 定义了 telnet 协议的规范。 telnet 协议基于 TCP 传输协议。因此, telnet 协议无法平滑的移植到 ICMP 协议栈中。定义一个私有的协议是唯一的选择。为了可以在 ICMP 协议栈中实现 telnet 服务,这个协议必须基于数据报( UDP ),而且,这个协议必须基于 ICMP 协议,并且使用 ICMP_ECHO 类型。在这个限制条件下,我们可以利用这个类型的 ICMP 报文中的净载荷来承载私有协议。
我们定义的私有协议的结构是:
图 3. 私有协议结构 - 请求请求报文中,除去 4 个字节的 ICMP 报文头部,前两个字节是 magic 数,用来标明报文是我们的私有协议。这个 maigc 可任意定义,比如是两个 ASCII 码字符“ LX ”。后两个字节指示命令的长度。在长度字段之后,是一个字符串。它是客户端请求执行的命令。以字符串的形式存储在长度字段之后,并以 0 为结束符。
与之对应的是,回应报文中携带的是字符串形式的命令执行结果。如下图:
图 4. 私有协议结构 - 回应由于利用这个私有协议实现的类 telnet 服务是在内核中实现的。因此,这个私有协议被命名为 ktelnet 协议。
完整的系统由三个部分组成: ktelnet 、 ktelnetd 和 kshell 。
ktelnet 运行在客户端,开发人员使用它接入 ktelnetd 。通常情况下,ktelnet 是一个运行在 windows 的可执行程序。
ktelnetd 运行在嵌入式设备上,接收来自 ktelnet 的报文,并从报文解析出命令。
kshell 是一个逻辑模块。它与 ktelnetd 一起运行在内核中,负责执行 ktelnetd 提交的命令。执行结果将反馈给 ktelnetd 。 ktelnetd 将执行结果返回给 ktelnet 。
ktelnetd 是 ktelnet 的服务端。 Kshell 则是 ktelnetd 的后台支持模块。 ktelnetd 和 kshell 与 ICMP 协议栈一起运行在 Linux 内核中。
返回列表