9.IPv6协议分析与实践

IPv6 协议分析与实践

1. 概述

1.1 简介

  • 用于解决 IPv4 的地址枯竭、骨干路由器维护的路由表过大、安全问题、不易进行自动配置和重新编址
  • 简化了报文头格式、充足的地址空间、层次化的地址结构、灵活的扩展头和邻居发现协议(NDP)

1.2 IPv6 地址

  • IPv6 采用128 位地址,分为 8 组,由冒号分隔; IPv4 映射 IPv6 地址,格式为 X:X:X:X:X:X:d.d.d.d
  • IPv6 地址由网络前缀和接口标识两个逻辑部分组成,接口标识通常根据物理地址自动生成,叫做 EUI-64
  • IPv6 没有广播地址,所有广播功能由组播实现
地址分类说明
单播地址(Unicast)一个单播地址对应一个接口,发往单播地址的数据包会被对应的接口接收
1. 未指定地址 ::/128,用于系统启动时,请求分配 IP 时作为源地址使用
2. 环回地址 ::1/128,用于自己向自己发送数据包时使用
3. 组播地址 FF00::/8
4. 唯一本地地址 FC00::/7,相当于私有IP,仅能够在本地网络使用
5. 链路本地单播地址 FE80::/10,用于没有路由存在的网络中,主机通过
MAC 地址自动配置生成IPv6地址,仅能在本地网络中使用
任播地址(Anycast)一个任播地址对应一组接口,发往任播地址的数据包会被这组接口的其中一个
接收,被哪个接口接收由具体的路由协议确定
多播地址(Multicast)一个组播地址对应一组接口,发往组播地址的数据包会被这组的所有接口接收

1.3 IPv6 头报文格式

在这里插入图片描述

  • 版本 : 版本号,IPv6 固定为 6

  • 传输类别 : 用于区分数据包的不同类别或优先级

  • 流标签 : 用于源端标记数据包顺序,供路由器特别处理,用来满足某些特殊服务,如QoS和实时信息

  • 载荷长度 : 数据长度,包括扩展报头

  • 下一个头部 : 基本报头后面的扩展报头类型

  • 跳数限制 : 数据包能经过的最大跳数,每经过一个路由器就减一

  • 源地址 : 发送端 IP 地址

  • 目的地址 : 接收端 IP 地址

  • 扩展报头 : 用于取代 IPv4 中的选项,相对而言提高了处理效率和扩展性

1.4 IPv6 常见扩展报文头

  • 当存在多个扩展报头时,必须按照下表顺序排列
序号名称说明
1IPv6 基本报头-
2逐跳选项扩展报头0在 IPv6 基本报头中定义
3目的选项扩展报头60指那些将被分组报文的最终目的地处理的选项
4路由扩展报头43用于源路由选项和Mobile IPv6
5分片扩展报头44在源节点发送的报文超过Path MTU时对报文分片时使用
6授权扩展报头51用于IPSec,提供报文验证、完整性检查
7封装安全有效载荷扩展报头50用于IPSec,提供报文验证、完整性检查和 IP 报文加密
8上层扩展报头如 TCP/UDP/ICMP 等,值与 IPv4 报文中协议一致

1.5 IPv6 伪头部

  • IPv6 报文校验工作都由上层协议完成,IPv6 报文没有校验和字段
    在这里插入图片描述

2. IPv6 编程

2.1 ipv6.h

#ifndef __ipv6_h_
#define __ipv6_h_#define IP_VERSION_6          6#define IP_PROTO_ICMP         1
#define IP_PROTO_IGMP         2
#define IP_PROTO_TCP          6
#define IP_PROTO_UDP          17
#define IP_PROTO_IPV6         41
#define IP_PROTO_SCTP         132
#define IP_PROTO_RAW          255/**  IPv6 fixed header**  BEWARE, it is incorrect. The first 4 bits of flow_lbl*  are glued to priority now, forming "class".*/
struct ipv6_hdr {uint8_t  priority:4;uint8_t  version:4;uint8_t  flow_lbl[3]; uint16_t payload_len;uint8_t  nexthdr;uint8_t  hop_limit;uint8_t  saddr[16];uint8_t  daddr[16];uint8_t  data[0];
};struct ipv6_hdr *ipv6_alloc_packet(uint32_t flow_lbl, uint16_t payload_len, uint8_t nexthdr, uint8_t hop_limit, const char *saddr, const char *daddr, const void *data);void ipv6_free_packet(struct ipv6_hdr **ipv6);void ipv6_print(struct ipv6_hdr *ipv6);int ipv6_socket();ssize_t ipv6_send(int sockfd, const void *data, size_t size, const char *daddr, int flags);ssize_t ipv6_recv(int sockfd, void *data, size_t size, const char *daddr, int flags);void ipv6_close(int sockfd);/*** IPv6 伪头部, 用于上层协议计算其 checksum** 注 : IPv6 没在校验和字段*/
struct ipv6_pseudo_header {uint8_t  src_addr[16];uint8_t  dest_addr[16];uint32_t length;uint8_t  zero[3];uint8_t  nextptr;uint8_t  data[0];
};uint16_t ipv6_cksum(const char *saddr, const char *daddr, uint8_t nextptr, const void *data, size_t size);#endif /* __ipv6_h_ */

2.2 ipv6.c

