10.ICMPv6协议分析与实践

ICMPv6 协议分析与实践

1. 概述

1.1 简介

  • Internet Control Message Protocol version 6 是 IPv6 网络使用的控制消息,功能类似 ICMP

1.2 ICMPv6 报文格式

在这里插入图片描述

1.2.1 ICMPv6 错误消息
类型说明代码
1目的不可达消息0 没有路由到达目的地
1 与目的地的通信由于管理被禁止,如防火墙禁止
2 超过了源地址的范围
3 地址不可达
4 端口不可达
5 源地址的入口/出口策略失败
6 拒绝到目的地的路由
2数据包太大0
3超时消息0 传输超时,即 hop-limit 为 0(tracetoute 原理)
1 分片重组超时,重组定时器超时了,还有分片没到达
4参数问题消息0 参数错误
1 错误的首部字段
2 不可识别的Next Header类型
3 不可识别的IPv6选项
100私有实验用x
101私有实验用x
127扩展保留x
1.2.2 ICMPv6 信息消息
类型说明代码
128回显请求0
129回显应答0
200私有实验用x
201私有实验用x
255扩展保留x

1.3 不会产生 ICMPv6 报文的情况

  • ICMPv6 差错报文
  • ICMPv6 重定向报文
  • 目的地址是 IPv6 组播地址的数据包,除了数据包太大报文和参数问题报文
  • 作为链路层组播数据包,以及上面的例外情况
  • 作为链路层广播数据包,以及上面的例外情况
  • 源地址不是唯一识别的单个节点的数据包,即源地址不能是未指定的地址、IPv6 组播地址和发送者所知道的选播地址

2. ICMPv6 编程

2.1 icmp6.h

#ifndef __icmpv6_h_
#define __icmpv6_h_

#define ICMPV6_DEST_UNREACH     1
#define ICMPV6_PKT_TOOBIG       2
#define ICMPV6_TIME_EXCEED      3
#define ICMPV6_PARAMPROB        4

#define ICMPV6_ECHO_REQUEST     128
#define ICMPV6_ECHO_REPLY       129

struct icmp6hdr {
	uint8_t  type;
    uint8_t  code;
    uint16_t cksum;

    union {
		uint32_t un_data32[1];
        uint16_t un_data16[2];
		uint8_t  un_data8[4];

        struct icmpv6_echo {
            uint16_t identifier;
			uint16_t sequence;
        } u_echo;

		struct icmpv6_nd_advt {
			uint32_t reserved:5;
			uint32_t override:1;
			uint32_t solicited:1;
			uint32_t router:1;
			uint32_t reserved2:24;
		} u_nd_advt;

		struct icmpv6_nd_ra {
			uint8_t  hop_limit;
			uint8_t  reserved:3;
			uint8_t  router_pref:2;
			uint8_t  home_agent:1;
			uint8_t  other:1;
			uint8_t  managed:1;
			uint16_t rt_lifetime;
		} u_nd_ra;

    } icmp6_dataun;

	uint8_t data[0];

#define icmp6_identifier		icmp6_dataun.u_echo.identifier
#define icmp6_sequence			icmp6_dataun.u_echo.sequence
#define icmp6_pointer			icmp6_dataun.un_data32[0]
#define icmp6_mtu				icmp6_dataun.un_data32[0]
#define icmp6_unused			icmp6_dataun.un_data32[0]
#define icmp6_maxdelay			icmp6_dataun.un_data16[0]
#define icmp6_router			icmp6_dataun.u_nd_advt.router
#define icmp6_solicited			icmp6_dataun.u_nd_advt.solicited
#define icmp6_override			icmp6_dataun.u_nd_advt.override
#define icmp6_ndiscreserved		icmp6_dataun.u_nd_advt.reserved
#define icmp6_hop_limit			icmp6_dataun.u_nd_ra.hop_limit
#define icmp6_addrconf_managed  icmp6_dataun.u_nd_ra.managed
#define icmp6_addrconf_other    icmp6_dataun.u_nd_ra.other
#define icmp6_rt_lifetime		icmp6_dataun.u_nd_ra.rt_lifetime
#define icmp6_router_pref		icmp6_dataun.u_nd_ra.router_pref
};

struct icmp6hdr* icmp6_alloc_echo_datagram(unsigned short id, unsigned seq);

struct icmp6hdr* icmp6_alloc_dest_unreach_datagram(
		unsigned short code, const void *data, size_t size);

void icmp6_free_datagram(struct icmp6hdr **icmp6);
		
void icmp6_print_echo(const struct icmp6hdr *icmp6);


int icmp6_socket();

ssize_t icmp6_send(int sockfd, const void *data, size_t size, 
		const char *daddr, int flags);

ssize_t icmp6_recv(int sockfd, void *buf, size_t size, 
		const char *daddr, int flags);

void icmp6_close(int sockfd);

#endif /* __icmp6v6_h_ */

2.1 icmp6.c

#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "icmp6.h"
#include "common.h"

struct icmp6hdr* icmp6_alloc_echo_datagram(unsigned short id, unsigned seq)
{
	struct icmp6hdr *icmp6;
	icmp6 = (struct icmp6hdr *) calloc(1, sizeof(struct icmp6hdr));
	icmp6->type             = ICMPV6_ECHO_REQUEST;
	icmp6->code             = 0;
	icmp6->icmp6_identifier = htons(id);
	icmp6->icmp6_sequence   = htons(seq);
	return icmp6;
}

