RT-Thread论坛
直播中

djelje

9年用户 1092经验值
擅长:光电显示
私信 关注
[问答]

如何使用ipv4_nat模块实现SNAT转发?

用的是rt-thread的3.1.0版本,c-sky 803S平台,有两个网口,NAT前两个网口的配置分别如下

  • char * argument_list0[] = {"ifconfig","e0","172.16.100.231","172.16.100.1","255.255.255.0", 0 };
  • cmd_ifconfig(5,argument_list0);

  • char * argument_list1[] = {"ifconfig","e1","192.168.100.10","192.168.100.1","255.255.255.0", 0};
  • cmd_ifconfig(5,argument_list1);


问题:在自己的PC机器上设置了一个路由

  • route ADD -p 172.16.100.59  mask 255.255.255.255 172.16.1
  • 00.231


转发给了板子,在板子上打印底层日志可以看到数据包
在RTOS内核中的MAIN方法上增加NAT策略

  • ip_nat_entry_t new_nat_entry;
  • err_t ret=0;
  • struct netif *e0_in_if;
  • struct netif *e1_out_if;
  • e0_in_if=netif_find("e0");
  • e1_out_if=netif_find("e1");
  • new_nat_entry.out_if = (struct netif *)&e1_out_if;
  • new_nat_entry.in_if = (struct netif *)&e0_in_if;
  • IP4_ADDR(&new_nat_entry.source_net, 172,16,100,0);
  • IP4_ADDR(&new_nat_entry.source_netmask, 255, 255, 255, 0);
  • IP4_ADDR(&new_nat_entry.dest_net, 192, 0, 0, 0);
  • IP4_ADDR(&new_nat_entry.dest_netmask, 255, 0, 0, 0);
  • ret=ip_nat_add(&new_nat_entry);


