ICMPv6报文解析及NAT处理

ICMPv6报文概述

参考RFC4443和RFC2460
ICMPv6报文是IPv6在internal control management protocol(ICMP)的基础之上做了一些改动,得到了ICMPv6协议,IPv6的next_header为58。

Message general format

每条ICMPv6消息之前都有一个IPv6报头和零个或多个IPv6扩展报头。ICMPv6报头由前一报头的下一报头值58来标识。(这与IPv4中用来标识ICMP的值不同。)
ICMPv6的一般格式如下:
ICMP报头一般格式
type :表示消息的类型,type的值决定剩余数据的格式。
code : code的值取决于type的值,用于更细的划分ICMPv6的消息类型。
Checksum : 校验和用于检测ICMPv6消息和部分IPv6报头中的数据损坏。
Message body :分为两类,一类是错误消息,类型是0~128;一类是信息消息,类型范围是129-255。

几种常见的ICMPv6消息格式

错误消息格式:

typemean
1Destination Unreachable
2Packet too big
3Time Exceeded
4Parameter Problem
100Private experimentation
101Private experimentation
127Reserved for expansion of ICMPv6 error messages

信息消息:

typemean
128Echo Request
129Echo Reply
200Private experimentation
201Private experimentation
255Reserved for expansion of ICMPv6 informational messages

ICMPv6报文解析流程

确认消息的源地址

发送ICMPv6消息的节点必须在计算校验和之前确定IPv6报头中的源IPv6地址和目的IPv6地址。如果节点有多个单播地址,则MUST按照如下方式选择消息的源地址:

  • 如果消息是对发送到节点的单播地址之一的消息的响应,则应答的源地址MUST与该地址相同。
  • 如果该消息是对发送到任何其他地址的消息的响应,
    • 一个多播组地址,
    • 一个不属于节点的单播地址由节点实现的任意播地址,
    • 或者ICMPv6报文的源地址MUST为本节点的单播地址。

该地址的选择应根据该规则,该规则将用于为该节点发起的任何其他数据包选择源地址,并给定数据包的目的地址。但是,如果这将导致从ICMPv6数据包的目的地可到达的地址的更有信息的选择,则可以以另一种方式选择它。

校验和的计算

校验和是个位补码的16位补码从ICMPv6消息开始的整个ICMPv6消息的总和类型字段,并加上IPv6报头的“伪头”字段,如[IPv6,章节8.1]中指定的。下一个报头值伪头中使用的是58。
在校验和计算中包含IP报头地址的任何传输或其他上层协议必须修改以用于IPv6,包括128位IPv6地址而不是32位IPv4地址。特别是,下面的插图显示了IPv6的TCP和UDP“伪头”:
在这里插入图片描述

  • 如果IPv6报文中包含路由头,则表示目的地址伪头中使用的地址是final目的地。在起始节点,该地址将在路由头的最后一个元素;在接收人处,的目的地址字段中IPv6报头。
  • 伪报头中的下一个报头值标识上层协议(例如,TCP为6,UDP为17)。如果在IPv6报头和上层报头之间有扩展报头,它将不同于IPv6报头中的下一个报头值。
  • 伪报头中的上层数据包长度是上层报头和数据的长度(例如,TCP报头加上TCP数据)。有些上层协议携带自己的协议长度信息(例如,UDP报头中的长度字段);对于这样的协议,这就是伪报头中使用的长度。其他协议(如TCP)不携带自己的长度信息,在这种情况下,伪报头中使用的长度是来自IPv6报头的有效载荷长度,减去IPv6报头和上层报头之间存在的任何扩展报头的长度。
  • 与IPv4不同,当UDP数据包由IPv6节点发起时,UDP校验和不可选。也就是说,无论何时发起UDP数据包,IPv6节点必须计算数据包和伪报头的UDP校验和,如果计算结果为零,则必须将其更改为十六进制FFFF以放置在UDP报头中。IPv6接收者必须丢弃包含零校验和的UDP数据包,并记录错误。

