地址分类 | 说明 |
---|---|
单播地址(Unicast) | 一个单播地址对应一个接口,发往单播地址的数据包会被对应的接口接收 1. 未指定地址 ::/128 ,用于系统启动时,请求分配 IP 时作为源地址使用2. 环回地址 ::1/128 ,用于自己向自己发送数据包时使用3. 组播地址 FF00::/8 ,4. 唯一本地地址 FC00::/7 ,相当于私有IP,仅能够在本地网络使用5. 链路本地单播地址 FE80::/10 ,用于没有路由存在的网络中,主机通过 MAC 地址自动配置生成IPv6地址,仅能在本地网络中使用 |
任播地址(Anycast) | 一个任播地址对应一组接口,发往任播地址的数据包会被这组接口的其中一个 接收,被哪个接口接收由具体的路由协议确定 |
多播地址(Multicast) | 一个组播地址对应一组接口,发往组播地址的数据包会被这组的所有接口接收 |
版本 : 版本号,IPv6 固定为 6
传输类别 : 用于区分数据包的不同类别或优先级
流标签 : 用于源端标记数据包顺序,供路由器特别处理,用来满足某些特殊服务,如QoS和实时信息
载荷长度 : 数据长度,包括扩展报头
下一个头部 : 基本报头后面的扩展报头类型
跳数限制 : 数据包能经过的最大跳数,每经过一个路由器就减一
源地址 : 发送端 IP 地址
目的地址 : 接收端 IP 地址
扩展报头 : 用于取代 IPv4 中的选项,相对而言提高了处理效率和扩展性
序号 | 名称 | 值 | 说明 |
---|---|---|---|
1 | IPv6 基本报头 | - | |
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 报文中协议一致 |
#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_ */
#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);
}
#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 4
0x0000: 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 .......@#(..1jab
0x0040: 630a
192.168.2.100> make run