struct icmp6hdr* icmp6_alloc_dest_unreach_datagram(unsigned short code, 
		const void *data, size_t size)
{
	struct icmp6hdr *icmp6;
	icmp6 = (struct icmp6hdr *) calloc(1, sizeof(struct icmp6hdr) + size);
	icmp6->type     = ICMPV6_DEST_UNREACH; 
	icmp6->code     = code; 
	memcpy(icmp6->data, data, size);
	return icmp6;
}

void icmp6_free_datagram(struct icmp6hdr **icmp6)
{
	if (NULL != icmp6 && NULL != *icmp6) {
		free(*icmp6);
		*icmp6 = NULL;
	}
}

void icmp6_print_echo(const struct icmp6hdr *icmp6) 
{
	printf("%02x\n", icmp6->type);
	printf("%02x\n", icmp6->code);
	printf("%04x\n", htons(icmp6->cksum));
	printf("%04x\n", ntohs(icmp6->icmp6_identifier));
	printf("%04x\n", ntohs(icmp6->icmp6_sequence));
}


int icmp6_socket() 
{
	int sockfd, size;
	if ((sockfd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) == -1) 
		handle_error("socket");

	// 增大接收缓冲区, 防止 ping 组播地址时溢出
	size = 128 * 1024;
	if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)))
		handle_error("setsockopt : SO_RCVBUF");

	return sockfd;
}

ssize_t icmp6_send(int sockfd, const void *data, size_t size, const char *daddr, int flags) 
{
	ssize_t count;
	struct sockaddr_in6 addr;

	memset(&addr, 0, sizeof(addr));
	addr.sin6_family = AF_INET6;
	inet_pton(addr.sin6_family, daddr, &addr.sin6_addr);

	if ((count = sendto(sockfd, data, size, flags, (struct sockaddr *)&addr, sizeof(addr))) == -1)
		handle_error("sendto");
	return count;
}

ssize_t icmp6_recv(int sockfd, void *buf, size_t size, 
		const char *daddr, int flags) 
{
	ssize_t count;
	struct sockaddr_in6 addr;
	socklen_t socklen = sizeof(addr);

	memset(&addr, 0, sizeof(addr));
	addr.sin6_family = AF_INET6;
	inet_pton(addr.sin6_family, daddr, &addr.sin6_addr);

	if ((count = recvfrom(sockfd, buf, size, flags, (struct sockaddr *)&addr, &socklen)) == -1)
		handle_error("recvfrom");
	return count;
}

void icmp6_close(int sockfd) 
{
	if (close(sockfd) == -1)
		handle_error("close");
}

2.1 main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <arpa/inet.h>

#include "ipv6.h"
#include "icmp6.h"

#define BUFFER_SIZE 1500

static void icmp6_ping(const char *saddr, const char *daddr)
{
	int sockfd;
	struct icmp6hdr *icmp6;
	char buffer[BUFFER_SIZE];

	sockfd = icmp6_socket();

	// 发送消息
	icmp6 = icmp6_alloc_echo_datagram(0x0001, 0x0009);
	icmp6->cksum = ipv6_cksum(saddr, daddr, IPPROTO_ICMPV6, icmp6, sizeof(struct icmp6hdr));
	icmp6_send(sockfd, icmp6, sizeof(struct icmp6hdr), daddr, 0);
	icmp6_free_datagram(&icmp6);

	// 接收消息
	memset(buffer, 0, BUFFER_SIZE);
	icmp6_recv(sockfd, buffer, BUFFER_SIZE, daddr, 0);

	// 解析消息
	icmp6 = (struct icmp6hdr *) buffer;
	icmp6_print_echo(icmp6);

	icmp6_close(sockfd);
}

int main(int argc, char *argv[])
{
	icmp6_ping("fe80::e0c:cff:fe0c:c0c", "fe80::20d:dff:fe0d:d0d");
	return 0;
}
192.168.2.100> watch make run
192.168.2.200> sudo tcpdump -nt -XX icmp6
IP6 fe80::e0c:cff:fe0c:c0c > fe80::20d:dff:fe0d:d0d: ICMP6, echo request,seq 9, len 8
	0x0000:  000d 0d0d 0d0d 0c0c 0c0c 0c0c 86dd 600a  ..............`.
	0x0010:  1b04 0008 3a40 fe80 0000 0000 0000 0e0c  ....:@..........
	0x0020:  0cff fe0c 0c0c fe80 0000 0000 0000 020d  ................
	0x0030:  0dff fe0d 0d0d 8000 4266 0001 0009       ........Bf....
IP6 fe80::20d:dff:fe0d:d0d > fe80::e0c:cff:fe0c:c0c: ICMP6, echo reply, seq 9, len 8
	0x0000:  0c0c 0c0c 0c0c 000d 0d0d 0d0d 86dd 6009  ..............`.
	0x0010:  1647 0008 3a40 fe80 0000 0000 0000 020d  .G..:@..........
	0x0020:  0dff fe0d 0d0d fe80 0000 0000 0000 0e0c  ................
	0x0030:  0cff fe0c 0c0c 8100 4166 0001 0009       ........Af....
参考链接

https://tools.ietf.org/html/rfc4443

https://tools.ietf.org/html/rfc2463

https://tools.ietf.org/html/rfc2460

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值