IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> TCP/IP详解之NAT -> 正文阅读

[网络协议]TCP/IP详解之NAT

一、原理
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();//void lwip_init(void)

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");//net dev是pp,1表示add net dev时的序号,如果不加1,find会失败
    in_if_eth0 = netif_find("e00");//net dev是e0,第二个0表示add net dev时的序号,如果不加0,find会失败
    nat_entry.out_if = out_if_ppp;
    nat_entry.in_if = in_if_eth0;

    IP4_ADDR(&nat_entry.source_net, 198, 120, 0, 210);//local ip
    IP4_ADDR(&nat_entry.source_netmask, 255, 255, 255, 0);
    IP4_ADDR(&nat_entry.dest_net, 123, 123, 123, 0);//dest ip
    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()函数中,网络层中处理报文;

	/* send to upper layers */
    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 /* IP_NAT */
    #if LWIP_RAW
    /* raw input did not eat the packet? */
    raw_status = raw_input(p, inp);

5、ip_nat_out()
ip_nat_out()放在ip4_input()函数中,网络层中处理报文;

/* packet not for us? */
    if (netif == NULL)
    {
        /* packet not for us, route or discard */
        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 /* IP_NAT */
        #if IP_FORWARD

        /* non-broadcast packet? */
        if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), inp))
        {
            /* try to forward IP packet on (other) interfaces */
            ip4_forward(p, (struct ip_hdr *)p->payload, inp);
        }
        else
        #endif /* IP_FORWARD */
        {
            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的注释原文:

/**
     * the reference count always equals the number of pointers
     * that refer to this pbuf. This can be pointers from an application,
     * the stack itself, or pbuf->next pointers from a chain.
     */
    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)
    {
        /* packet consumed, send it out on in_if */
        struct netif *in_if;

        /* check if the pbuf has room for link headers */
        if (pbuf_header(p, PBUF_LINK_HLEN))
        {
            /* pbuf has no room for link headers, allocate an extra pbuf */
            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"));
                /* @todo: stats? */
                // pbuf_free(p);
                // p = NULL;
                return 1;
            }
            else
            {
                pbuf_chain(q, p);
                is_chain = 1;
            }
        }
        else
        {
            /* restore p->payload to IP header */
            if (pbuf_header(p, -PBUF_LINK_HLEN))
            {
                LWIP_DEBUGF(LWIP_NAT_DEBUG, ("ip_nat_input: restoring header failed\n"));
                /* @todo: stats? */
                // pbuf_free(p);
                // p = NULL;
                return 1;
            }
            else q = p;
        }

        /* if we come here, q is the pbuf to send (either points to p or to a chain) */
        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));
        }

        /* now that q (and/or p) is sent (or not), give up the reference to it this frees the input pbuf (p) as we have consumed it. */
        if (1 == is_chain)
        {
            pbuf_dechain(q);
            pbuf_free(q);
        }
    }

三、延伸
1、网上看到这样一个问题:NAT和路由,谁先谁后?
如果流量从inside端口进来,那么先执行路由,后执行NAT(本地到全局);
如果流量从outside端口进来,那么先执行NAT(全局到本地),后执行路由。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-01-04 13:46:39  更:2022-01-04 13:47:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/8 11:37:48-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码