IPv6版本的ICMP [ICMPv6]包含了上述伪头它的校验和计算;这与IPv4版本的ICMP不同,后者在校验和中不包含伪报头。改变的原因是为了保护ICMP免受误传或损坏它所依赖的IPv6报头字段,与IPv4不同,这些字段不受互联网层校验和的保护。ICMP伪报头中的下一报头字段包含值58,用于标识ICMP协议的IPv6版本。

一段处理ICMPv6目的不可达报文的代码示例

static int
handle_icmp6_err(struct mbuf *m, struct icmp6_hdr *ih6, struct ip6_hdr *ic_ip6)
{
	struct udphdr *ic_udp = NULL; /* initialize to make compiler happy */
	struct icmp6_hdr *ic_icmp6;
	clickpcb_t *pcb;
	clickpcb_udp_t *udp_pcb;
	clickpcb_udp_t *udp_opcb;
	clickpcb_icmp_t *icmp_pcb;
	clickpcb_icmp_t *icmp_opcb;

	clicktcp_enter_func(m, ih6);

	if (ic_ip6->ip6_nxt == IPPROTO_ICMPV6) {
		ic_icmp6 = (struct icmp6_hdr *)(ic_ip6 + 1);

		pcb = clickpcb6_lookup(&ic_ip6->ip6_dst,
		                       &ic_ip6->ip6_src,
		                       0,
		                       ic_icmp6->icmp6_id,
		                       ic_ip6->ip6_nxt);

	} else {
		ic_udp = (struct udphdr*)(ic_ip6 + 1);

		if (ATCP_IS_IP()) {
			IP2L4_dispatcher(m, ntohs(ic_udp->uh_dport), ntohs(ic_udp->uh_sport));
			return MBUFINUSE;
		}
		pcb = clickpcb6_lookup(&ic_ip6->ip6_dst,
		                       &ic_ip6->ip6_src,
		                       ntohs(ic_udp->uh_dport),
		                       ntohs(ic_udp->uh_sport),
		                       ic_ip6->ip6_nxt);
	}

	if (pcb == NULL)
		return QUEUEBSD;

	if (pcb->cp_type == PCB_UDP) {
		if ((pcb->cp_flags & CLICKPCB_UDP_SERVER) == 0)
			return FREEMBUF;
	
		udp_pcb = (clickpcb_udp_t *)pcb;
		if (udp_pcb->reverse == NULL)
			return FREEMBUF; /* bug 64435 */

		CHECK_PCB_EROUTE((clickpcb_t *)udp_pcb->reverse, udp_pcb->reverse->cp_eroute, EROUTE_LOOKUP_ONLY);

		if (udp_pcb->reverse->cp_eroute.rule == NULL)
			return FREEMBUF;

		/* Reset connection client timeout timer */
		udp_opcb = udp_pcb->reverse;

		click_callout_reset(&udp_opcb->timeout_callout,
		                    udp_opcb->timeout, clickudp_timeout, udp_opcb);

		udp_opcb = udp_pcb->reverse;

		clickicmp6_error_nat_patch(m, pcb, ih6, ic_ip6);
		clicktcp6_output(m, ERT_RTENTRY(udp_opcb->cp_eroute.rule), ERT_NUMA_IFP(udp_opcb->cp_eroute.rule), udp_opcb->cp_eroute.rule);
		return MBUFINUSE;

	} else if (pcb->cp_type == IPPROTO_ICMPV6) {
		if ((pcb->cp_flags & CLICKPCB_ICMP_SERVER) == 0)
			return FREEMBUF;

		icmp_pcb = (clickpcb_icmp_t *)pcb;
		if (icmp_pcb->reverse == NULL)
			return MBUFINUSE;

		CHECK_PCB_EROUTE((clickpcb_t *)icmp_pcb->reverse, icmp_pcb->reverse->cp_eroute, EROUTE_LOOKUP_ONLY);

		if (icmp_pcb->reverse->cp_eroute.rule == NULL)
			return FREEMBUF;

		/* Reset connection client timeout timer */
		icmp_opcb = icmp_pcb->reverse;

		click_callout_reset(&icmp_opcb->timeout_callout,
		                    icmp_opcb->timeout, clickicmp_timeout, icmp_opcb);

		icmp_opcb = icmp_pcb->reverse;
		clickicmp6_error_nat_patch(m, pcb, ih6, ic_ip6);    
		clicktcp6_output(m, ERT_RTENTRY(icmp_opcb->cp_eroute.rule), ERT_NUMA_IFP(icmp_opcb->cp_eroute.rule), icmp_opcb->cp_eroute.rule);
		return MBUFINUSE;

	} else {
		return FREEMBUF;
	}
}