目的是想让板子上接收到的访问172.16.100.59的数据包转发到192段,
我现在在lwip_init中增加了ip_nat_init函数的调用,
在tcpip.c中tcpip_inpkt函数增加了ip_nat_input(p);函数的调用,如下

  • err_t
  • __fast tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
  • {
  • #if LWIP_TCPIP_CORE_LOCKING_INPUT
  •   err_t ret;
  •   LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_inpkt: PACKET %p/%pn", (void *)p, (void *)inp));
  •   LOCK_TCPIP_CORE();
  •   ret = input_fn(p, inp);
  •   UNLOCK_TCPIP_CORE();

  • #if IP_NAT
  •   ip_nat_input(p);
  • #endif /* LWIP_NAT */


。。。
请问这样处理是否正确,是否可以通过板子的192.16.100.10获取到数据包?


回帖(1)

二霸

2025-10-11 16:14:36

我们使用RT-Thread的IPv4 NAT模块来实现SNAT转发。根据您提供的信息,有两个网口:e0配置为172.16.100.231(内网),e1配置为192.168.100.10(外网)。现在需要在RT-Thread设备上配置NAT,使得内网(172.16.100.0/24)的机器可以通过该设备访问外网(192.168.100.0/24)以及更外部的网络。

步骤:
1. 确保RT-Thread系统已开启NAT功能(在menuconfig中配置)。
2. 初始化网络接口,并设置默认路由。
3. 设置NAT规则,将内网地址转换为外网接口地址(SNAT)。

根据您提供的两个网口的配置命令,我们已经在e0和e1上配置了IP地址。接下来需要:
- 设置默认路由,指向外网的网关(例如,e1的网关192.168.100.1)。
- 启用e1作为NAT出口(WAN口),e0作为内网口(LAN口)。
- 添加SNAT规则,将来自内网(e0)的包的源IP转换为外网接口e1的IP(192.168.100.10)。

注意:在RT-Thread中,使用NAT模块需要先使能,然后通过命令或API进行配置。

具体操作:

1. 在rtconfig.h或通过menuconfig开启NAT功能:
    #define RT_LWIP_NAT 1

2. 在代码中,初始化网络接口后,设置路由和NAT:

    - 设置默认路由(假设外网网关为192.168.100.1):
        route_add("0.0.0.0", "0.0.0.0", "192.168.100.1", "e1");

    - 启用e1接口作为NAT出口(WAN口):
        finsh_cmd_exec("nat set network e1", sizeof("nat set network e1"));

    - 添加SNAT规则(将e0子网的源IP转换为e1的IP):
        finsh_cmd_exec("nat add snat 172.16.100.0 255.255.255.0 192.168.100.10", sizeof("nat add snat 172.16.100.0 255.255.255.0 192.168.100.10"));

3. 另外,您还需要在PC上设置路由,将到172.16.100.59(RT-Thread内网中的另一台设备)的下一跳指向RT-Thread的内网口IP(172.16.100.231)。您已经做了这一步:
        route ADD -p 172.16.100.59 MASK 255.255.255.255 172.16.100.231

    这样,当PC访问172.16.100.59时,会先发送到172.16.100.231(RT-Thread的e0口),然后RT-Thread再根据路由表转发到内网设备(172.16.100.59)。

但是,请注意:这里您设置的是到172.16.100.59这个特定IP的路由。如果您希望整个内网段(172.16.100.0/24)都通过RT-Thread访问,应该设置整个网段的路由:
        route ADD -p 172.16.100.0 MASK 255.255.255.0 172.16.100.231

4. 另外,确保RT-Thread设备本身可以访问外网(即RT-Thread的路由表正确,并且有到外网的路由)。我们上面已经设置了默认路由。

5. 在内网设备上设置默认网关为RT-Thread的内网口IP(172.16.100.231),这样内网设备发出的包才会先到RT-Thread。

6. 在RT-Thread中,还需要开启IP转发功能(在menuconfig中开启:RT_LWIP_IP_FORWARD)。

7. 使用命令的方式配置NAT(在finsh中执行):
        nat set network e1          # 设置NAT的出口网络接口为e1
        nat add snat 172.16.100.0 255.255.255.0 192.168.100.10   # 添加SNAT规则

或者通过代码调用:
        int nat_netif_set(struct rt_netif *netif);
        int nat_snat_add(rt_uint32_t ip, rt_uint32_t mask, rt_uint32_t gw);

注意:在代码中调用时,需要先获取网络接口设备(e1和e0的netif结构)。

代码示例(在初始化网络后调用):
        #include
        #include

        void nat_init(void)
        {
            struct rt_netif *netif;

            // 设置默认路由(可选,如果已经通过其他方式设置)
            // 这里使用route命令的方式,或者使用API:rt_netif_set_default_gateway()

            // 设置NAT的出口接口(e1)
            netif = netif_find("e1");
            if (netif)
            {
                nat_netif_set(netif);
            }

            // 添加SNAT规则(转换内网172.16.100.0/24的源地址为192.168.100.10)
            nat_snat_add(inet_addr("172.16.100.0"), inet_addr("255.255.255.0"), inet_addr("192.168.100.10"));
        }

8. 如果内网设备要访问外网,除了SNAT规则外,还需要允许数据包转发。在RT-Thread中,需要确保防火墙没有阻止(默认应该允许),并且IP转发已开启。

9. 验证:
    - 在RT-Thread设备上ping外网地址(如192.168.100.1)应该通。
    - 在内网设备上ping外网地址(如192.168.100.1)应该通,且显示的是从192.168.100.10回来的。
    - 在PC上(已经设置了路由)访问172.16.100.59应该可以到达。

注意:如果外网接口(e1)的IP是动态获取的(DHCP),那么SNAT规则中的转换地址应该使用接口地址(即使用0.0.0.0表示使用接口地址),但RT-Thread的NAT模块是否支持这种方式需要看具体实现。当前版本(3.1.0)可能不支持动态SNAT(即使用接口地址作为转换后地址),所以需要指定IP。如果使用接口地址,可以尝试:
        nat add snat 172.16.100.0 255.255.255.0 0.0.0.0

但是,根据RT-Thread的NAT文档,SNAT规则中的转换地址可以设置为0.0.0.0,表示使用NAT出口接口的地址。所以,推荐使用:
        nat add snat 172.16.100.0 255.255.255.0 0.0.0.0

这样,无论e1的IP是什么(静态或动态),都会使用e1当前的IP进行转换。

因此,修改为:
        nat_snat_add(inet_addr("172.16.100.0"), inet_addr("255.255.255.0"), inet_addr("0.0.0.0"));

10. 在代码中执行命令的方式:

        char cmd[128];
        snprintf(cmd, sizeof(cmd), "nat set network e1");
        finsh_cmd_exec(cmd, strlen(cmd));

        snprintf(cmd, sizeof(cmd), "nat add snat 172.16.100.0 255.255.255.0 0.0.0.0");
        finsh_cmd_exec(cmd, strlen(cmd));

或者使用API(推荐,避免字符串解析错误):
        if (nat_netif_set(netif) == 0)
        {
            rt_kprintf("NAT network interface set to e1 success.n");
        }
        else
        {
            rt_kprintf("Failed to set NAT network interface.n");
        }

        if (nat_snat_add(inet_addr("172.16.100.0"), inet_addr("255.255.255.0"), inet_addr("0.0.0.0")) == 0)
        {
            rt_kprintf("SNAT rule added.n");
        }
        else
        {
            rt_kprintf("Failed to add SNAT rule.n");
        }

11. 最后,确保在初始化网络后,并且两个网口已经配置好并启动(up)后再进行NAT配置。

总结步骤:
    - 配置两个网口的IP(已经做了)。
    - 设置默认路由(到外网网关)。
    - 设置内网设备(包括PC)的网关为RT-Thread的内网口IP(172.16.100.231)。
    - 在RT-Thread上开启NAT,设置WAN口(e1),添加SNAT规则(内网段转换为WAN口IP)。
    - 开启IP转发。

按照以上步骤,应该可以实现SNAT转发。

要使用RT-Thread 3.1.0的ipv4_nat模块实现SNAT转发,需按以下步骤操作。以下是完整解决方案:


1. 配置网络接口


你已正确配置两个网口:


// 内网接口 e0 (LAN)
char *argument_list0[] = {"ifconfig", "e0", "172.16.100.231", "172.16.100.1", "255.255.255.0", 0};
cmd_ifconfig(5, argument_list0);

// 外网接口 e1 (WAN)
char *argument_list1[] = {"ifconfig", "e1", "192.168.100.10", "192.168.100.1", "255.255.255.0", 0};
cmd_ifconfig(5, argument_list1);

2. 启用IP转发


rtconfig.h中开启IP转发:


#define RT_LWIP_IP_FORWARD 1

3. 初始化NAT模块


在应用代码中初始化NAT:


#include 

void nat_init() {
    struct rt_netif *netif;

    // 查找外网接口e1
    netif = netif_find("e1");
    if (netif) {
        // 设置NAT出口为e1
        if (nat_netif_set(netif) == RT_EOK) {
            rt_kprintf("NAT set to e1 (WAN) successn");
        }
    }

    // 添加SNAT规则:将内网172.16.100.0/24的源IP转换为e1的IP
    if (nat_snat_add(inet_addr("172.16.100.0"),
                     inet_addr("255.255.255.0"),
                     inet_addr("192.168.100.10")) == RT_EOK) {
        rt_kprintf("SNAT rule added: 172.16.100.0/24 -> 192.168.100.10n");
    }
}

在系统启动时调用nat_init()(如创建Init线程)。


4. 设置路由




  • RT-Thread设备:添加默认路由指向外网网关:


    // 在代码中添加默认路由
    struct rt_netdev *dev = rt_netdev_get_by_name("e1");
    if (dev) {
      rt_netdev_set_gateway(dev, inet_addr("192.168.100.1"));
    }

    或通过Finsh命令:


    route add 0.0.0.0 0.0.0.0 192.168.100.1



  • 你的PC:添加静态路由访问内网:


    route ADD -p 172.16.100.59 MASK 255.255.255.255 172.16.100.231



5. 验证配置


在RT-Thread的Finsh中检查:


msh > nat status
NAT enabled on interface: e1
SNAT rules:
  172.16.100.0/24 -> 192.168.100.10

6. 测试连接



  • 在内网设备(如172.16.100.59)尝试访问外网(如ping 192.168.100.1)。

  • 在WAN侧设备抓包,确认源IP已转换为192.168.100.10


7. 常见问题处理




  • NAT不生效



    1. 检查RT_LWIP_NATRT_LWIP_IP_FORWARD是否启用。

    2. 确保接口状态为UPifconfig e0 up)。

    3. 验证路由表(route print)。




  • 动态IP支持
    若e1使用DHCP,修改SNAT规则为动态地址:


    nat_snat_add(inet_addr("172.16.100.0"), 
               inet_addr("255.255.255.0"),
               inet_addr("0.0.0.0")); // 0.0.0.0表示自动使用e1的IP




关键点:确保所有设备的路由指向正确,且NAT规则覆盖了需要转发的内网子网。通过上述步骤,内网设备即可通过SNAT访问外网资源。


举报

更多回帖

发帖
×
20
完善资料,
赚取积分