ITPub博客

首页 > IT基础架构 > 网络通信/物联网 > IPv6的NAT原理

IPv6的NAT原理

网络通信/物联网 作者:上海地面通 时间:2019-10-11 13:37:19 0 删除 编辑

在亿万互联网用户享受着Internet 带来便利的同时, IPv4 地址即将耗尽的问题却早在 20 年前就被网络专家们意识到了,并采取了措施延缓 IPv4 的消耗,这项措施就是 NAT (网络地址转换)技术。 NAT 主要作用在于 节约IP 地址,而非所谓的增加 IP 的方向性以及隐藏私有 IP , 且 IPv4 的 NAT 打破了互联网本身的“互联”特性,使得一部分 IP 地址不再双向可达, NAT 为无方向的 IP 协议增加了一个方向,特别是 stateful 的 NAT 类型。 此时,IPv6 时代已逐渐到来。

而 IPv6 的标准中不建议使用 NAT , 这是何缘由呢?我们已知 IPv6 地址数量巨大 ,只要 在地球上 ,都可 为 蚂蚁 配置 IP 设备 , 因此 IPv4 的一些修补手段将不再需要,为了保持协议本身以及相关标准的纯洁性, 在 IPv6 中 几乎不再 建议使用 NAT 。虽然不再 建议使用 , 但在某些必要情况下 ,还是可能 需要 实现IPv6 的 NAT 。 在 RFC6296 的标题是 IPv6-to-IPv6 Network Prefix Translation 中 ,描述了IPv6 下的 NAT 的实现要点,给出了一个合理的建议,既保持了 IP 的无方向性,又可以满足 NAT 的语义,这就是 IPv6 之 NAT stateless 的缘由。

IPv6 地址有将近 128 个可随意调配的位,其庞大的地址空间,一般的单位都会被分配到一个拥有很大量地址的网段,此网段拥有足够多的地址来和内网主机进行映射,即可用于映射的 IP 地址池容量巨大,且既然不想再使用非 IP 层的信息来保持信息,又要保持映射,那就要用纯 IP 层的信息,这样对上层影响最小。对于 IPv6 而言, NAT 利用 checksum 算法来保持流标识信息,丝毫不管这个 checksum 是谁的 checksum ,因为它根本就不改变数据包的 checksum...

接下来给大家讲解一下 checksum 无关性和自动转换 , 就是 考虑a+b+c+d=X 。 其中X 就是 checksum ,我们把 a , b 当成源 IP 地址的两部分, c , d 当成目的 IP 地址的两部分,我们作源地址转换,将 a 和 b 都改变,比如 a 改变成了 A ,那么 将b 改成多少才能保持 checksum 的值 X 不变,求解 即可 。IPv6 的建议 NAT 实现 亦 是 上文 这个原理, 只是将其换为 计算机布尔数域 求解 。既然可以不触动第四层的checksum 值,那么 NAT 对第四层协议的影响也就减小了,虽然它还是解决不了诸如 ESP/AH 等穿越 NAT 的问题。基于以上算法, IPv6 在做 NAT 的时候,在给定的子网网段内,可以自动生成一个新的 IP 地址供映射之用,从算法本身来看,冲突的可能性非常之小致于 0 。

既然IPv6 的 NAT 机制 “ 自动 ” 为一个连接选择了一个 IP 地址,那么当返回包到来的时候,如何把地址转换 为原地址 呢?IPv6 的 NAT 把地址 “ 转换回去 ” 这件事完全靠算法本身, 而 算法本身 就 能将转换后的地址再转回原来的, 具有 解的唯一性,在IPv6 的 NAT 实现中,算法只针对 IP 地址中 16 位的地址信息进行自动生成,而其它的则需要手工显式配置,由于内网 IPv6 地址可以使用 MAC 地址映射成唯一的地址, 且 转换后的地址是唯一的,将这一切反过来,最后还是能映射回原始的IP 地址的。

如果抛开地址转换这一说,仅仅考虑算法本身,那还是可以给出一个实际可以运行的代码的,该代码使用了计算checksum 的算法:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

 

// 以下 2 个函数就是计算校验码的,具体的原理请参 RFC1071/RFC1624/RFC1141 。

static inline u_int16_t add16(

    u_int16_t a,

    u_int16_t b)

{

    a += b;

    return a + (a < b);

}

 

static inline u_int16_t csum16(const u_int16_t *buf, int len)

{

    u_int16_t csum = 0;

    while(len--) csum = add16(csum, *buf++);

    return csum;

}

 

 

int main(int argc, char **argv)

{

 

    u_int16_t buf[18] = {0};

    int i = 0;

    memcpy(buf, "efghhijk", 8);

    memcpy(buf+4, "12345678", 8);

    memcpy(buf+8, "xxyywert", 8);

    memcpy(buf+12, "zxcvkljh", 8);

    // 正确做法是打印 16 进制数据,此处为了简单,打印了字符串

    printf(" 原始数据: %s   长度: %d\n", (char*)buf, strlen((char*)buf));    

    printf(" 原始数据的校验码: %X\n", csum16(buf, 16));    

    

    u_int16_t tip[3] = {0};

    memcpy(tip, "#$!%", 4);

    u_int16_t tip_sum = csum16(tip, 2);

    printf("\nNAT 规则: efghhijk1234/12 - 〉 efghhijk#$!%/12\n\n");

    printf(" 固定从第 9 个字节开始修改 4 个字节为: %s 其校验码为: %X\n", (char*)tip, tip_sum);

    

    // 定位固定修改后的动态修改的初始地址,注意,我们仅仅修改 16 位信息

    u_int16_t* pcsum = buf + 4+2;  

    

    // 计算动态修改的值

    *pcsum = ~add16(

        add16(

            ~(*pcsum),

            ~csum16(buf+4, 2)

        ),

        tip_sum

    );

    printf(" 动态修改的值为: %X\n", *pcsum);

    memcpy(buf+4, tip, 4); // 完成修改

    

    printf(" 当前数据: %s   长度: %d\n", buf, strlen((char*)buf));    

    printf(" 当前校验码: %X\n", csum16(buf, 16));    

    printf("------------- 以下是还原操作 -------------\n");

    printf("\n 反向 NAT 规则: efghhijk#$!%/12 - 〉 efghhijk1234/12\n\n");

 

    u_int16_t tip2[3] = {0};

        memcpy(tip2, "1234", 4);

    printf(" 我们只需要记住原始数据被固定修改前的: %s\n", tip2);

 

    u_int16_t tip_sum2 = csum16(tip2, 2);

    

    u_int16_t* pcsum2 = buf+6;

 

    *pcsum2 = ~add16(

        add16(

            ~(*pcsum2),

            ~csum16(buf+4, 2)

        ),

        tip_sum2

    );

    // 还原

    memcpy(buf+4, tip2, 4);

    

    printf(" 原始数据: %s\n", (char *)buf);

    printf(" 原始校验码: %X\n", csum16(buf, 16));    

    

}

 

运行结果如下:

把以上的原理套用在IPv6 的 NAT 上,就是一种实现。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69943410/viewspace-2659495/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2019-08-02

  • 博文量
    33
  • 访问量
    40170