static void
clickicmp6_error_nat_patch(struct mbuf *m, clickpcb_t *pcb, struct icmp6_hdr *ih6, struct ip6_hdr *ic_ip6)
{
	struct udphdr *ic_udp;
	struct icmp6_hdr  *ic_icmp6;
	struct ip6_hdr *ip6;
	clickpcb_udp_t *udp_pcb;
	clickpcb_udp_t *udp_opcb;
	clickpcb_icmp_t *icmp_pcb;
	clickpcb_icmp_t *icmp_opcb;
	uint16_t oldcksum;
	uint32_t ph_sum = 0;

	clicktcp_enter_func(m, pcb);

	/*Calculates the pseudo-header checksum*/
	struct in6_addr *src_ip6 = &(ic_ip6->ip6_src);
	struct in6_addr *dst_ip6 = &(ic_ip6->ip6_dst);

	for (int i = 0; i < 8; i++) {
	    ph_sum += htons(src_ip6->s6_addr16[i]);
	    ph_sum += htons(dst_ip6->s6_addr16[i]);
	}
	ph_sum += htons(ic_ip6->ip6_plen);
	ph_sum += htons(ic_ip6->ip6_nxt);

	if (pcb->cp_type == PCB_UDP) {
		udp_pcb = (clickpcb_udp_t *)pcb;

		ip6 = mtod(m, struct ip6_hdr *);

		ic_udp = (struct udphdr *)(ic_ip6 + 1);

		udp_opcb = udp_pcb->reverse;
		if (ih6->icmp6_type == ICMP6_DST_UNREACH && ih6->icmp6_code == ICMP6_DST_UNREACH_NOPORT) {
			ip6->ip6_src = udp_opcb->cp_localip6;
		}
		ip6->ip6_dst = udp_opcb->cp_remoteip6;
		ic_ip6->ip6_src = udp_opcb->cp_remoteip6;
		ic_ip6->ip6_dst = udp_opcb->cp_localip6;

		/*Accumulates 16 bits of UDP*/
		ph_sum += htons(udp_opcb->cp_localport);
	    ph_sum += htons(udp_opcb->cp_remoteport);
	    ph_sum += htons(ic_udp->uh_ulen);
	    ph_sum += *(uint16_t *)(ic_udp);
		/*Processing overflow*/
		while (ph_sum >> 16) {
		    ph_sum = (ph_sum & 0xFFFF) + (ph_sum >> 16);
		}

		/*Calculated checksum*/
        uint16_t old_cksum = ic_udp->uh_sum;
        uint32_t sum = ph_sum + ~(old_cksum & 0xFFFF);
        while (sum >> 16) {
            sum = (sum & 0xFFFF) + (sum >> 16);
        }
        uint16_t new_cksum = ~sum;

        /*Assign the checksum to the packet header*/
        ic_udp->uh_sum = new_cksum;
	} else if (pcb->cp_type == IPPROTO_ICMPV6) {
		icmp_pcb = (clickpcb_icmp_t *)pcb;

		ip6 = mtod(m, struct ip6_hdr *);

		ic_icmp6 = (struct icmp6_hdr*)(ic_ip6 + 1);

		icmp_opcb = icmp_pcb->reverse;

		ip6->ip6_dst = icmp_opcb->cp_remoteip6;
		ic_ip6->ip6_src = icmp_opcb->cp_remoteip6;
		ic_ip6->ip6_dst = icmp_opcb->cp_localip6;
		ic_icmp6->icmp6_id = icmp_opcb->cp_id;

		/*Accumulates 16 bits of ICMPV6*/
		ph_sum += htons(ic_icmp6->icmp6_id);
	    ph_sum += htons(ic_icmp6->icmp6_seq);
	    ph_sum += *(uint16_t *)(ic_icmp6);

		/*Processing overflow*/
		while (ph_sum >> 16) {
		    ph_sum = (ph_sum & 0xFFFF) + (ph_sum >> 16);
		}
		/*Calculated checksum*/
        uint16_t old_cksum = ic_icmp6->icmp6_cksum;
        uint32_t sum = ph_sum + ~(old_cksum & 0xFFFF);
        while (sum >> 16) {
            sum = (sum & 0xFFFF) + (sum >> 16);
        }
        uint16_t new_cksum = ~sum;

        /*Assign the checksum to the packet header*/
        ic_icmp6->icmp6_cksum = new_cksum;
	} else {
		return;
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/360979.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

lombok导致的IndexOutOfBoundsException

一、问题描述 ERROR 25152 --- [1.190-81-exec-9] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.mybatis.spring.MyBatisSyste…

MacBook安装虚拟机VMware Fusion

MacBook安装虚拟机VMware Fusion 官方下载地址: https://customerconnect.vmware.com/cn/downloads/info/slug/desktop_end_user_computing/vmware_fusion/11_0 介绍 之前的版本都要收费,现在出了对个人免费的版本, 棋哥给的破解版的版本是8,升级系统后用不了了. 官方去下载…

thinkadmin操作栏审核通过(操作确认),审核驳回(录入信息)

录入信息页面 {extend name="../../admin/view/main"}{block name=content} <style>textarea {font-size: 16px;padding: 10px;border: 1px solid #ccc;

EtherNET主站转Profinet网关实现EtherNET协议和Profinet协议相互转换

北京兴达易控EtherNET主站转Profinet网关是一种能将EtherNET协议和Profinet协议相互转换的设备&#xff0c;将网络通信技术与工业自动化技术完美结合。它不仅简化了通信的复杂度&#xff0c;而且提高了系统的可靠性和稳定性。 作为一个EtherNET主站转Profinet网关&#xff0c;它…

Python实现avif图片转jpg格式并识别图片中的文字

文章目录 一、图片识别文字1、导包2、代码实现3、运行效果 二、avif格式图片转jpg格式1、导包2、代码实现3、运行效果4、注意事项 三、Python实现avif图片转jpg格式并识别文字全部代码 在做数据分析的时候有些数据是从图片上去获取的&#xff0c;这就需要去识别图片上的文字。P…

软考(高级)在犹豫是否需要报班,不知大家有什么建议?

据我观察&#xff0c;软考是一门可以通过自学掌握的考试&#xff0c;并不争议。然而&#xff0c;尽管如此&#xff0c;我还是不建议大部分同学选择自学&#xff0c;因为相比报班而言&#xff0c;自学的成本反而较高。软考的难度并不低&#xff0c;往年的总体通过率仅为20%&…

IP关联是什么?有什么后果?如何防止电商账号因IP关联被封?

在跨境电商的世界里&#xff0c;IP关联给多账号运营的商家带来了挑战。比如&#xff0c;亚马逊IP关联规则的执行对于那些经营多个店铺的卖家来说可能是一个不小的障碍。IP关联的影响不只是限于亚马逊&#xff0c;其他平台如Instagram、Facebook也有类似的机制&#xff0c;在之前…

oracle数仓rac两个节点查询耗时不一致问题处理

问题描述 数据库节点1查询比节点2查询慢。现场操作应用发现发现同一sql语句在节点2上只要2分钟左右&#xff0c;在节点1&#xff0c;该条sql执行要超过30分钟。 处理过程 根据问题&#xff0c;初步判断是由于错误的执行计划&#xff0c;导致性能问题&#xff0c;但实际上对两…

海外代理IP推荐:5大最佳Luminati替代方案

在跨境出海业务中&#xff0c;海外代理IP是非常高效的助力工具&#xff0c;因此也衍生了非常多的代理服务商。想必大多数都知道Brightdata&#xff08;原名Luminati&#xff09;&#xff0c;但是&#xff0c;由于代理IP的高需求&#xff0c;慢慢地我们发现除了高价的卢米&#…

Redis常见问题

击穿 概念&#xff1a;在Redis获取某一key时, 由于key不存在, 而必须向DB发起一次请求的行为, 称为“Redis击穿”。 引发击穿的原因&#xff1a; 第一次访问恶意访问不存在的keyKey过期 合理的规避方案&#xff1a; 服务器启动时, 提前写入规范key的命名, 通过中间件拦截对…

Qt Excel读写 - QXlsx的安装配置以及测试

Qt Excel读写 - QXlsx的安装配置以及测试 引言一、安装配置二、简单测试 引言 Qt无自带的库处理Excel 文件&#xff0c;但可通过QAxObject 借助COM接口进行Excel的读写1。亦可使用免费的开源第三方库&#xff1a;QXlsx&#xff0c;一个基于Qt库开发的用于读写Microsoft Excel文…

知识点积累系列(六)操作系统(Linux+Windows+MacOS)篇【持续更新】

云原生学习路线导航页&#xff08;持续更新中&#xff09; 本文是 知识点积累 系列文章的第六篇&#xff0c;记录日常学习中遇到的 操作系统相关 的知识点&#xff0c;包括 Linux、Windows、MacOS等 1.Linux相关 1.1.shell脚本 1.2.命令相关 1.2.1.vim命令 1.2.2.nslookup命…

【C++】类和对象(二)——构造/析构/拷贝构造函数

&#x1f497;个人主页&#x1f497; ⭐个人专栏——C学习⭐ &#x1f4ab;点击关注&#x1f929;一起学习C语言&#x1f4af;&#x1f4ab; 目录 导读1. 默认成员函数2. 构造函数2.1 引入2.2 特性2.3 默认构造函数 3. 析构函数3.1 概念3.2 特性3.3 默认析构函数 4. 拷贝构造函…

如何发布一款移动 App?

如何发布一款移动 App&#xff1f; 本文转自 公众号 ByteByteGo&#xff0c;如有侵权&#xff0c;请联系&#xff0c;立即删除 今天来聊聊如何发布一款移动 App。 移动 App 的发布流程不同于传统方法。下图简化了这一过程&#xff0c;以帮助您理解。 移动应用程序发布流程的典…

计算机设计大赛 垃圾邮件(短信)分类算法实现 机器学习 深度学习

文章目录 0 前言2 垃圾短信/邮件 分类算法 原理2.1 常用的分类器 - 贝叶斯分类器 3 数据集介绍4 数据预处理5 特征提取6 训练分类器7 综合测试结果8 其他模型方法9 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 垃圾邮件(短信)分类算…

力扣349两个数的交集

题目连接&#xff1a;349. 两个数组的交集 - 力扣&#xff08;LeetCode&#xff09; 给定两个数组 nums1 和 nums2 &#xff0c;返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。 示例 1&#xff1a; 输入&#xff1a; nums1 [1,2,2…

elementUI之el-form-item的嵌套的场景

像这种&#xff0c;计费规则这几个字而且带红点&#xff0c;外观上是el-form-item&#xff0c;但是其并没有直接和控件进行相关联&#xff0c;这是和其他的el-form-item不同之处。所以这里就得用上嵌套了。也就是说elementUI中el-form-item是可以嵌套使用的。

幻兽帕鲁(Palworld)v0.1.3免安装中文版(下载及配置中文及服务器搭建)

配置中文 进入到游戏文件夹中 Palworld.v0.1.3.0\game\Engine\Binaries\ThirdParty\Steamworks\Steamv153\Win64\steam_settings设置中文 simplified chinesewindows搭建服务器 要求 中央处理器4核&#xff08;推荐&#xff09;内存16千兆字节&#xff08;GB&#xff09; …

深入玩转Playwright:高级操作解析与实践

playwright高级操作 iframe切换 ​ 很多时候&#xff0c;网页可能是网页嵌套网页&#xff0c;就是存在不止一个html标签&#xff0c;这时候我们的selenium或者playwright一般来说定位不到&#xff0c;为什么呢&#xff1f; ​ 因为默认是定位到第一个标准的html标签内部。 …

Python算法题集_滑动窗口最大值

本文为Python算法题集之一的代码示例 题目239&#xff1a;滑动窗口最大值 说明&#xff1a;给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗…