ICMPv6报文解析及NAT处理

news2024/11/17 3:32:04

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;
	}
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从零开始的OpenGL光栅化渲染器构建6-PBR光照模型

前言 PBR&#xff0c;或者基于物理的渲染(Physically Based Rendering)&#xff0c;它指的是一些在不同程度上都基于与现实世界的物理原理更相符的基本理论所构成的渲染技术的集合。正因为基于物理的渲染目的便是为了使用一种更符合物理学规律的方式来模拟光线&#xff0c;因此…

Mybatis 源码系列:领略设计模式在 Mybatis 其中的应用

文章目录 一、Builder模式二、工厂模式三、单例模式四、代理模式五、组合模式六、模板方式模式七、适配器模式八、装饰器模式九、迭代器模式 虽然我们都知道有23种设计模式&#xff0c;但是大多停留在概念层面&#xff0c;真实开发中很少遇到&#xff0c;Mybatis源码中使用了大…

代理IP技术在云函数中的创新应用与拓展空间

目录 前言 一、代理IP技术的基本概念和原理 二、云函数的基本原理和优势 1. 弹性伸缩 2. 省时省力 3. 按需计费 三、代理IP技术在云函数中的创新应用 1. 反爬虫技术 2. 访问安全性和隐私保护 3. 地理定位和访问控制 四、代理IP技术在云函数中的拓展空间 1. 代理IP池…

力扣hot100 跳跃游戏 贪心

Problem: 55. 跳跃游戏 文章目录 思路复杂度Code 思路 &#x1f468;‍&#x1f3eb; 参考 挨着跳&#xff0c;记录最远能到达的地方 复杂度 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( 1 ) O(1) O(1) Code class Solution {public boolean canJump(int[] nums)…

AI大模型专题:大模型安全与伦理研究报告2024

今天分享的是AI大模型系列深度研究报告&#xff1a;《AI大模型专题&#xff1a;大模型安全与伦理研究报告2024》。 &#xff08;报告出品方&#xff1a;腾讯研究院&#xff09; 报告共计&#xff1a;76页 大模型具有不同于传统模型的特点和优势 第一&#xff0c;大模型和传统…

页面通过Vue进行整体页面不同语言切换 i18n库

目录 引入 如何做到 下载i18n库 构建整体翻译文件结构 语言包文件 i18n配置文件 把i18n挂载到vue实例上 添加按钮点击事件切换语言 引入 我们现在有这样一个要求,我们想要对我们开发的网页进行国际化操作,也就是我们不仅要有中文,还要有英文等。用户可以随时进行不同语言…

langchain+xray:prompt控制漏洞扫描

写在前面 xray是长亭推出的一款漏洞扫描工具。 langchain是调用LLM大模型完成自动化任务的框架。 本篇文章是对langchain自定义工具的探索&#xff0c;通过编写一个xray调用的工具&#xff0c;联合ChatGPT对xray进行调用&#xff0c;实现对目标的漏洞扫描。 xray功能分析 …

洛谷-P4124题-手机号码-Java

题目 题目链接&#xff1a; https://www.luogu.com.cn/problem/P4124 分析 给定两个长度为11位的数字&#xff0c;代表两个区间 [L,R] 需要编写程序来计算出&#xff0c;这两个区间内满足要求的数字个数。这样的题一般来说就是数位dp题。首先我们可以根据容斥原理 [0,R]中满…

第一批 Apple Vision Pro 开箱和佩戴体验新鲜出炉!!!

注: 本文转自微信公众号 BravoAI (专注AI资讯和技术分享), 原文网址:第一批 Apple Vision Pro 开箱和佩戴体验新鲜出炉!!!, 扫码关注公众号 编者按: 整个AR/VR行业都在等AVP, 期待它能带来ChatGPT般的冲击 AVP(Apple Vision Pro) 是苹果公司研发的第一款"空间计算 (Spa…

跟着pink老师前端入门教程-day11

3、CSS 三角 网页中常见一些三角形&#xff0c;使用CSS直接画出来即可&#xff0c;不必做成图片或字体图标 4、CSS 用户界面样式 4.1 什么是界面样式 界面样式&#xff1a;更改一些用户操作样式&#xff0c;以便提高更好的用户体验&#xff08;更改用户的鼠标样式、表单轮廓…

