ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

VxWorks IPnet协议栈NAT配置与ipnet_eth_input排查实践

VxWorks IPnet协议栈NAT配置与ipnet_eth_input排查实践 简介这是一份面向嵌入式网络开发者的VxWorks 6.5 IPNet组件源代码资源聚焦IPTCP、IPNet_NAT_Rule与IPNet_ETH_Input三大模块为理解实时操作系统TCP/IP协议栈的实现与调优提供直接参考。压缩包共112个文件包括76个C源文件、31个头文件以及4个mk构建脚本和makefile整体仅633KB便于快速下载与阅读。内容覆盖IP层收发、路由表管理、NAT地址转换、套接字接口、ICMPv6以及以太网输入解析等核心路径代码模块划分清晰适合需要深入分析网络协议栈、排查通信问题或定制网络功能的开发者对照源码学习。已有342人浏览学习建议结合描述中的模块剖析与实际调试场景进行阅读可有效掌握VxWorks网络组件的工作原理与实现细节。1. 从 ipnet2.rar 说起IPnet 在 VxWorks 里的组件化布局做网络设备的朋友大概率都收过类似ipnet2.rar这种命名的工程包里面不是一份完整文档而是 IPnet 协议栈相关的源码、组件定义和编译产物。真正让新人卡住的地方不是包里的代码而是 IPnet 本身它在 VxWorks 里不是一个大而全的 TCP/IP而是一堆可裁剪组件——IPTCP、NAT、eth_input 各自独立互相之间靠配置和钩子耦合。抓包看到ipnet_eth_input时脑子里得立刻浮现出收包路径的前后顺序以及 NAT 规则在哪一步介入。这篇文章顺着这个思路把 IPnet 的组件关系、NAT 规则下发、eth_input 回调跟踪这三件事串起来给一套能在目标板上实际操作的方案。适合正在做 VxWorks 移植、网络功能开发或排障的工程师尤其是第一次接触 IPnet 项目的人。2. 拆开 IPnet 组件IPTCP、NAT 与 eth_input 的依赖关系2.1 IPnet 组件的配置清单与裁剪IPnet 协议栈以组件component的形式存在于 VxWorks 工程中每个组件对应一组源文件和配置宏。常见组件包括IPNET_CORE、IPNET_TCP、IPNET_UDP、IPNET_NAT、IPNET_ETH等。你拿到的包里那个ipnet2.rar通常就是某个 BSP 工程导出的组件备份里面带有prjConfig.c和组件依赖表。要在自己的工程里确认组件清单常见做法是使用 Workbench 自带命令行工具查询工程组件。假设你的工程位于$VXWORKS_WS/proj可以执行vxprj component list proj | grep -i nat执行结果会列出 NAT 组件是否被包含以及它依赖的组件名。NAT 一般不是独立存在的它必须依赖IPNET_CORE和IPNET_IP同时与IPNET_ETH有隐含的绑定关系因为地址转换发生在 IP 层但数据要从以太网接口进来。裁剪时要注意如果只添加IPNET_NAT而没有添加IPNET_ETH规则可以配置但数据包进不来。反过来如果只保留IPNET_ETH而砍掉IPNET_NAT那么ipnet_eth_input照样工作只是包过 NAT 时直接被转发。我一般会先把组件树完整构建出来再按功能裁剪不要一开始就追求最小集。2.2 IPTCP 与 ipnet_eth_input 在收包路径中的分工IPTCP这个名称容易让人误以为是一个“TCP 加速组件”其实它在 IPnet 里代表的是 TCP/IP 协议栈的主体具体说就是 IP、ICMP、TCP 和 UDP 协议的实现。而ipnet_eth_input是网络接口层Ethernet Interface Layer的入口函数它负责把网卡驱动收上来的原始帧解析成 netBuf然后交给 IP 层。两者的关系可以简单归纳为eth_input 负责“接包”IPTCP 负责“拆包与状态机”。在 IPnet 代码里收包路径大致是这样走的netDevRxRoutine() // 驱动层收包触发网络任务 - ipnet_eth_input() // 解析以太网帧头校验长度与类型 - ipnet_ip_input() // IP 层入口判断是否需要转发或本机接收 - ipnet_tcp_input() / ipnet_udp_input() // 协议层处理ipnet_eth_input这个名字在源码里对应多个变体比如ipnet_eth_input_v6、ipnet_eth_input_v4。它做的事情首先是判断帧的 EtherType是 IPv4 就调 IPv4 处理是 ARP 就单独走 ARP 流程。它本身不碰 NATNAT 是在ipnet_ip_input()之后、查路由之前触发的。2.3 用 windRiver 命令行检查组件是否加载目标板起来后不要在 shell 里瞎猜组件有没有加载。IPnet 自带了几个顶层命令可以直接展示协议栈状态。手工敲ifconfig只能看到接口看不到组件。我一般先敲ipnetShellShow或muxShow- muxShow这个命令会列出所有绑定到 MUX 层的协议栈实例如果看到ipnet条目说明 IPnet 核心已经注册。再看 NAT 是否使能可以用- ipnat show如果返回IPnet NAT: not configured说明 NAT 组件虽然编进来了但还没有任何规则或者配置文件没有初始化。这时需要按下一章的方式下发规则。3. 配置 ipnet nat rule从临时规则到持久化脚本3.1 NAT rule 语法与参数表IPnet 的 NAT 规则不是直接在源码里硬编码而是通过ipnat命令或者调用ipnet_nat_rule_add()接口动态写入的。命令行的常见语法如下ipnat -a -s src -d dst -r redirect [ -p proto ] [ -l port ] [ -o out_if ]其中各参数含义如下参数含义示例-a添加规则add-a-d删除规则delete-d-s源地址范围-s 192.168.1.0/24-d目的地址范围注意与删除的 d 重复实际用位置区分-d 203.0.113.5-r重定向到的地址-r 10.0.0.2-p协议类型tcp/udp/icmp-p tcp-l可选的本地端口范围-l 80-o出接口名称-o gei0这里有个容易混淆的点-d在添加规则里是 destination在删除规则里是 delete实际使用时通常配合-a或-D区分。有些版本的 IPnet 中删除是-D所以不要死记拿到目标板上先输ipnat -h看帮助。3.2 在目标机上通过命令行下发 NAT 规则假设设备有两个接口内网gei0地址是192.168.1.1/24外网inf0地址是203.0.113.1/24。现在想让内网192.168.1.0/24的流量访问外网203.0.113.5的 Web 服务时源地址被转换成外网接口地址。可以在 VxWorks shell 里执行- ipnat -a -s 192.168.1.0/24 -d 203.0.113.5 -p tcp -l 80 -r 203.0.113.1 -o inf0命令的含义是来自192.168.1.0/24、目的为203.0.113.5且端口为 TCP 80 的包经过inf0接口出去时源地址改写为203.0.113.1。这里-r指定的就是转换后的源地址注意它不是目的重定向而是“rewrite”的缩写有些文档把它解释为 redirect但实际作用是地址转换后的源地址。规则加完后用下面的命令确认- ipnat show如果看到类似NAT rule: src 192.168.1.0/24 - 203.0.113.1 proto tcp dport 80的输出现象说明规则已经进入内核链。此时可以抓一下inf0上的包确认源地址是否真的变了。如果规则没有生效先检查ipnat show是否打印了Failed状态以及接口名是否写错。IPnet 的接口名不是 Linux 的 eth0 那种而是由 BSP 定义ifconfig -a先看一遍。3.3 写进配置文件VxWorks 启动脚本中的 NAT 持久化命令行下发的规则重启后就没了量产环境必须把 NAT 规则固化到启动脚本里。VxWorks 的启动脚本通常是tffs0:/startup或$VXWORKS_BOOT指定的文件。我一般的做法是在脚本里调用一个 shell 函数或者直接每行一条ipnat -a。例如startup.txt内容# 启动时配置 NAT ipnat -a -s 192.168.1.0/24 -d 203.0.113.5 -p tcp -l 80 -r 203.0.113.1 -o inf0 ipnat -a -s 192.168.1.0/24 -d 203.0.113.5 -p udp -l 53 -r 203.0.113.1 -o inf0注意第一行是#注释VxWorks 启动脚本支持这种格式。脚本里每条命令后可以加 echo NAT rule 1 ok来确认执行成功但要注意 shell 环境下并不总是可用有的目标板只认分号。更稳妥的方式是写一个小函数ipnat_add() { if ipnat -a $ ; then echo nat rule ok: $* else echo nat rule failed: $* exit 1 fi } ipnat_add -s 192.168.1.0/24 -d 203.0.113.5 -p tcp -l 80 -r 203.0.113.1 -o inf0写配置时要注意一个问题IPnet 的 NAT 规则在接口初始化之前添加会报“interface not found”。所以启动脚本里必须先把接口ipup且ifconfig配置完再去加 NAT 规则。如果遇到ipnat: cant find interface的错误十有八九是顺序问题。4. 跟踪 ipnet_eth_input收包后 NAT 在哪个钩子生效4.1 ipnet_eth_input 的调用链要理解 NAT 规则为什么有的包会命中、有的不会必须看清楚ipnet_eth_input之后发生了什么。以一个来自内网、目标为外网地址的 TCP 包为例调用链如下ipnet_eth_input() - ipnet_eth_vlan_process() // 处理 VLAN 标签无则跳过 - ipnet_eth_ipv4_process() // 判断 IP 协议版本 - ipnet_ip_input() // 进入 IP 层 - ipnet_ip_route_lookup() // 查找路由决定转发或本机 - ipnet_nat_process() // NAT 规则检查和改写 - ipnet_ip_output() // 从外网接口发出从这段链可以看出NAT 不在eth_input里而是在 IP 层的路由查询之后。因此如果你的 NAT 规则没有生效第一步不是怀疑eth_input丢了包而是确认ipnet_ip_input()有没有被正确调用。比如有些驱动在MUX层就把包吃掉或者上层协议栈没有绑定到接口都会导致eth_input后面的路径走不到。4.2 挂接回调函数观察 NAT 命中与否IPnet 支持在特定协议层挂接 hook常见的是ipnet_ip_input和ipnet_nat_process。不过这些内部函数并不一定导出了符号很多 BSP 会去掉调试信息。我推荐的可靠方案是在ipnet_eth_input的调用入口临时插桩或者利用 MUX 层的netHookAdd观察原始包。用netHookAdd挂一个回调代码示例如下#include net/mux.h void nat_trace(int event, struct net_buf *pBuf, int ifindex) { struct ip_hdr *iph; if (pBuf NULL) { return; } /* 假定 netBuf 的 data 指针已指向 IP 头具体偏移看驱动怎么填 */ iph (struct ip_hdr *)pBuf-data; if (iph-ip_proto IPPROTO_TCP) { printf(hook: src%08x dst%08x len%d\n, iph-ip_src, iph-ip_dst, ntohs(iph-ip_len)); } } /* 在任务中注册 */ STATUS ret netHookAdd(NET_MUX_KEEP, nat_trace, 0);注册后每个从 MUX 来的包都会进nat_trace打印源目 IP。参数NET_MUX_KEEP表示包在回调后保留不会影响正常路径。打印里能看到转换前的源地址。如果源地址始终不是内网地址说明包在进入 MUX 前就已经被别的组件处理了问题在驱动侧如果源地址是内网地址但目的 IP 已经是外网那么 NAT 没有拿到原包需要检查规则本身。比起在eth_input里改打印netHookAdd的好处是不用重新编译整个协议栈可以直接在 shell 里通过 debug stub 调用。缺点是回调里不能做耗时操作也不能调用可能引起调度的printfVxWorks 的 printf 在某些模式下会锁。现场调试时我一般只打印状态码真正分析用抓包工具。4.3 常见问题规则不生效时的排查清单根据我经手的几个 IPnet 项目NAT 规则不生效的根因通常集中在以下四点。第一接口名或地址掩码不匹配。ipnat show里的规则要逐字段对特别是网段掩码IPnet 内部使用 32 位掩码命令行里/24写错了会变成0.0.0.0。第二路由缺失。NAT 只在查路由成功后执行如果到目的地址没有路由包在ipnet_ip_route_lookup()就被丢了。用route show检查路由表。第三NAT 规则顺序。IPnet 的规则是顺序匹配最先命中的规则生效不会像 Linux 那样跳到后面的规则。如果前面有一条宽松规则后面的精确规则就一直不命中。用ipnat show -v查看匹配计数没增长的就是被杀规则挡住了。第四回包路由问题。NAT 不仅改源地址还要在返回路径上改目的地址如果外网接口没配置“反 NAT”规则连接会建不起来。IPnet 通常自动维护反向映射但前提是规则里指定了-r转换地址而不是-o外网接口。如果反向映射失效看到的现象是客户端能发送 SYN 但收不到 SYN-ACK。5. 进阶验证用回环接口测试 NAT 规则与 eth_input 回调在目标板上直接打流不方便时可以用回环接口做端到端验证。先给回环接口一个地址比如127.0.0.1然后把 NAT 规则指向它再用ping或写一个小 socket 程序触发收包。注意 IPnet 的 NAT 默认不处理本地发出的包因为ipnet_eth_input只接收从线路进来的帧本地回环包走的是另一条路径。但回环接口可以验证eth_input组件本身是否正常挂载。一条实用技巧在 shell 里手动调用ipnet_eth_input的调试入口来模拟一帧数据不经过外部网卡。前提是连接导入ipnet_eth_input的符号表。假设符号仍然可见可以这样做char frame[] {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x11,0x22,0x33,0x44,0x55, 0x08,0x00, ...}; ipnet_eth_input(frame, sizeof(frame), IFINDEX_LOOPBACK);用回环接口测试的好处是不会真的把包发出去只验证协议栈的收包处理路径。如果ipnet_eth_input返回OK说明从以太网解析到 IP 层的链路没有断如果返回ERROR就要顺着返回值查通常是帧长度不够或 EtherType 不支持。另一个验证技巧是把 NAT 规则的源地址设成回环网段然后用ipnat show的匹配计数变化来判断有没有命中- ipnat -a -s 127.0.0.1/32 -d 127.0.0.1 -p icmp -r 127.0.0.1 -o lo0 - ping 127.0.0.1 - ipnat show -v如果规则命中ipnat show -v里这条规则的hits会增加。实际项目中这个方法常用来快速判断 NAT 模块是否存在底层 bug而不必先排除驱动和物理链路干扰。不要把回环测试的结果当成真实链路的性能它的目的是验证数据包能否走通“eth_input → IP 层 → NAT”这条软件路径真实数据还得接双口测试一边灌流量一边在另一侧抓包比对源地址。本文还有配套的精品资源点击获取
返回列表