注意事项:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <netinet/udp.h>
#include <arpa/inet.h>
#include <net/if.h>
// 计算校验和
unsigned short
checksum(unsigned short* addr, int len)
{
int nleft = len;
int sum = 0;
unsigned short* w = addr;
unsigned short answer = 0;
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
if (nleft == 1) {
*(unsigned char*)(&answer) = *(unsigned char*)w;
sum += answer;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return answer;
}
// 计算IPv6 UDP校验和
unsigned short
udp6_checksum(struct ip6_hdr* ip6, struct udphdr* udp, char* data, int data_len)
{
struct pseudo_header6
{
struct in6_addr source_address;
struct in6_addr dest_address;
u_int32_t udp_length;
u_int8_t placeholder[3];
u_int8_t protocol;
} pseudo_header6;
pseudo_header6.source_address = ip6->ip6_src;
pseudo_header6.dest_address = ip6->ip6_dst;
memset(pseudo_header6.placeholder, 0, sizeof(pseudo_header6.placeholder));
pseudo_header6.protocol = IPPROTO_UDP;
pseudo_header6.udp_length = htonl(sizeof(struct udphdr) + data_len);
int psize = sizeof(struct pseudo_header6) + sizeof(struct udphdr) + data_len;
char* pseudogram = malloc(psize);
memcpy(pseudogram, &pseudo_header6, sizeof(struct pseudo_header6));
memcpy(pseudogram + sizeof(struct pseudo_header6), udp, sizeof(struct udphdr));
memcpy(pseudogram + sizeof(struct pseudo_header6) + sizeof(struct udphdr), data, data_len);
unsigned short csum = checksum((unsigned short*)pseudogram, psize);
free(pseudogram);
return csum;
}
int
main(int argc, char* argv[])
{
if (argc < 2) {
fprintf(stderr, "Usage: %s <message>", argv[0]);
exit(EXIT_FAILURE);
}
// 要发送的消息
char* message = argv[1];
int msg_len = strlen(message);
// 创建原始套接字,应该使用IPPROTO_RAW,使用IPPROTO_IDP会导致发送的数据异常
int sockfd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW);
if (sockfd < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
// 设置IP_HDRINCL选项,我们自己构建IPv6头部
int one = 1;
if (setsockopt(sockfd, IPPROTO_IPV6, IP_HDRINCL, &one, sizeof(one)) < 0) {
perror("setsockopt IP_HDRINCL");
close(sockfd);
exit(EXIT_FAILURE);
}
// 设置SO_BINDTODEVICE选项,绑定到ens37接口
if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, "ens33", strlen("ens33")) < 0) {
perror("setsockopt SO_BINDTODEVICE");
close(sockfd);
exit(EXIT_FAILURE);
}
// 准备目标地址结构
struct sockaddr_in6 dest_addr;
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.sin6_family = AF_INET6;
dest_addr.sin6_port = htons(0); // 目标端口需要设置为0,否则会发送失败
if (inet_pton(AF_INET6, "fe80::328e:a3dc:3971:56b5", &dest_addr.sin6_addr) <= 0) {
perror("inet_pton dest_addr");
close(sockfd);
exit(EXIT_FAILURE);
}
// 准备IPv6头部
struct ip6_hdr* ip6 = (struct ip6_hdr*)malloc(sizeof(struct ip6_hdr));
memset(ip6, 0, sizeof(struct ip6_hdr));
ip6->ip6_flow = htonl((6 << 28) | (0 << 20) | 0); // 版本(6) | 流量类别 | 流标签
ip6->ip6_plen = htons(sizeof(struct udphdr) + msg_len); // 负载长度
ip6->ip6_nxt = IPPROTO_UDP; // 下一个头部(UDP)
ip6->ip6_hlim = 64; // 跳数限制
// 设置源IPv6地址
if (inet_pton(AF_INET6, "2001:db8::2", &ip6->ip6_src) <= 0) {
perror("inet_pton src_ip6");
free(ip6);
close(sockfd);
exit(EXIT_FAILURE);
}
// 设置目标IPv6地址
if (inet_pton(AF_INET6, "fe80::328e:a3dc:3971:56b5", &ip6->ip6_dst) <= 0) {
perror("inet_pton dest_ip6");
free(ip6);
close(sockfd);
exit(EXIT_FAILURE);
}
// 准备UDP头部
struct udphdr* udp = (struct udphdr*)malloc(sizeof(struct udphdr));
memset(udp, 0, sizeof(struct udphdr));
udp->source = htons(54321); // 源端口
udp->dest = htons(12345); // 目标端口
udp->len = htons(sizeof(struct udphdr) + msg_len);
udp->check = 0; // 先设为0,后面计算校验和
// 计算UDP校验和
udp->check = udp6_checksum(ip6, udp, message, msg_len);
if (udp->check == 0) {
udp->check = 0xffff; // RFC 768规定,校验和为0时用0xffff代替
}
// 构建完整的数据包
int packet_size = sizeof(struct ip6_hdr) + sizeof(struct udphdr) + msg_len;
char* packet = malloc(packet_size);
memset(packet, 0, packet_size);
// 复制各部分到数据包
memcpy(packet, ip6, sizeof(struct ip6_hdr));
memcpy(packet + sizeof(struct ip6_hdr), udp, sizeof(struct udphdr));
memcpy(packet + sizeof(struct ip6_hdr) + sizeof(struct udphdr), message, msg_len);
// 发送数据包
if (sendto(sockfd, packet, packet_size, 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr)) < 0) {
perror("sendto");
free(ip6);
free(udp);
free(packet);
close(sockfd);
exit(EXIT_FAILURE);
}
printf("Sent IPv6 UDP packet from 2001:db8::2:54321 to fe80::328e:a3dc:3971:56b5:12345 via ens33: %s\n", message);
// 清理资源
free(ip6);
free(udp);
free(packet);
close(sockfd);
return 0;
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。