#include 
#include 
#include 
#include 
#include 
#include #include "ipv6.h"
#include "cksum.h"
#include "common.h"struct ipv6_hdr *ipv6_alloc_packet(uint32_t flow_lbl, uint16_t payload_len, uint8_t nexthdr, uint8_t hop_limit, const char *saddr, const char *daddr, const void *data) 
{struct ipv6_hdr *ipv6;struct sockaddr_in6 addr;ipv6 = (struct ipv6_hdr *) calloc(1, sizeof(struct ipv6_hdr) + payload_len);ipv6->version     = IP_VERSION_6;ipv6->flow_lbl[0] = flow_lbl >> 16 & 0x0000000F;ipv6->flow_lbl[1] = flow_lbl >> 8  & 0x000000FF;ipv6->flow_lbl[2] = flow_lbl >> 0  & 0x000000FF;ipv6->payload_len = htons(payload_len);ipv6->nexthdr     = nexthdr;ipv6->hop_limit   = hop_limit;if (inet_pton(AF_INET6, saddr, &addr.sin6_addr) == 0) handle_error_en(EINVAL, "saddr");memcpy(ipv6->saddr, &addr.sin6_addr, sizeof(addr.sin6_addr));if (inet_pton(AF_INET6, daddr, &addr.sin6_addr) == 0) handle_error_en(EINVAL, "saddr");memcpy(ipv6->daddr, &addr.sin6_addr, sizeof(addr.sin6_addr));memcpy(ipv6->data, data, payload_len);return ipv6;	
}void ipv6_free_packet(struct ipv6_hdr **ipv6)
{if (NULL != ipv6 && NULL != *ipv6) {free(*ipv6);*ipv6 = NULL;}
}void ipv6_print(struct ipv6_hdr *ipv6)
{char str[INET6_ADDRSTRLEN];printf("%u\n", ipv6->version);printf("%s\n", ipv6->flow_lbl);printf("%u\n", ntohs(ipv6->payload_len));printf("%u\n", ipv6->nexthdr);printf("%u\n", ipv6->hop_limit);if (inet_ntop(AF_INET6, ipv6->saddr, str, INET6_ADDRSTRLEN) == NULL)handle_error("inet_ntop");printf("%s\n", str);if (inet_ntop(AF_INET6, ipv6->daddr, str, INET6_ADDRSTRLEN) == NULL)handle_error("inet_ntop");printf("%s\n", str);
}int ipv6_socket() 
{int sockfd;int flag = 1;if ((sockfd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW)) == -1) handle_error("socket");if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_HDRINCL, &flag, sizeof(flag)) == -1)handle_error("setsockopt : IPV6_HDRINCL");return sockfd;}ssize_t ipv6_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 ipv6_recv(int sockfd, void *data, 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, data, size, flags, (struct sockaddr *)&addr, &socklen)) == -1)handle_error("recvfrom");return count;
}void ipv6_close(int sockfd)
{if (close(sockfd) == -1)handle_error("close");
}uint16_t ipv6_cksum(const char *saddr, const char *daddr, uint8_t nextptr, const void *data, size_t size)
{struct sockaddr_in6 addr;unsigned int hdr_len, checksum;struct ipv6_pseudo_header *header; hdr_len = sizeof(struct ipv6_pseudo_header) + size;header  = (struct ipv6_pseudo_header *) calloc(1, hdr_len);if (inet_pton(AF_INET6, saddr, &addr.sin6_addr) == 0)  handle_error_en(EINVAL, "saddr");memcpy(header->src_addr, &addr.sin6_addr, sizeof(addr.sin6_addr));if (inet_pton(AF_INET6, daddr, &addr.sin6_addr) == 0)  handle_error_en(EINVAL, "daddr");memcpy(header->dest_addr, &addr.sin6_addr, sizeof(addr.sin6_addr));header->nextptr = nextptr;header->length  = htons(size);memcpy(header->data, data, size);   checksum = cksum((uint16_t *)header, hdr_len);free(header);return htons(checksum);
}

2.3 main.c

#include 
#include 
#include 
#include 
#include #include "ipv6.h"static void ipv6_test() 
{int sockfd;struct ipv6_hdr *ipv6;unsigned short tot_len;unsigned short data_len;const char *saddr = "fe80::20c:cff:fe0c:c0c";const char *daddr = "fe80::20d:dff:fe0d:d0d";const char data[] = { 0x1f, 0x40, 0x23, 0x28, 0x00, 0x0c, 0x31, 0x6a, 0x61, 0x62, 0x63, 0x0a };sockfd   = ipv6_socket(daddr);data_len = sizeof(data);tot_len  = sizeof(struct ipv6_hdr) + data_len;ipv6 = ipv6_alloc_packet(0xd3f3e, data_len, IPPROTO_UDP, 64, saddr, daddr, data);ipv6_send(sockfd, ipv6, tot_len, daddr, 0);ipv6_free_packet(&ipv6);ipv6_close(sockfd);
}int main(int argc, char *argv[])
{ipv6_test();return 0;
}
192.168.2.200> sudo tcpdump -nt -XX ip6
IP6 fe80::20c:cff:fe0c:c0c.8000 > fe80::20d:dff:fe0d:d0d.9000: UDP, length 40x0000:  000d 0d0d 0d0d 000c 0c0c 0c0c 86dd 600d  ..............`.0x0010:  3f3e 000c 1140 fe80 0000 0000 0000 020c  ?>...@..........0x0020:  0cff fe0c 0c0c fe80 0000 0000 0000 020d  ................0x0030:  0dff fe0d 0d0d 1f40 2328 000c 316a 6162  .......@#(..1jab0x0040:  630a192.168.2.100> make run
参考链接
  • https://tools.ietf.org/html/rfc2460
  • https://zh.wikipedia.org/wiki/IPv6
  • https://support.huawei.com/hedex/hdx.do?docid=EDOC1000105967&lang=zh&idPath=24030814|9856750|22715517|9858933|15837


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部