一、原理 NAT的工作原理就是重写重写通过路由器的数据包的识别信息。这种情况常发生在数据传输的两个方向上。在这种最基本的形式中,NAT需要重写往一个方向传输的数据包的源IP地址,重写往另一个方向传输的数据包的目的IP地址。这允许传出的数据包的源IP地址变为NAT路由器面向Internet的网络接口地址,而不是原始主机的接口地址。因此,在互联网上的主机看来,数据包是来自于具备全局路由IP的NAT路由器,而不是位于NAT内部的私有地址的主机。
二、移植 我的实际项目中,是ppp和eth之间数据包的转发,具体参考了rtthread组件中的NAT源码,但是并没有具体的详细使用说明,下面记录我的详细移植过程,其实还可以参考一下esp官方的SDK,里面实现了NAPT,完整的嵌入到LWIP协议栈中。 1、rtconfig.h中添加宏定义
#define LWIP_USING_NAT
2、nat初始化 ip_nat_init()放在lwip_init()最后执行;
ip_nat_init();
3、添加参数配置 ip_nat_start()我是放在ppp_netdev_refresh()最后,ppp拨号成功后配置;
void ip_nat_start(void)
{
ip_nat_entry_t nat_entry;
err_t ret = 0;
struct netif *out_if_ppp;
struct netif *in_if_eth0;
out_if_ppp = netif_find("pp1");
in_if_eth0 = netif_find("e00");
nat_entry.out_if = out_if_ppp;
nat_entry.in_if = in_if_eth0;
IP4_ADDR(&nat_entry.source_net, 198, 120, 0, 210);
IP4_ADDR(&nat_entry.source_netmask, 255, 255, 255, 0);
IP4_ADDR(&nat_entry.dest_net, 123, 123, 123, 0);
IP4_ADDR(&nat_entry.dest_netmask, 255, 255, 255, 0);
ret = ip_nat_add(&nat_entry);
if (ret != ERR_OK)
{
rt_kprintf("%s ip_nat_add failed ret:%d\r\n", __FUNCTION__, ret);
}
}
4、ip_nat_input() ip_nat_input()放在ip4_input()函数中,网络层中处理报文;
LWIP_DEBUGF(IP_DEBUG, ("ip4_input: \n"));
ip4_debug_print(p);
LWIP_DEBUGF(IP_DEBUG, ("ip4_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len));
ip_data.current_netif = netif;
ip_data.current_input_netif = inp;
ip_data.current_ip4_header = iphdr;
ip_data.current_ip_header_tot_len = IPH_HL_BYTES(iphdr);
#if IP_NAT
if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), inp))
ip_nat_input(p);
#endif
#if LWIP_RAW
raw_status = raw_input(p, inp);
5、ip_nat_out() ip_nat_out()放在ip4_input()函数中,网络层中处理报文;
if (netif == NULL)
{
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: packet not for us.\n"));
#if IP_NAT
if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), inp))
ip_nat_out(p);
#endif
#if IP_FORWARD
if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), inp))
{
ip4_forward(p, (struct ip_hdr *)p->payload, inp);
}
else
#endif
{
IP_STATS_INC(ip.drop);
MIB2_STATS_INC(mib2.ipinaddrerrors);
MIB2_STATS_INC(mib2.ipindiscards);
}
pbuf_free(p);
return ERR_OK;
}
6、pbuf_free: p->ref > 0 移植后运行,pbuf_free一直报pbuf_free: p->ref > 0的错误,就是在pbuf_free的时候还有指针指向本pbuf,下面是ref的注释原文:
LWIP_PBUF_REF_T ref;
通过追踪,我发现在pbuf_chain(q, p)后再pbuf_free(q),就会出现此问题,我是通过pbuf_dechain(q)解开q和p,再pbuf_free(q),而且我把pbuf_free( p )都注释掉了,因为ip4_input()函数中会执行pbuf_free( p ),不想改变LWIP中的ip4_input(),只负责NAT部分。我不能保证一定就是正确的,如果有误,还请指出。目前的测试结果看,没有内存泄漏的问题。
if (consumed)
{
struct netif *in_if;
if (pbuf_header(p, PBUF_LINK_HLEN))
{
q = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
if (q == NULL)
{
LWIP_DEBUGF(LWIP_NAT_DEBUG, ("ip_nat_input: no pbuf for outgoing header\n"));
return 1;
}
else
{
pbuf_chain(q, p);
is_chain = 1;
}
}
else
{
if (pbuf_header(p, -PBUF_LINK_HLEN))
{
LWIP_DEBUGF(LWIP_NAT_DEBUG, ("ip_nat_input: restoring header failed\n"));
return 1;
}
else q = p;
}
in_if = nat_entry.cmn->cfg->entry.in_if;
iphdr->dest.addr = nat_entry.cmn->source.addr;
ip_nat_chksum_adjust((u8_t *) & IPH_CHKSUM(iphdr), (u8_t *) & (nat_entry.cmn->cfg->entry.out_if->ip_addr.addr), 4, (u8_t *) & (iphdr->dest.addr), 4);
ip_nat_dbg_dump("ip_nat_input: packet back to source after nat: ", iphdr);
LWIP_DEBUGF(LWIP_NAT_DEBUG, ("ip_nat_input: sending packet on interface ("));
ip_nat_dbg_dump_ip(&(in_if->ip_addr));
LWIP_DEBUGF(LWIP_NAT_DEBUG, (")\n"));
err = in_if->output(in_if, q, (ip_addr_t *) & (iphdr->dest));
if (err != ERR_OK)
{
LWIP_DEBUGF(LWIP_NAT_DEBUG, ("ip_nat_input: failed to send rewritten packet. link layer returned %d\n", err));
}
if (1 == is_chain)
{
pbuf_dechain(q);
pbuf_free(q);
}
}
三、延伸 1、网上看到这样一个问题:NAT和路由,谁先谁后? 如果流量从inside端口进来,那么先执行路由,后执行NAT(本地到全局); 如果流量从outside端口进来,那么先执行NAT(全局到本地),后执行路由。
|