Redis简单阐述、安装配置及远程访问

目录 一、Redis简介 1.什么是Redis 2.特点 3.优势 4.数据库对比 5.应用场景 二、 安装与配置 1.下载 2.上传解压 3.安装gcc 4.编译 5.查看安装目录 6.后端启动 7.测试 8.系统服务配置 三、Redis远程访问 1.修改访问IP地址 2.设置登录密码 3.重启Redis服务 …

《QDebug 2024年1月》

一、Qt Widgets 问题交流 1. 二、Qt Quick 问题交流 1.Repeator 的 delegate 在 remove 移除时的注意事项 Qt Bug Tracker&#xff1a;https://bugreports.qt.io/browse/QTBUG-47500 Repeator 在调用 remove 函数之后&#xff0c;对应的 Item 会立即释放&#xff0c;后续就…

RabbitMQ之死信队列、延迟队列和懒队列

目录 死信队列 何时会产生死信 死信队列的配置方式 参数x-dead-letter-routing-key 如何确定一个消息是不是死信 延迟队列 懒队列 声明懒队列的两种方式 参数声明 策略指定 死信队列 死信队列是RabbitMQ中非常重要的一个特性。简单理解&#xff0c;他是RabbitMQ对于未…

第九节HarmonyOS 常用基础组件20-Divider

1、描述 提供分割器组件&#xff0c;分割不同内容块或内容元素。 2、接口 Divider() 3、属性 名称 参数类型 描述 vertical boolean 使用水平分割线还是垂直分割线。 false&#xff1a;水平分割线 true&#xff1a;垂直分割线 color ResourceColor 分割线颜色 默认…

数字图像处理(实践篇)三十七 OpenCV-Python 使用SIFT和BFmatcher对两个输入图像的关键点进行匹配实践

目录 一 涉及的函数 二 实践 三 报错处理 使用SIFT(尺度不变特征变换)算法

【Midjourney】AI绘画案例(1)龙年吉祥神兽

说明&#xff1a; 1、文中图片版权均为Midjourney所有&#xff0c;请勿用作商业用途。 2、文中图片均经过 Upscale x 4 处理。 3、由于模型原因&#xff0c;某些图片存在暇玼。 1、吉祥神兽——天马&#xff08;独角兽&#xff09; 天马消灾星。 提示词 Prompt: Sky Unicor…

力扣(LeetCode)227. 基本计算器 II

给你一个字符串表达式 s &#xff0c;请你实现一个基本计算器来计算并返回它的值。 整数除法仅保留整数部分。 你可以假设给定的表达式总是有效的。所有中间结果将在 [-231, 231 - 1] 的范围内。 注意&#xff1a;不允许使用任何将字符串作为数学表达式计算的内置函数&#…

Java在网络通信中应该如何选择合适的序列化框架?

前言 说到网络通信就会牵涉到数据的序列化与反序列化&#xff0c;现如今序列化框架也是层出不穷&#xff0c;比如FST、Kryo、ProtoBuffer、Thrift、Hessian、Avro、MsgPack等等&#xff0c;有的人可能会有疑问&#xff0c;为什么市面上有这么多框架&#xff0c;JDK不是已经有自…

LeetCode刷题日志-117填充每个节点的下一个右侧指针II

二叉树的题目&#xff0c;我认为二叉树必须要掌握递归的三种遍历算法&#xff0c;以及层序遍历算法才能做二叉树题目。这道题目我的想法是&#xff1a; 因为在二叉树每一层中&#xff0c;next指针指向的是的当前节点的右边的节点&#xff0c;所以&#xff0c;使用层序遍历&…

中国建设银行,这年终奖噶噶高!!!!(含算法原题)

国企年终 今天刷到一个近期帖子:「中国建设银行&#xff0c;这年终奖噶噶高!!!!」 先撇去具体内容不看&#xff0c;能在自然年的 月初&#xff0c;就把去年的奖金发了的企业&#xff0c;首先值得一个点赞。 再细看内容&#xff0c;年终奖是一个 字头的 位数。 由于国企通常没…