网络工具
SylixOS 支持众多网络工具,本节介绍各网络工具的使用方法。
网络包过滤器(npf)
npf 简介
为了对内部网络提供保护,有必要对通过防火墙的数据包进行检查,例如检查其源地址和目的地址、端口地址、数据包的类型等,根据这些数据来判断这个数据包是否为合法数据包,如果不符合预定义的规则,就不将这个数据包发送到目的计算机中去。由于包过滤技术要求内外通信的数据包必须通过使用这个技术的计算机,才能进行过滤,因而包过滤技术通常用在路由器上。
SylixOS 提供的 npf(net packet filter)工具是一个网络过滤器,该工具提供了以下过滤规则:
- 过滤链路层帧:指定为 MAC 规则,此规则需要提供目的计算机的硬件地址。
- 过滤 IP 数据报:指定为 IP 规则,此规则需要指定一个想要过滤的 IP 地址范围。
- 过滤传输层数据包:指定为 UDP 或 TCP 规则,此规则将过滤某个 IP 地址范围和某个端口号范围的数据包。
下表是 SylixOS 目前支持的网络包过滤规则。
规则 | 说明 | 值 |
---|---|---|
LWIP_NPF_RULE_MAC | 过滤以太网帧 | 0 |
LWIP_NPF_RULE_IP | 过滤 IP 数据报 | 1 |
LWIP_NPF_RULE_UDP | 过滤 UDP 数据包 | 2 |
LWIP_NPF_RULE_TCP | 过滤 TCP 数据包 | 3 |
npf 函数
下面函数初始化 SylixOS 网络包过滤器。
#include <SylixOS.h>
INT Lw_Inet_NpfInit(VOID);
函数 Lw_Inet_NpfInit 原型分析:
- 此函数成功返回返回 0,失败返回-1 并设置错误号。
下面函数向网络包过滤器增加一个新规则。
#include <SylixOS.h>
PVOID Lw_Inet_NpfRuleAdd(CPCHAR pcNetifName,
INT iRule,
UINT8 pucMac[],
CPCHAR pcAddrStart,
CPCHAR pcAddrEnd,
UINT16 usPortStart,
UINT16 usPortEnd);
函数 Lw_Inet_NpfRuleAdd 原型分析:
- 此函数成功返回新规则,失败返回 LW_NULL 并设置错误号。
- 参数 pcNetifName 是网络接口名。
- 参数 iRule 是对应的规则,如下表所示。
- 参数 pucMac 是禁止通行的 MAC 地址组。
- 参数 pcAddrStart 是禁止通行的起始 IP 地址。
- 参数 pcAddrEnd 是禁止通行的终止 IP 地址。
- 参数 usPortStart 是禁止通行的起始端口号。
- 参数 usPortEnd 是禁止通行的终止端口号。
参数 pcAddrStart 和 pcAddrEndus 用于规则“UDP”、“TCP”和“IP”,参数 usPortStart 和 usPortEnd 用于规则“UDP”和“TCP”,参数 pucMac 用于规则“MAC”。
规则 | 说明 | 值 |
---|---|---|
LWIP_NPF_RULE_MAC | 过滤以太网帧 | 0 |
LWIP_NPF_RULE_IP | 过滤 IP 数据报 | 1 |
LWIP_NPF_RULE_UDP | 过滤 UDP 数据包 | 2 |
LWIP_NPF_RULE_TCP | 过滤 TCP 数据包 | 3 |
下面函数从网络包过滤器删除一个规则。
INT Lw_Inet_NpfRuleDel(CPCHAR pcNetifName,
PVOID pvRule,
INT iSeqNum);
函数 Lw_Inet_NpfRuleDel 原型分析:
- 此函数成功返回 0,失败返回-1 并设置错误。
- 参数 pcNetifName 是网络接口名。
- 参数 pvRule 是规则句柄(为 NULL 时表示使用规则序列号)。
- 参数 iSeqNum 是规则序列号。
下面函数可以使网络过滤器绑定到指定的网卡和从指定的网卡解除绑定。
#include <SylixOS.h>
INT Lw_Inet_NpfAttach(CPCHAR pcNetifName);
INT Lw_Inet_NpfDetach(CPCHAR pcNetifName);
以上函数原型分析:
- 函数成功返回 0,失败返回-1 并设置错误。
- 参数 pcNetifName 是网络接口名。
下面函数可以获得网络过滤器信息。
ULONG Lw_Inet_NpfDropGet(VOID);
ULONG Lw_Inet_NpfAllowGet(VOID);
INT Lw_Inet_NpfShow(INT iFd);
函数 Lw_Inet_NpfShow 原型分析:
- 此函数成功返回 0,失败返回-1 并设置错误号。
- 参数 iFd 是目标文件描述符。
调用 Lw_Inet_NpfDropGet 函数可以获得丢弃的数据包个数(这些包包括规则性过滤的和缓存不足造成的丢弃),调用 Lw_Inet_NpfAllowGet 函数可以获得运行通行的数据包个数,调用 Lw_Inet_NpfShow 函数可以将网络过滤器的详细信息打印到指定的文件中(此文件已通过 open 函数打开)。
网络信息查看(netstat)
在 SylixOS 中, netstat 命令用于显示网络相关信息,如网络连接、路由表、接口状态、多播成员等。命令格式如下:
netstat {[-wtux --A] -i | [hrigs]}
-h :显示帮助信息
-r :显示路由表信息
-i :显示接口信息
-g :显示多播组成员信息
-s :显示网络状态信息
-w :显示原始套接字信息
-t :显示TCP信息
-u :显示UDP信息
-p :显示PACKET套接字信息
-x :显示UNIX域套接字信息
-l :显示所有LISTEN状态的信息
-a :显示所有套接字信息
输出信息含义
在 SylixOS 中,执行 netstat 命令后输出如下:
# netstat -a
--UNIX--:
TYPE FLAG STATUS LCONN SHUTD NREAD MAX_BUFFER PATH
--PACKET--:
TYPE FLAG PROTOCOL INDEX MMAP MMAP_SIZE TOTAL DROP
--TCP LISTEN--:
LOCAL REMOTE STATUS RETRANS RCV_WND SND_WND
*:23 *:* listen 0 0 0
*:21 *:* listen 0 0 0
--TCP--:
LOCAL REMOTE STATUS RETRANS RCV_WND SND_WND
10.4.120.10:23 10.4.0.30:64525 estab 0 65535 65672
--UDP--:
LOCAL6 REMOTE6 UDPLITE
*:137 *:0 no
*:161 *:0 no
- 从输出结果可以看出,分为五部分:UNIX、PACKET、TCP LISTEN、TCP、UDP。
- UNIX 部分。
- TYPE:UNIX 域套接字类型:stream、seqpacket、dgram。
- FLAG:I/O 标志,如 NONBLOCK。
- STATUS:当前状态(仅对于类型为 stream):none、listen、connect、estab。
- LCONN :本地 socket 连接数量。
- SHUTD:当前关闭状态:rw、r、w、no。
- NREAD:有效数据字节数(单位字节)。
- MAX_BUFFER:最大接收缓冲区大小。
- PATH:UNIX 域文件路径名。
- PACKET 部分。
- TYPE:PACKET 套接字类型:raw、dgram。
- FLAG:I/O 标志,如 NONBLOCK。
- PROTOCOL:协议类型,如下表所示。
- INDEX:网络接口索引。
- MMAP:是否进行了 mmap。
- MMAP_SIZE:映射内存的大小。
- TOTAL:总网络包数量。
- DROP:丢弃的网络包数量。
协议类型 | 说明 |
---|---|
IPPROTO_IP | IPv4 网络协议 |
IPPROTO_ICMP | 因特网控制报文协议 |
IPPROTO_TCP | 传输控制协议 |
IPPROTO_UDP | 用户数据报协议 |
IPPROTO_IPV6 | IPv6 网络协议 |
IPPROTO_RAW | 原始 IP 数据包协议 |
- TCP LISTEN 部分。
- LOCAL:本地 IP 地址及端口号。
- REMOTE:远端 IP 地址及端口号。
- STATUS:TCP 状态如下表所示。
- RETRANS:重传计数。
- RCV_WND:接收窗口大小。
- SND_WND:发送窗口大小。
- TCP 部分。
- LOCAL:本地 IP 地址及端口号。
- REMOTE:远端 IP 地址及端口号。
- STATUS:TCP 状态如下表所示。
- RETRANS:重传计数。
- RCV_WND:接收窗口大小。
- SND_WND:发送窗口大小。
- UDP 部分。
- LOCAL6:本地 IP 地址及端口号。
- REMOTE6:远端 IP 地址及端口号。
- UDPLITE:是否是 UDPLITE。
TCP 状态 | 说明 |
---|---|
CLOSED | 初始关闭状态 |
LISTEN | 监听状态,服务器端可以接收连接 |
SYN_SENT | 客户端发送 SYN 报文后进入此状态 |
SYN_RCVD | 服务器端接收到 SYN 报文后进入此状态 |
ESTABLISHED | 连接已经建立 |
FIN_WAIT_1 | 主动关闭连接一方发送 FIN 报文进入此状态(一般很难看到) |
FIN_WAIT_2 | 进入 FIN_WAIT_1 状态的一端,当收到另一端的 ACK 后进入此状态 |
CLOSE_WAIT | 另一方请求关闭连接,回应完 ACK 后进入此状态,然后就可以做关闭本地等操作 |
CLOSING | 发起关闭方发送 FIN 报文,在没有收到 ACK 报文前收到了另一方的 FIN 报文进入此状态(这种情况可能会发送在双方同时请求关闭) |
LAST_ACK | 被动关闭一方发送完 FIN 报文后,最后等待对方的 ACK 报文进入此状态 |
TIME_WAIT | FIN_WAIT_1 状态下,收到了对方同时带 FIN 标志和 ACK 标志的报文时进入此状态而无需进入 FIN_WAIT_2 状态 |
显示网络接口信息
执行下面命令显示网络接口信息。
# netstat –i
|RECEIVE |TRANSMIT
FACE MTU RX-BYTES RX-OK RX-ERR RX-DRP RX-OVR TX-BYTES TX-OK TX-ERR TX-DRP TX-OVR FLAG
en1: 1500 2031404 25593 0 0 0 1790 27 0 0 0 UBLEth
lo0: 0 648 11 0 0 0 648 11 0 0 0 UL
输出信息中显示了系统中存在的所有网络接口及其信息,这些信息包括:网络 MTU、接收数据的字节数、成功接收的数据包数、接收的错误数据包数、发送的数据包数、网络接口标志等。SylixOS 中支持的网络接口标志如下表所示。
标志 | 说明 |
---|---|
U | 网络接口 Up |
B | 支持广播(Broadcast) |
P | 网络接口是点对点连接(Point-to-point) |
D | 网络接口 Dhcp 开启 |
L | 网络接口已经连接(Linkup) |
Eth | 网络接口是以太网设备支持 ARP(Etharp) |
G | 网络接口支持 IGMP |
网络接口配置(ifconfig)
ifconfig 是 SylixOS 中用于显示和配置网络设备的命令,命令格式如下:
ifconfig [netifname] [{inet | netmask | gateway}] [address]
netifname :指定的网络接口名(如en1)
address :指定的地址(如192.168.1.33)
inet :指定设置IPv4地址
netmask :指定设置子网掩码
gateway :指定设置网关
address :指定的地址(如192.168.1.33)
设置 IP 地址
# ifconfig en1 inet 192.168.1.33
此命令设置网络接口 en1 的 IPv4 地址为 192.168.1.33。
设置网关
# ifconfig en1 gateway 192.168.1.1
此命令设置网络接口 en1 的网关为 192.168.1.1。
设置 DNS
# ifconfig dns 0 192.168.1.254
此命令设置 DNS 0 的地址为 192.168.1.254。
如果 ifconfig 参数为网络接口将打印该接口的网络信息,特殊地, ifconfig 没有指定任何参数将打印系统中所有网络接口的信息。
简单文件传输(TFTP)
TFTP 简介
TFTP(Trivial File Transfer Protocol)即简单文件传送协议,在开始工作时,TFTP 的客户与服务器交换信息,客户发送一个读请求或写请求给服务器。
TFTP 报文的头两个字节表示操作码。对于读请求和写请求,文件名字段说明客户要读或写的位于服务器上的文件。这个文件字段以 0 字节作为结束。模式字段是一个 ASCII 码字符串“netascii”或“octet”(可大小写任意组合),同样以 0 字节结束。“netascii”表示数据是以成行的 ASCII 码字符组成,以回车字符后跟换行字符(称为 CR / LF)作为行结束符。这两个行结束字符在这种格式和本地主机使用的行定界符之间进行转化。“octet”则将数据看作 8 bit 一组的字节流。
每个数据分组包含一个块编号字段,它以后要在确认分组中使用。以读一个文件作为例子,TFTP 客户需要发送一个读请求说明要读的文件名和文件模式。如果这个文件能被这个客户读取,TFTP 服务器就返回一个块编号为 1 的数据分组。TFTP 客户又发送一个块编号为 1 的 ACK。TFTP 服务器随后发送块编号为 2 的数据。TFTP 客户发回块编号为 2 的 ACK。重复这个过程直到这个文件传送完。除了最后一个数据分组可含有不足 512 字节的数据,其他每个数据分组均含有 512 字节的数据。当 TFTP 客户收到一个不足 512 字节的数据分组,就知道它收到了最后一个数据分组。
在写请求的情况下,TFTP 客户发送写请求指明文件名和模式。如果该文件能被该客户写,TFTP 服务器就返回块编号为 0 的 ACK 包。该客户就将文件的头 512 字节以块编号为 1 发出。服务器则返回块编号为 1 的 ACK。这种类型的数据传输称为停止等待协议(它只用在一些简单的协议如 TFTP 中)。
最后一种 TFTP 报文类型是差错报文,它的操作码为 5。它用于服务器不能处理读请求或写请求的情况。在文件传输过程中的读和写差错也会导致传送这种报文,接着停止传输。差错编号字段给出一个数字的差错码,跟着是一个 ASCII 表示的差错报文字段,可能包含额外的操作系统说明的信息。既然 TFTP 使用不可靠的 UDP,TFTP 就必须处理分组丢失和分组重复。分组丢失可通过发送方的超时与重传机制解决,和许多 UDP 应用程序一样,TFTP 报文中没有检验和,它假定任何数据差错都将被 UDP 的检验和检测到。
整体上来说,TFTP 是一个简单易于实现的协议:每一个数据包大小固定,且每个数据包都有确认机制,可以实现一定程度的可靠性。当然 TFTP 的缺点也很明显:传输效率不高,滑动窗口机制太简单,并且该窗口仅有一个包的大小而且超时机制并不完善。
下图显示了 5 种 TFTP 报文格式(操作码为 1 和 2 的报文使用相同的格式)。
TFTP 命令
SylixOS 默认为关闭状态,使用时需要使能 tftp 服务,即将宏 LW_CFG_NET_TFTP_EN 置为 1,在 libsylixos/SylixOS/config/net/net_tools_cfg.h 可以发现。
SylixOS 中使用 tftp 命令可实现通过 TFTP 协议发送和接收文件, tftpdpath 命令可修改 TFTP 服务器的默认路径。命令格式如下:
tftpdpath [new path]
new path :新的路径名。
下面命令设置 tftp 服务器的路径名为/tmp/sylixos。
# tftpdpath /tmp/sylixos
tftp 命令格式如下:
tftp [-i] [Host] [{get | put}] [Source] [Destination]
Host :服务器地址
-i :指定TFTP模式为“octet”
get :从TFTP服务器获得一个文件
put :向TFTP服务器发送一个文件
Source :源文件名
Destination :目的文件名(get一个文件时此参数可为空)
下面命令从 IP 为 192.168.1.30 的 TFTP 服务器获得文件 sylixos.log。
# tftp –i 192.168.1.30 get sylixos.log
文件传输(FTP)
FTP 简介
FTP(File Transfer Protocol)是 TCP/IP 协议族中的协议之一,其目的是提供文件的共享性,也就是说 FTP 完成的是两台计算机之间的拷贝。FTP 采用两个 TCP 连接来传输一个文件。
- 控制连接以通常的客户服务器方式建立。服务器以被动方式打开众所周知的用于 FTP 的端口 21,然后等待客户的连接。客户则以主动方式打开 TCP 端口 21,来建立连接。控制连接始终等待客户与服务器之间的通信。该连接将命令从客户传给服务器,并传回服务器的应答。
- 每当一个文件在客户与服务器之间传输时,就创建一个数据连接。
FTP 数据表示
FTP 协议规范提供了控制文件传送与存储的多种选择。在以下四个方面中每一个方面都必须作出一个选择。
- 文件类型包括 ASCII 码文件类型、EBCDIC 文件类型、二进制文件类型、本地文件类型(目前 SylixOS 仅支持 ASCII 码文件类型和二进制文件类型)。
- ASCII 码文件类型,文本文件以 ASCII 码形式在数据连接中传输,这要求发方将本地文本文件转换成 ASCII 码形式,而收方则将 ASCII 码再还原成本地文本文件,其中,用 ASCII 码传输的每行都带有一个回车,而后是一个换行。这意味着收方必须扫描每个字节,查找 CR、LF 对。
- EBCDIC 文件类型,文本文件传输方式要求两端都是 EBCDIC。
- 二进制文件类型,数据发送呈现为一个连续的比特流,通常用于传输二进制文件。
- 本地文件类型,该方式在具有不同字节大小的主机间传输二进制文件。每一字节的比特数由发方规定。对使用 8bit 字节的系统来说,本地文件以 8bit 字节传输就等同于二进制文件传输。
- 格式控制,该选项只对 ASCII 码文件类型和 EBCDIC 文件类型有效。
- 非打印,文件中不能含有垂直格式信息。
- 远程登录格式控制,文件含有向打印机解释的远程登录垂直格式控制。
- Fortran 回车控制,每行首字符是 Fortran 格式控制符。
- 结构;
- 文件结构(默认选择)文件被认为是一个连续的字节流。不存在内部的文件结构。
- 记录结构,该结构只用于文本文件(ASCII 或 EBCDIC)。
- 页结构,每页都带有页号发送,以便收方能随机地存储各页。
- 传输方式,它规定文件在数据连接中如何传输。
- 流方式,文件以字节流的形式传输。对于文件结构,发方在文件尾提示关闭数据连接。对于记录结构,有专用的两字节序列码标志记录结束和文件结束。
- 块方式,文件以一系列块来传输,每块前面都带有一个或多个首部字节。
- 压缩方式,一个简单的全长编码压缩方法,压缩连续出现的相同字节。
SylixOS 中选择如下:
- 类型,ASCII 或二进制。
- 格式,非打印。
- 结构,文件结构。
- 传输方式,流方式。
FTP 协议命令
命令和应答在客户和服务器的控制连接上以 ASCII 码形式传送。这就要求在每行结尾都要返回 CR、LF 对(也就是每个命令或每个应答)。
下表所示是 SylixOS 支持的 FTP 命令。
FTP命令 | 说明 | FTP命令 | 说明 |
---|---|---|---|
USER | 指定远程系统上的用户名 | RNFR | 文件重命名进程的前一半。需要重命名的文件的旧路径和文件名 |
PASS | 向远程用户发送密码(USER 命令之后使用) | REST | 标识出文件内的数据点,将从这个点开始继续传送文件 |
CWD | 把当前目录改为远程文件系统的指定目录 | RETR | 这条命令让服务器给客户传送一份在路径名中指定的文件的副本 |
CDUP | 把当前目录改为远程文件系统的根目录 | STOR | 让服务器接收一个来自数据连接的文件 |
PWD | 在应答中返回当前工作目录的名称 | APPE | 让服务器准备接收一个文件并指示它把这些数据附加到指定的文件名,如果指定的文件尚未存在,就创建它 |
ALLO | 发送文件前在服务器上分配 x 个字节 | SYST | 用于查明服务器上操作系统的类型 |
PORT | 为数据连接指定一个 IP 地址和本地端口 | MKD | 创建一个在路径名中指定的目录 |
PASV | 告诉服务器在一个非标准端口上收听数据连接 | RMD | 删除一个在路径名中指定的目录 |
TYPE | 确定数据的传输方式 | DELE | 删除服务器站点上在路径名中指定的文件 |
LIST | 让服务器给客户发送一份列表 | MDTM | 更新时间信息 |
NLST | 让服务器给客户发送一份目录列表 | SIZE | 发送文件大小 |
NOOP | 什么都不做 | SITE | 提供了某些服务特性 |
FTP 应答
应答都是 ASCII 码形式的 3 位数字,并跟有报文选项。其原因是软件系统需要根据数字代码来决定如何应答,而选项串是面向人工处理的。由于客户通常都要输出数字应答和报文串,一个可交互的用户可以通过阅读报文串(而不必记忆所有数字回答代码的含义)来确定应答的含义。
应答 3 位码中每一位数字都有不同的含义,如下表所示给出了应答代码第一位和第二位的含义。
应答 | 说明 |
---|---|
1yz 2yz 3yz 4yz 5yz | 肯定预备应答。它仅仅是在发送另一个命令前期待另一个应答时启动 肯定完成应答。一个新命令可以发送 肯定中介应答。该命令已被接受,但另一个命令必须被发送 暂态否定完成应答。请求的动作没有发生,但差错状态是暂时的,所以命令可以过后再发 永久性否定完成应答。命令不被接受,并且不再重试 |
x0z x1z x2z x3z x4z x5z | 语法错误 信息 连接。应答指控制或数据连接 鉴别和记帐。应答用于注册或记帐命令 未指明 文件系统状态 |
第三位数字给出了差错报文的附加含义。例如,这里是一些典型的应答,都带有一个可能的报文串。
- 125:数据连接已经打开;传输开始。
- 200:就绪命令。
- 214:帮助报文(面向用户)。
- 331:用户名就绪,要求输入口令。
- 425:不能打开数据连接。
- 452:错写文件。
- 500:语法错误(未认可的命令)。
- 501:语法错误(无效参数)。
- 502:未实现的 MODE (方式命令)类型。
FTP 连接管理
数据连接有以下三大用途:
- 从客户向服务器发送一个文件。
- 从服务器向客户发送一个文件。
- 从服务器向客户发送文件或目录列表。
FTP 服务器把文件列表从数据连接上发回,而不是控制连接上的多行应答。这就避免了行的有限性对目录大小的限制,而且更易于客户将目录列表以文件形式保存,而不是把列表显示在终端上。
前面我们说过 SylixOS 传输方式是流方式,并且文件结尾是关闭数据连接的标志。这意味着对每一个文件传输或目录列表来说都要建立一个全新的数据连接,过程如下:
- 正由于是客户发出命令要求建立数据连接,所以数据连接是在客户的控制下建立的。
- 客户通常在客户端主机上为所在数据连接端选择一个临时端口号。客户从该端口发布一个被动的打开。
- 客户使用 PORT 命令从控制连接上把端口号发向服务器。
- 服务器在控制连接上接收端口号,并向客户端主机上的端口发布一个主动的打开,服务器的数据连接端使用端口 20。
FTP 命令
SylixOS 中使用 ftpdpath 命令可以修改 FTP 服务器的默认路径。命令格式如下:
ftpdpath [new path]
new path :新的路径名
下面命令设置 FTP 服务器默认路径名为/sylixos。
# ftpdpath /sylixos
SylixOS 中使用 ftpds 命令可以查看链接到 SylixOS 中的所有 ftp 信息,命令格式如下:
ftpds
ftpds 命令显示如下,结果显示建立了一个 192.168.1.30 的 FTP 连接。
# ftpds
ftpd show >>
ftpd path: /sylixos
REMOTE TIME ALIVE(s)
--------------- ------------------------ ------------
192.168.1.30 Sat Jan 09 12:01:00 2167 11
total ftp session : 1
网络设备管理(Telnet)
Telnet 简介
Telnet 协议是个简单的远程登录协议,其服务过程可以分为以下三个步骤:
- 本地用户在本地终端上对远程系统进行登录。
- 将本地终端上的键盘输入逐键传到远端。
- 将远端的输出送回本地终端。
在上述过程中,输入/输出均对远端系统的内核透明,远程登录服务本身也对用户透明,这种透明是 Telnet 的重要特点。
Telnet 提供了三种基本的服务。首先定义了一个网络虚拟终端(Network Virtual Terminal,NVT),为远程系统提供了一个标准接口。客户机程序不必详细了解所有可能的远程系统,它们只需要使用标准接口的程序。其次,它包括了一个客户机和服务器协商选项的机制,而且还提供了一组标准选项。最后它对等地处理连接的两端。即连接的双方都可以是程序,尤其是客户端不一定非是用户终端不可,允许任意程序作为客户。
当用户调用 Telnet 时,用户机器上的应用程序作为客户与远程的服务器建立一个 TCP 连接,在此连接上进行通信。此时,客户就从用户键盘接受键盘消息并送到服务器,同时它接收服务器发回的字符并显示在用户屏幕上。
服务器本身并不直接处理从客户传输来的消息,而是将这些消息送给操作系统处理,然后再将返回的数据转交给客户。也就是说,此时的服务器,我们称之为“伪终端”(Pseudo Terminal),它允许像 Telnet 服务器一样的运行程序向操作系统转送字符,并且使得字符似乎是来自本地键盘一样。
为了提供在不同操作系统、不同种类计算机间的互操作性,Telnet 专门提供了一种标准的键盘定义方式,称为网络虚拟终端(NVT)。客户程序把来自用户终端的按键和命令序列转换成 NVT 格式,并发给服务器。远程服务器程序把收到的数据和命令,从 NVT 格式转换为远程系统需要的格式。对于返回的数据,远程服务器将数据从远程机器的格式转换为 NVT 格式,并且本地客户将数据从 NVT 格式转换为本地机器的格式。
目前 SylixOS 支持 Telnet 服务器,也就是说,可以通过 Telnet 客户端程序连接到 SylixOS 目标系统来管理 SylixOS 设备资源。
Telnet 协议命令
表中为 Telnet 协议的常用命令。
表 Telnet 协议命令
Telnet命令 | 说明 | Telnet命令 | 说明 |
---|---|---|---|
EOF | 文件结束符 | EL | 删除行 |
SUSP | 挂起当前进程 | GA | 继续进行 |
ABORT | 异常终止进程 | SB | 子选项开始 |
EOR | 记录结束符 | WILL | 发送方想激活选项 |
SE | 子选项结束 | WONT | 发送方想禁止选项 |
NOP | 无操作 | DO | 发送方想让接收方激活选项 |
DM | 数据标记 | DONT | 发送方想让接收方禁止选项 |
BRK | 中断 | IAC | IAC |
IP | 中断进程 | AYT | 对方是否在运行 |
AO | 异常终止输出 | EC | 转义字符 |
其中常用的选项协商如下:
- WILL xxx:我想具有 xxx 特性,你是否同意。
- WONT xxx:我不想具有 xxx 特性。
- DO xxx:我同意你可以具有 xxx 特性。
- DONT xxx:我不同意你具有 xxx 特性。
选项协商需要 3 个字节,首先是 IAC,然后是 WILL、DO、WONT 或 DONT,最后一个标识字节用来指明操作的选项,如下表所示。
表 选项标识
选项标识 | 说明 |
---|---|
ECHO(1) | 回显 |
SGA(3) | 抑制继续执行 |
STATUS(5) | 状态 |
TM(6) | 定时标记 |
TTYPE(24) | 终端类型 |
NAWS(31) | 窗口大小 |
TSPEED(32) | 端口速度 |
LFLOW(33) | 远程流式控制 |
LINEMODE(34) | 行方式 |
ENVIRON(36) | 环境变量 |
选项协商是 Telnet 协议中最复杂的部分,当一方要执行某个选项时需向另一端发送请求,若对方接受该选项,则选项在两端同时起作用,否则两端保持原来的模式,Telnet 的命令格式如下图所示。
图 协商命令格式
IAC 是 Telnet 协议中的保留码,双方用 IAC 确定收到的字节是数据还是命令,Telnet 协议的命令至少包含两个字符(IAC 和命令码)的字节序列,选项协商则有 3 个字节,第 3 个字节为协商的选项,当协商的选项存在子选项时,要进行子选项协商,命令格式如下图所示。
图 子选项协商命令格式
网络连接检测(ping)
ping 简介
ping 命令是用来查看网络上另一个主机系统的网络连接是否正常的工具。 ping 命令的工作原理是:向网络的另一个主机系统发送 ICMP 报文,如果指定的系统得到了报文,它再把报文传回给发送者。
前面我们介绍了 ICMP 协议是 IP 层的一个组成部分,因此需要使用原始套接字(SOCK_RAW)发送和接收 ICMP 报文, ping 命令使用的 ICMP 头格式,如下图所示。
其中标识部分作为发送方和接收方的约定标号,发送方将此标号发送给接收方,当收到接收方的回复后,发送方通过检测此标号以确定此回复是自己所关心的。序列号记录的 ping 的计数。
ping 命令
在 SylixOS 中可以使用 ping 命令来检查网络的是否连通,命令格式如下:
ping IP/hostname [-l datalen] [-n times] [-i ttl] [-w timeout]
IP :目标IP地址
hostname :主机名(如www.sylixos.com)
-l :数据长度
-n :次数
-i :TTL值
-w :超时值
下面命令检查是否连通 www.sylixos.com,显示如下:
# ping www.sylixos.com –n 3
Execute a DNS query...
Pinging www.sylixos.com [106.39.47.146]
Pinging 106.39.47.146
Reply from 106.39.47.146: bytes=32 time=0ms TTL=64
Reply from 106.39.47.146: bytes=32 time=0ms TTL=64
Reply from 106.39.47.146: bytes=32 time=0ms TTL=64
Ping statistics for 106.39.47.146:
Packets: Send = 3, Received = 3, Lost = 0(0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms
点对点连接(PPP)
PPP 简介
PPP 协议全称为点到点协议(Point-To-Point Protocol),它作为一种提供在点到点链路上传输封装网络层数据包的数据链路层协议处于 TCP/IP 协议栈的第二层,主要被设计用来在支持全双工的同异步链路上进行点到点之间的数据传输。
PPP 协议是在 SLIP(Serial Line IP)串行线 IP 协议的基础上发展起来的,由于 SLIP 协议只支持异步传输方式、无协商过程(尤其不能协商如双方 IP 地址等网络层属性)等缺陷,在以后的发展过程中逐步被 PPP 协议所替代。
PPP 主要由三类协议组成:链路控制协议族(LCP)、网络层控制协议族(NCP)和 PPP 扩展协议族。链路控制协议主要用于建立、拆除和监控 PPP 数据链路,网络层控制协议族主要用于协商在该数据链路上所传输的数据包的格式与类型,PPP 扩展协议族主要用于提供对 PPP 功能的进一步支持,同时 PPP 还提供了用于网络安全方面的验证协议族(PAP 和 CHAP)。
PPP 帧格式
PPP 帧格式,如下图所示。
字段 F 代表 Flag,此标志标识了一个物理帧的起始和结束,值为 0x7E;字段 A 代表 Address,此地址标识了 PPP 广播地址,值为 0xFF;字段 C 代表 Control 为控制字段,值为 0x03;FCS 字段为帧校验域。真正属于 PPP 报文内容的是 F、A、C、协议、信息字段。
根据协议字段的不同,信息字段也不同如下表所示。
协议 | 信息 |
---|---|
0x0021 | IP数据报 |
0xC021 | 链路控制协议LCP |
0x8021 | 网络控制数据NCP |
0xC023 | 安全性认证PAP |
0xC025 | LQR |
0xC223 | 安全性认证CHAP |
0x8031 | 桥NC |
0x802b | IPX控制协议 |
当信息字段中出现和标志字段一样的比特 0x7E 时,就必须采取一些措施。因 PPP 协议是面向字符型的,所以它不能采用 HDLC(高级数据链路控制)所使用的零比特插入法,而是使用一种特殊的字符填充。具体的做法是将信息字段中出现的每一个 0x7E 字节转变成 2 字节序列(0x7D,0x5E)。若信息字段中出现一个 0x7D 的字节,则将其转变成 2 字节序列(0x7D,0x5D)。若信息字段中出现 ASCII 码的控制字符,则在该字符前面要加入一个 0x7D 字节。这样做的目的是防止这些表面上的 ASCII 码控制字符被错误地解释为控制字符。
PPP 协商过程
PPP 在建立链路之前要进行一系列的协商过程,过程如下:PPP 首先进行 LCP 协商,协商内容包括:MRU(最大接收单元)、魔术字(magic number)、验证方式、异步字符映射等选项。LCP 协商成功后,进入链路建立(Establish)状态。如配置了 CHAP 或 PAP 验证,便进入 CHAP 或 PAP 验证阶段,验证通过后才会进入网络阶段协商(NCP),如 IPCP、IPXCP、BCP 的协商,任何阶段的协商失败都将导致链路的拆除。
魔术字主要用于检测链路自环,PPP 靠发送 Echo Request、Echo Reply 报文来检测自环和维护链路状态,如果连续发现有超过最大自环允许数目个 Echo Request 报文中魔术字与上次魔术字相同,则判定网络发生自环现象,如果链路发送自环,则就需要采取相应措施对链路复位。另外,LCP 发送 config request 时也可以检测自环,LCP 发现自环后,在发送一定数目的报文后,也会复位链路。如果 PPP 发送的 Echo Request 报文产生丢失,则在连续丢失最大允许丢失的个数之后,将链路复位,以免过多的无效数据传输。
PAP 验证过程
PAP 为两次握手协议,它通过用户名及口令来对用户进行验证。PAP 验证过程如下:当两端链路可相互传输数据时,被验证方发送本端的用户名及口令到验证方,验证方根据本端的用户表(或 radius 服务器)查看是否有此用户,口令是否正确。如正确则会给对端发送 ACK 报文,通告对端已被允许进入下一阶段协商;否则发送 NAK 报文,通告对端验证失败 此时并不会直接将链路关闭。只有当验证失败次数达到一定值时,才会关闭链路,来防止因误传、网络干扰等造成不必要的 LCP 重新协商过程。
PAP 的特点是在网络上以明文的方式传递用户名及口令,如在传输过程中被截获,便有可能对网络安全造成极大的威胁。因此,它适用于对网络安全要求相对较低的环境。
CHAP 验证过程
CHAP 为三次握手协议,它的特点是,只在网络上传输用户名,而并不传输用户口令,因此它的安全性要比 PAP 高。CHAP 的验证过程为:首先由验证方向被验证方发送一些随机产生的报文,并同时将本端的主机名附带上一起发送给被验证方。被验证方接到对端对本端的验证请求(Challenge)时,便根据此报文中验证方的主机名和本端的用户表查找用户口令字,如找到用户表中与验证方主机名相同的用户,便利用报文 ID、此用户的密钥用 Md5 算法生成应答(Response),随后将应答和自己的主机名送回。验证方接到此应答后,用报文 ID、本方保留的口令字(密钥)和随机报文用 Md5 算法得出结果,与被验证方应答比较,根据比较结果返回相应的结果。
PPP API
SylixOS 中使用下面函数可以创建 PPPoS 型网卡。
#include <SylixOS.h>
INT API_PppOsCreate(CPCHAR pcSerial,
LW_PPP_TTY *ptty,
PCHAR pcIfName,
size_t stMaxSize);
函数 API_PppOsCreate 原型分析:
- 此函数成功返回 0,失败返回-1 并设置错误号。
- 参数 pcSerial 是串行接口设备名。
- 参数 ptty 是串口工作参数。
- 输出参数 pcIfName 返回网络接口名。
- 参数 stMaxSize 是缓冲区大小。
下面函数将创建 PPPoE 型网卡。
#include <SylixOS.h>
INT API_PppOeCreate(CPCHAR pcEthIf, PCHAR pcIfName, size_t stMaxSize);
函数 API_PppOeCreate 原型分析:
- 此函数成功返回 0,失败返回-1 并设置错误号。
- 参数 pcEthIf 是以太网网卡名。
- 输出参数 pcIfName 返回网络设备名称。
- 参数 stMaxSize 缓冲区大小。
下面函数将创建 PPPoL2TP 型网卡。
#include <SylixOS.h>
INT API_PppOl2tpCreate(CPCHAR pcEthIf, CPCHAR pcIp,
UINT16 usPort, CPCHAR pcSecret,
size_t stSecretLen, PCHAR pcIfName,
size_t stMaxSize);
函数 API_PppOl2tpCreate 原型分析:
- 此函数成功返回 0,失败返回-1 并设置错误号。
- 参数 pcEthIF 是以太网名字。
- 参数 pcIp 是服务器地址。
- 参数 usPort 是服务器端口。
- 参数 pcSecret 是安全相关。
- 参数 stSecretLen 是缓冲区大小。
- 输出参数 pcIfName 返回网络设备名称。
- 参数 stMaxSize 是缓冲区大小。
下面函数进行拨号连接。
#include <SylixOS.h>
INT API_PppConnect(CPCHAR pcIfName, LW_PPP_DIAL *pdial);
函数 API_PppConnect 原型分析:
- 此函数成功返回 0,失败返回-1 并设置错误号。
- 参数 pcIfName 是网络设备名。
- 参数 pdial 是拨号参数。
下面函数断开连接和删除连接。
#include <SylixOS.h>
INT API_PppDisconnect(CPCHAR pcIfName, BOOL bForce);
INT API_PppDelete(CPCHAR pcIfName);
函数 API_PppDisconnect 原型分析:
- 此函数成功返回 0,失败返回-1 并设置错误号。
- 参数 pcIfName 是网络设备名。
- 参数 bForce 表示是否强制断开。
函数 API_PppDelete 原型分析:
- 此函数成功返回 0,失败返回-1 并设置错误号。
- 参数 pcIfName 是网络设备名。
下面函数获得设备连接状态。
#include <SylixOS.h>
INT API_PppGetPhase(CPCHAR pcIfName, INT *piPhase);
函数 API_PppGetPhase 原型分析:
- 此函数成功返回 0,失败返回-1 并设置错误号。
- 参数 pcIfName 是网络设备名。
- 输出参数 piPhase 返回连接状态。
网络地址转换 (NAT)
NAT 简介
NAT 是将 IP 数据报报头中的 IP 地址转换为另一个 IP 地址的过程。在实际应用中,NAT 主要用于实现私有网络访问外部网络的功能。这种通过使用少量的公有 IP 地址映射多数的私有 IP 地址的方式,可以在一定程度上缓解 IP 地址空间枯竭的压力。
私有网络地址(以下简称私网地址)是指内部网络或主机的 IP 地址,公有网络地址(以下简称公网地址)是指在互联网上全球唯一的 IP 地址。IANA( Internet AssignedNumber Authority)规定将下列的 IP 地址保留用作私网地址,不在 Internet 上被分配。
- A 类私有地址:10.0.0.0~10.255.255.255。
- B 类私有地址:172.16.0.0~172.31.255.255。
- C 类私有地址:192.168.0.0~192.168.255.255。
如果选择上述三个范围之外的其他网段作为内部网络地址,则当与其他网络互通时有可能造成混乱。
NAT 转换机制
路由器中维护着一张地址端口对应表,所有经过路由器并且需要进行地址转换的报文,都会通过这个对应表做相应的修改,进行 < 私有地址+端口 > 与 < 公有地址+端口 > 之间的转换。转换过程如下所示:
- 内部网络主机向外发送报文时,路由器将报文的源 IP 地址和端口替换为路由器的外部网络地址和端口。
- 当外部的报文进入内部网络时,路由器会查找地址端口对应表,将报文的目的地址和端口进行转换,转换为真正的目的地址。
如下图所示,内部用户访问外部服务器的流程如下:
- 用户向服务器发送源地址为 1.1.1.1:5000、目的地址为 2.2.2.2:5000 的报文。
- 用户发至服务器的报文,在经过路由器的时候,经过地址转换,报文的源地址由 1.1.1.1:5000 改变为 2.2.2.1:2000。
- 服务器收到用户的报文后,向用户回送报文,报文的源地址为 2.2.2.2:5000,目的地址为 2.2.2.1:2000。
- 服务器发至用户的报文,在经过路由器的时候,经过地址转换,目的地址由 2.2.2.1:2000 改变为 1.1.1.1:5000。
上述的地址转换过程对终端(如下图中的用户和服务器)来说是透明的。对外部服务器而言,它认为客户的 IP 地址就是 2.2.2.1,而并不知道有 1.1.1.1 这个地址。因此,NAT“隐藏”了私有网络的拓扑。
在内部网络的主机访问外部网络的时候,当内部网络的主机非常多,外部 IP 地址却只有一个时,地址转换可能就会显得效率比较低。解决这个问题需要一个私有网络拥有多个外部地址。
SylixOS 中实现的 NAT 使用地址池的方式来解决上面的问题,地址池就是一些合法 IP 地址(公有网络 IP 地址)的集合。用户可根据自己拥有的合法 IP 地址的多少、内部网络主机的多少、以及实际应用情况,配置合适的 IP 地址池。当主机从内部网络访问外部网络时,将会从地址池中挑选一个 IP 地址做为转换后的报文源地址。
NAT 地址转换使内部的大量主机可以使用少量公网 IP 地址就可以访问外部网络资源,并且为内部主机提供了“隐私”保护,但是地址转换也存在了以下缺点:
- 涉及 IP 地址的数据报文不能被加密,否则无法对数据报文进行地址转换。在应用层协议中,如果报文中有地址或端口需要转换,则报文不能被加密。例如,不能使用加密的 FTP 连接,否则 FTP 的 port 命令不能被正确转换。
- 网络调试变得更加困难。比如,某一台内部网络的主机试图攻击其他网络,则很难指出究竟是哪一台机器是恶意的,因为主机的 IP 地址被屏蔽了。
NAT API
在 SylixOS 中,调用下面函数开始 NAT 转换和停止 NAT 转换。
#include <SylixOS.h>
INT Lw_Inet_NatStart(CPCHAR pcLocalNetif, CPCHAR pcApNetif);
INT Lw_Inet_NatStop(VOID);
函数 Lw_Inet_NatStart 原型分析:
- 此函数成功返回 0,失败返回-1 并设置错误号。
- 参数 pcLocalNetif 是本地内外网络接口。
- 参数 pcApNetif 是外网网络接口。
NAT Shell 命令
下表是 SylixOS NAT 功能支持的所有 Shell 命令:
命令 | 说明 |
---|---|
nats | 查看 nat 信息 |
natmap | nat 转换端口映射 |
natalias | nat 别名配置 |
natlocal | 增加 nat 本地端口 |
natwan | 增加 nat 外部端口 |
natipfrag | nat 中 IP 分片支持开关 |
nat | nat 功能启停 |
nattraffic | nat 通路状态信息开关 |
在 SylixOS 中可以使用 nats 命令查看 NAT 信息,使用 nat 命令可以开始和停止 NAT 网络。命令格式如下:
nat [stop] / {[LAN netif] [WAN netif]}
stop :停止NAT网络
LAN netif :本地网络接口
WAN netif :外部网络接口
下面命令将设置 wl2 作为本地网络接口,en1 作为外部网络接口。
# nat wl2 en1
nats 命令格式如下:
nats
ip addr :IP地址
nats 命令输出如下:
# nats
NAT networking alias setting >>
ALIAS LOCAL START LOCAL END
--------------- --------------- ---------------
NAT networking direct map setting >>
ASS PORT LOCAL PORT LOCAL IP IP CNT PROTO
---------- ---------- --------------- -------- -------
NAT networking summary >>
NAT networking off!
IP Fragment: TCP-Disable UDP-Disable ICMP-Disable
使用 natwan 命令增加一个新的网络接口为 WAN 口, natlocal 命令增加一个新的网络接口为 LAN 口,另外, natmap 命令可以实现将 WAN 口上对应的外网网络端口(port1)映射到内外中的某个网络地址上的一个端口(port2),命令使用例子如下:
natmap add port1 port2 192.168.1.2 tcp 1
以上命令的含义是:将外网的端口 port1 映射到内外地址 192.168.1.2 的端口 port2,映射的协议为 TCP。
说明:
natmap 支持映射的协议包括:TCP、UDP。
nattraffic命令用于开启或关闭 NAT 数据通路,执行 nattraffic on 即可开启该功能,开启之后可以通过 /proc/net/nat/traffic 文件 NAT 信息,如下所示:
# cat /proc/net/nat/traffic
LOCAL IP OUT (Kbps) IN (Kbps) OUT KB IN KB
--------------- ----------- ----------- --------- ---------
10.6.2.128 0 0 770629 16240943
52.123.252.0 0 0 0 0
47.97.209.0 0 0 0 0
40.126.35.128 0 0 0 0
40.78.122.0 0 0 0 0
- LOCAL IP:本地 IP 地址;
- OUT (Kbps):从协议栈出去的包速度;
- IN (Kbps):进入协议栈的包速度;
- OUT KB:从协议栈出去的包总数;
- IN KB:进入协议栈的包总数。
SylixOS 下可以通过查看 /proc/net/nat/assnode 文件获取 NAT 节点信息,如下所示:
# cat /proc/net/nat/assnode
LOCAL IP LOCAL PORT ASS PORT PROTO IDLE(sec) STATUS
--------------- ---------- -------- ----- --------- --------
10.6.2.44 32884 53080 TCP 80 OPEN
10.6.2.38 32790 63189 TCP 90 FIN
10.6.2.59 32878 64420 TCP 170 OPEN
10.6.2.56 32924 56477 TCP 255 OPEN
10.6.2.56 32994 58786 TCP 460 FIN
10.6.2.59 32972 65489 TCP 20 OPEN
10.5.2.17 32903 49902 TCP 10 OPEN
- LOCAL IP:本地 IP 地址;
- LOCAL PORT:本地端口号;
- ASS PORT:对外端口号;
- PROTO:协议类型;
- IDLE(sec):当前链接已经空闲的秒数;
- STATUS:当前链接的状态。
网络路由(route/sroute)
路由原理
IP 路由选择通常是简单的。如果目的主机与源主机直接相连(如点对点链路)或都在一个共享网络上(以太网或令牌环网),那么 IP 数据报就直接送到目的主机上。否则,主机把数据报发往默认的路由器上,由路由器来转发该数据报。大多数的主机都是采用这种简单机制。
在一般的体制中,IP 可以从 TCP、UDP、ICMP 和 IGMP 接收数据报(即在本地生成的数据报)并进行发送,或者从一个网络接口接收数据报(待转发的数据报)并进行发送。IP 层在内存中有一个路由表,当收到一份数据报并进行发送时,它都要对该表搜索一次。当数据报来自某个网络接口时,IP 首先检查目的 IP 地址是否为本机的 IP 地址之一或者 IP 广播地址。如果确实是这样,数据报就被送到由 IP 首部协议字段所指定的协议模块进行处理。如果数据报的目的不是这些地址,那么如果 IP 层被设置为路由器的功能,那么就对数据报进行转发,否则数据报被丢弃。
路由表的每一项都包含下面这些信息:
- 下一站(或下一跳)路由器(next-hop router)的 IP 地址,或者有直接连接的网络 IP 地址。下一站路由器是指一个在直接相连网络上的路由器,通过它可以转发数据报。下一站路由器不是最终的目的,但是它可以把传送给它的数据报转发到最终目的。
- 标志。其中一个标志指明目的 IP 地址是网络地址还是主机地址,另一个标志指明下一站路由器是否为真正的下一站路由器,还是一个直接相连的接口,如下表所示。
- 为数据报的传输指定一个网络接口。
- 网络接口名字(字符串名)。
IP 路由选择是逐跳地(hop-by-hop)进行的。从这个路由表信息可以看出,IP 并不知道到达任何目的的完整路径(当然,除了那些与主机直接相连的目的)。所有的 IP 路由选择只为数据报传输提供下一站路由器的 IP 地址。它假定下一站路由器比发送数据报的主机更接近目的,而且下一站路由器与该主机是直接相连的。
IP 路由选择主要完成以下功能:
- 搜索路由表,寻找能与目的 IP 地址完全匹配的表目(网络号和主机号都要匹配)。如果找到,则把报文发送给该表目指定的下一站路由器或直接连接的网络接口(取决于标志字段的值)。
- 搜索路由表,寻址能与目的 IP 地址匹配的网络地址。
- 搜索路由表,寻找标为“默认(default)”的表目。如果找到,则把报文发送给该表目指定的下一站路由器。
如果上面这些步骤都没有成功,那么该数据报就不能被传送。如果不能传送的数据报来自本机,那么一般会向生成数据报的应用程序返回一个“主机不可达”或“网络不可达”的错误。
完整主机地址匹配在网络号匹配之前执行。只有当它们都失败后才选择默认路由。默认路由,以及下一站路由器发送的 ICMP 间接报文(如果我们为数据报选择了错误的默认路由)是 IP 路由选择机制中功能强大的特性。
路由匹配总是优先匹配本地主机,如果本地主机匹配失败,则匹配网络地址。
下表描述符了路由标志:
路由标志 | 说明 |
---|---|
U | 该路由可以使用 |
G | 路由到一个网关(路由器)。如果没有此标志说明目的地址是直接连接 |
H | 路由到一个主机,目的地址是一个完整的主机地址。如果没有设置该标志,说明该路由是到一个网络,而目的地址是一个网络地址:一个网络号,或者网络号与子网组合 |
D | 该路由是由重定向报文创建的 |
M | 该路由已被重定向报文修改 |
标志 G 是非常重要的,因为由它区分了间接路由和直接路由(直接路由不设置该标志)。发往直接路由的分组中不但具有目的端的 IP 地址,还具有其链路层地址。发往一个间接路由时,IP 地址指明的是最终的目的地址,链路层地址指明的是网关(下一站路由)。
H 标志表明目的地址是一个完整的主机地址,没有设置 H 标志说明目的地址是一个网络地址(主机号部分是 0)。当为某个目的 IP 地址搜索路由表时,主机地址项必须与目的地址完全匹配,而网络地址项只需匹配目的地址的网络号和子网号就可以了。
每当初始化一个接口时,就为接口自动创建一个直接路由,对于点对点链路和环回接口来说,路由是到达主机(例如,设置 H 标志)。对于广播接口来说,如以太网,路由是到达网络。
到达主机或网络的路由如果不是直接相连的,那么就必须加入路由表, route 命令描述了如何向路由表填加一条路由条目。
源路由(策略路由)
源路由(Source Routing)是一种网络通信的机制,其中数据包的发送者(源)可以指定数据包的传输路径,而不是由网络设备根据路由表自动选择路径。
在源路由中,发送者可以在数据包的首部中指定经过的中间节点或路由器,以确定数据包的传输路径。这些中间节点按照数据包首部中的指示进行转发,直到数据包到达目标节点。
源路由的主要优点是发送者可以精确控制数据包的路径,可以绕过故障节点或选择更优的路径。它可用于避免特定网络拓扑中的瓶颈或故障,或者用于实现特定的网络策略和优化。
然而,源路由也存在一些限制和安全风险。由于发送者完全控制数据包的路径,因此可能会有恶意用户滥用源路由功能,导致网络安全问题。为了应对这些问题,源路由的使用通常受到网络设备和协议的限制,并且需要进行适当的安全措施。
需要注意的是,源路由的使用相对较少,大多数网络通信都依赖于动态路由协议和路由器根据路由表自动选择最佳路径来转发数据包。源路由通常用于特定的网络环境或特殊需求,而在一般的互联网通信中较少使用。
SylixOS 提供了 sroute命令来配置源路由的路由条目,详细使用方法可参见 “Shell 概述” 章节。
路由 API(老式接口)
SylixOS 1.5.6 之前的版本应用程序可以调用简易路由接口来操作路由表,下面函数可以增加一条路由信息到路由表:
#include <sys/route.h>
int route_add (struct in_addr *pinaddr, int type, const char *ifname);
函数 route_add 原型分析:
- 此函数成功返回 0,失败返回-1 并设置错误号。
- 参数 pinaddr 是路由地址。
- 参数 type 是路由类型,如下表所示。
- 参数 ifname 是网络接口名。
调用 route_change 函数可以改变一条路由信息:
#include <sys/route.h>
int route_change(struct in_addr *pinaddr, int type, const char *ifname);
函数 route_change 原型分析:
- 此函数成功返回 0,失败返回-1 并设置错误号。
- 参数 pinaddr 是路由地址。
- 参数 type 是路由类型如下表所示。
- 参数 ifname 是网络接口名。
调用 route_delete 函数删除一条路由信息:
#include <sys/route.h>
int route_delete(struct in_addr *pinaddr);
函数 route_delete 原型分析:
- 此函数成功返回 0,失败返回-1 并设置错误号。
- 参数 pinaddr 是路由地址。
调用 route_getnum 函数可以获得路由表中总的路由条目:
#include <sys/route.h>
int route_getnum(void);
调用 route_get 函数可以获得满足一定条件的路由信息:
#include <sys/route.h>
int route_get(u_char flag, struct route_msg *msgbuf, size_t num);
函数 route_get 原型分析:
- 此函数返回获得的路由条目数。
- 参数 flag 是类型掩码如下表所示类型。
- 输出参数 msgbuf 是路由信息结构。
- 参数 num 是路由信息结构缓存数。
route_get 函数获得的路由条目信息将被存储在 route_msg 结构中,该结构描述如下:
struct route_msg {
u_char rm_flag; /* 类型 */
int rm_metric; /* 暂时未使用 */
struct in_addr rm_dst; /* 目的地址信息 */
struct in_addr rm_gw; /* 网关信息 */
struct in_addr rm_mask; /* 子网掩码信息 */
struct in_addr rm_if; /* 网络接口地址 */
char rm_ifname[IF_NAMESIZE]; /* 网络接口名 */
};
注意:
路由类型以或的方式存储在 route_msg 结构的成员 rm_flag 中。
路由类型 | 说明 |
---|---|
ROUTE_TYPE_HOST | 目的是主机 |
ROUTE_TYPE_NET | 目的是网络 |
ROUTE_TYPE_GATEWAY | 目的是网关 |
ROUTE_TYPE_DEFAULT | 默认网关 |
route 命令(老式命令)
使用 route 命令可以显示或设置系统路由表信息。命令格式如下:
route [add | del | change] {-host | -net | -gw} [addr] [[dev] | if]
add :增加路由表条目
del :删除路由表条目
change :改变路由表条目
-host :主机地址
-net :网络地址
-gw :网关地址
dev :网络设备
addr :IP地址
if :网络设备接口名
输出信息含义
# route
kernel routing tables
Destination Gateway Mask Flag Interface
build-in routing tables
Destination Gateway Mask Flag Interface
192.168.7.0 * 255.255.255.0 U en1
192.168.7.30 * 255.255.255.0 UH en1
127.0.0.0 * 255.0.0.0 U lo0
127.0.0.1 * 255.0.0.0 UH lo0
default 192.168.7.1 255.255.255.0 UG en1
输出信息的各标题含义如下:
- Destination:路由选择目的地址。
- Gateway:网关地址。
- Mask:子网掩码。
- Flag:路由标志,如下表所示。
- Interface:网络接口。
输出的倒数第 2 行是环回接口,它的名字是 lo0,该条路由标志是“UH”,没有“G”标志说明该路由不是一个网关而是一个直接路由,“H”标志说明目的地址(127.0.0.1)是一个主机地址,而不是一个网络地址。
最后一行为默认路由,每个主机都有一个或多个默认路由。这一项表明,如果在路由表中没有找到特定的路由,就把分组发送到路由器 192.168.7.1,这说明当前主机利用这一个路由表项就可以通过 Internet 经路由器 192.168.7.1 访问其他的系统,“UG”标志表明它是一个网关。
路由标志 | 说明 |
---|---|
U | 该路由可以使用 |
G | 路由到一个网关(路由器)。如果没有此标志说明目的地址是直接连接 |
H | 路由到一个主机,目的地址是一个完整的主机地址。如果没有设置该标志,说明该路由是到一个网络,而目的地址是一个网络地址:一个网络号,或者网络号与子网组合 |
D | 该路由是由重定向报文创建的 |
M | 该路由已被重定向报文修改 |
路由 API(新式接口)
SylixOS 1.5.6 之后的版本应用程序可以通过调用标准 IO 的 ioctl 函数或者 write 函数来操作路由条目。
- 支持的 ioctl 命令如下:。
- SIOCADDRT :添加一条路由信息。
- SIOCDELRT :删除一条路由信息。
- SIOCCHGRT :修改一条路由信息。
- SIOCGETRT :获取一条路由信息。
- SIOCLSTRT :遍历 IPv4 路由信息。
- SIOCLSTRTM :获取整个 IPv4 的路由信息。
- Ioctl 函数的参数 pArg 为以下类型结构体的指针,结构体如下:
struct rtentry {
u_long rt_pad1;
struct sockaddr rt__dst; /* 目的地址 */
struct sockaddr rt_gateway; /* 网络网关 */
struct sockaddr rt_genmask; /* 子网掩码 */
char rt_ifname[IF_NAMESIZE]; /* 网络接口 */
u_short rt_flags;
u_short rt_refcnt;
u_long rt_pad3;
void *rt_pad4;
short rt_metric; /* 度量 */
void *rt_dev; /* 未使用的设备 */
u_long rt_hopcount; /* 跳数 */
u_long rt_mtu; /* 路由的MTU */
u_long rt_window;
u_short rt_irtt;
u_long rt_pad5[16]};
命令 SIOCLSTRT 相关的结构体如下:
struct rtentry_list {
u_long rtl_bcnt; /* rtentrt缓存区大小 */
u_long rtl_num; /* 缓存区中路由数量 */
u_long rtl_total; /* 所有的路由数量 */
struct rtentry *rtl_buf; /* 路由缓存 */
};
命令 SIOCLSTRTM 相关结构体如下:
struct rt_msghdr_list {
size_t rtml_bsize; /* rtml_buf缓存区大小 */
size_t rtml_rsize; /* 缓存区路由数量 */
size_t rtml_tsize; /* 系统返回所有的路由数量 */
struct rt_msghdr *rtml_buf; /* 路由缓存 */
};
实例代码:
- 已经知道可以通过标准 IO 的 ioctl 函数来实现对路由条目的各种操作,包括添加、删除、修改以及获得路由信息。
- 在下面程序里,通过填充路由对外的结构体 rtentry 里的信息来实现添加路由的操作,首先用 bzero 函数清空结构体的信息,设置主机路由为 RTF_UP | RTF_HOST,填充目的地址、子网掩码以及网关的协议簇和协议长度,而后通过 inet_aton 函数将想要的目的地址、子网掩码以及网关信息写入 sin_addr 中,最后设置 metric 和 rt_ifname 。
- 使用 AF_INET 生成的套接字,通过 ioctl 函数调用 SIOCADDRT 命令实现对路由的添加。
#include <stdio.h>
#include <net/route.h>
#include <string.h>
#include <socket.h>
int main (int argc, char **argv)
{
int iSock;
int iRet;
struct rtentry rtentry;
bzero(&rtentry, sizeof(struct rtentry));
rtentry.rt_flags = RTF_UP | RTF_HOST; /* 设置主机路由 */
rtentry.rt_dst.sa_len = sizeof(struct sockaddr_in);
rtentry.rt_dst.sa_family = AF_INET;
rtentry.rt_genmask.sa_len = sizeof(struct sockaddr_in);
rtentry.rt_genmask.sa_family = AF_INET;
rtentry.rt_gateway.sa_len = sizeof(struct sockaddr_in);
rtentry.rt_gateway.sa_family = AF_INET;
inet_aton("192.168.1.32", &((struct sockaddr_in *)&rtentry.rt_dst)->sin_addr);
inet_aton("255.255.255.0", &((struct sockaddr_in *)&rtentry.rt_genmask)->sin_addr);
inet_aton("192.168.2.1", &((struct sockaddr_in *)&rtentry.rt_gateway)->sin_addr);
strlcpy(rtentry.rt_ifname, "en1", IF_NAMESIZE);
rtentry.rt_metric = 3;
iSock = socket(rtentry.rt_dst.sa_family, SOCK_DGRAM, 0);
if (iSock < 0) {
return (PX_ERROR);
}
/*
* 使用 SIOCADDRT 命令添加一条路由
*/
iRet = ioctl(iSock, SIOCADDRT, &rtentry);
if (iRet < 0) {
fprintf(stdout, "fail to ioctl\n");
close(iSock);
return (-1);
} else {
close(iSock);
}
return (0);
}
在 SylixOS Shell 下运行程序,部分结果显示如下:
# ./route_ioctl
# route
IPv4 Route Table:
Destination Gateway Genmask Flags Metric Ref Use Iface
123.123.123.123 123.0.0.1 255.0.0.0 UH 0 0 0 en1
192.168.1.32 192.168.2.1 255.255.255.0 UH 3 0 0 en1
0.0.0.0 10.4.0.1 0.0.0.0 UG 4 0 0 en1
10.4.120.30 0.0.0.0 255.255.255.255 UH 4 0 0 en1
10.4.0.0 0.0.0.0 255.255.0.0 U 4 0 0 en1
127.0.0.1 0.0.0.0 255.255.255.255 UH 4 0 0 lo0
127.0.0.0 0.0.0.0 255.0.0.0 U 4 0 0 lo0
通过 route 命令查看路由信息可以看出,想要设置的路由已经添加完成。即目的地址 192.168.1.32,子网掩码 255.255.255.0,网关 192.168.1.1,metric 为 3,接口为 en1 的路由条目。
使用 write 函数添加路由需要填充 rt_msghdr 结构体,首先获得由路由套接字 AF_ROUTE 生成的文件描述符,注意此处必须是 SOCK_RAW 才能沟通底层,填充 rt_msghdr 结构体时,需要构建 msg 结构体,结构体的 buf 数组用来存放想要设置的目的地址、子网掩码以及网关,另外值得注意的是设置目的地址、掩码以及网关时,对应的协议簇和长度也需要填充。
填充信息后调用 write 函数来实现路由的添加。
#include <stdio.h>
#include <unistd.h>
#include <net/route.h>
#include <string.h>
#include <socket.h>
#define ROUND_UP(x, align) (size_t)(((size_t)(x) + (align - 1)) & ~(align - 1))
#define SO_ROUND_UP(len) ROUND_UP(len, sizeof(size_t))
#define SA_ROUND_UP(x) SO_ROUND_UP(((struct sockaddr *)(x))->sa_len)
#define SA_NEXT(t, x) (t)((PCHAR)(x) + SA_ROUND_UP(x))
#define SOCKADDRSET(X,R) \
if (msg.rtm.rtm_addrs & (R)) \
{ \
int len = SA_ROUND_UP(X); \
memcpy(pnt, (caddr_t)(X), len); \
pnt += len; \
}
struct {
struct rt_msghdr rtm;
char buf[512];
} msg;
int main (int argc, char **argv)
{
int iSock;
ssize_t iRet;
caddr_t pnt;
struct sockaddr_in dest;
struct sockaddr_in mask;
struct sockaddr_in gate;
iSock = socket(AF_ROUTE, SOCK_RAW, 0);
if (iSock < 0) {
fprintf(stdout, "fail to socket\n");
return (-1);
}
static int msg_seq = 0;
memset (&msg, 0, sizeof (struct rt_msghdr));
msg.rtm.rtm_version = RTM_VERSION;
msg.rtm.rtm_type = RTM_ADD;
msg.rtm.rtm_seq = msg_seq++;
msg.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
msg.rtm.rtm_flags = RTF_HOST | RTF_UP;
msg.rtm.rtm_index = 1;
pnt = (caddr_t)msg.buf;
inet_aton("192.168.1.32", &dest.sin_addr);
dest.sin_family = AF_INET;
dest.sin_len = sizeof(struct sockaddr_in);
inet_aton("192.168.1.1", &gate.sin_addr);
gate.sin_family = AF_INET;
gate.sin_len = sizeof(struct sockaddr_in);
inet_aton("255.255.255.0", &mask.sin_addr);
mask.sin_family = AF_INET;
mask.sin_len = sizeof(struct sockaddr_in);
SOCKADDRSET (&dest, RTA_DST);
SOCKADDRSET (&gate, RTA_GATEWAY);
SOCKADDRSET (&mask, RTA_NETMASK);
msg.rtm.rtm_msglen = pnt - (caddr_t) &msg;
iRet = write(iSock, &msg, msg.rtm.rtm_msglen);
if (iRet < 0) {
fprintf(stdout, "fail to write error\n");
return (PX_ERROR);
}
return (0);
}
在 SylixOS Shell 下运行程序,部分结果显示如下:
# ./route_write
# route
IPv4 Route Table:
Destination Gateway Genmask Flags Metric Ref Use Iface
123.123.123.123 123.0.0.1 255.0.0.0 UH 0 0 0 en1
192.168.1.32 192.168.1.1 255.255.255.0 UH 0 0 0 lo0
192.168.1.32 192.168.2.1 255.255.255.0 UH 3 0 0 en1
0.0.0.0 10.4.0.1 0.0.0.0 UG 4 0 0 en1
10.4.120.30 0.0.0.0 255.255.255.255 UH 4 0 0 en1
10.4.0.0 0.0.0.0 255.255.0.0 U 4 0 0 en1
127.0.0.1 0.0.0.0 255.255.255.255 UH 4 0 0 lo0
127.0.0.0 0.0.0.0 255.0.0.0 U 4 0 0 lo0
通过 route 命令查看路由信息可以看出,想要设置的路由已经添加完成。即目的地址 192.168.1.32,子网掩码 255.255.255.0,网关 192.168.1.1,接口为 en1 的路由。由于未设置 metric,默认为 0。
route 命令(新式命令)
在 SylixOS 中, route 命令用来处理与网络路由相关的信息,如可以通过指令添加、删除、修改以及获取的一条 IPv4 或者 IPv6 路由信息,也可以通过遍历获得 IPv4 或者 IPv6 路由所有信息。命令格式如下:
route [add|del|chg] [-host|-net|dl|gw] [dest] [netmask] [geteway] {metric} [dev]
add :增加路由表条目
del :删除路由表条目
chg :修改路由表条目的metric
-host :主机地址
-net :网络地址
dest : 目标地址
netmask :子网掩码
gateway :网关地址
metric :度量
dev :网络设备接口
dl :default ,主要对默认路由信息进行操作
gw : gateway ,用法与host相似
下面是 route 命令的使用实例:
- 添加一条到主机的路由(网络接口:en1)。
route add –host 192.168.7.40 mask 255.0.0.0 123.0.0.0 dev en1
- 添加一条到网络的路由(网络接口:en2)并设置 metric 为 3。
route add –net 180.149.132.47 mask 255.0.0.0 12.0.0.0 metric 3 dev en2
- 删除一条网络路由。
route del -net 180.149.132.47 mask 255.0.0.0
- 删除一条主机路由。
route del -host 192.168.7.40 dev en1
- 改变一条主机路由的 metric 为 3。
route chg –host 192.168.7.40 mask 255.0.0.0 123.0.0.0 metric 3 dev en1
多域信息安全(ifsec)
多域信息安全简介
网络信息系统通常将自身划分为多个自治管理域(也称为安全域) ,通过多域间的安全隔离来实现对用户的访问控制以及信息资源的安全管理。
SylixOS 通过对宏 LW_CFG_LWIP_SEC_REGION 的控制来选择是否支持多域信息安全功能,使用 shell 命令“ ifsec ”来设置网络接口的安全域值。
多域信息安全机制
SylixOS 的多域信息安全功能支持 TCP、UDP、RAW、AF_PACKET 等多种协议网络数据包传输,下面以 TCP 为例,介绍一下该机制原理:
首先,用户通过 shell 命令“ ifsec ”设置网络接口的域值,之后,当网卡接收到以太网数据包时,会将数据包传给 LwIP 协议栈,数据包传到 TCP 传输层时,进入 tcp_input 函数中,对网络接口安全域值和 TCP 协议控制块安全域值进行对比,若域值一致,则将数据传递给应用层,否则丢弃该数据包。
ifsec 命令
在 SylixOS 中可以使用 ifsec 命令来设置网络接口的安全域值,命令格式如下:
ifsec [ifname] [security region]
ifname:网络接口名称
security region:安全域值,范围0~255
SO_SECREGION 选项
在 SylixOS 中可以使用 SO_SECREGION 选项来设置套接字的安全域值,使用方法如下所示:
int sec_region = 5;
socklen_t optlen = sizeof(sec_region);
setsockopt(sockFd, SOL_SOCKET, SO_SECREGION, (const void *)&sec_region, optlen);
sec_region 的值为需要设置的套接字安全域值。