首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >​​数据链路层(L2)原始套接字,IPv6,UDP发送数据包代码示例​​

​​数据链路层(L2)原始套接字,IPv6,UDP发送数据包代码示例​​

原创
作者头像
胡椒粉
发布2025-08-29 11:52:10
发布2025-08-29 11:52:10
1870
举报
文章被收录于专栏:Linux系统开发Linux系统开发
代码语言:txt
复制
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ether.h>
#include <netinet/ip6.h>
#include <netinet/udp.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <netpacket/packet.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;
}

// 计算UDP校验和
unsigned short udp_checksum(struct ip6_hdr *ip6, struct udphdr *udp, char *data, int data_len) {
    int len = sizeof(struct udphdr) + data_len;
    char *buf = malloc(len + sizeof(struct ip6_hdr));
    memcpy(buf, ip6, sizeof(struct ip6_hdr));
    memcpy(buf + sizeof(struct ip6_hdr), udp, sizeof(struct udphdr));
    memcpy(buf + sizeof(struct ip6_hdr) + sizeof(struct udphdr), data, data_len);

    // 伪首部校验和
    unsigned short csum = checksum((unsigned short *)buf, len + sizeof(struct ip6_hdr));
    free(buf);
    return csum;
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <message>\\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    // 要发送的消息
    char *message = argv[1];
    int msg_len = strlen(message);

    // 创建原始套接字
    int sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (sockfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 获取ens33接口索引
    struct ifreq if_idx;
    memset(&if_idx, 0, sizeof(struct ifreq));
    strncpy(if_idx.ifr_name, "ens33", IFNAMSIZ - 1);
    if (ioctl(sockfd, SIOCGIFINDEX, &if_idx) < 0) {
        perror("SIOCGIFINDEX");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    // 设置目标地址
    struct sockaddr_ll sockaddr;
    memset(&sockaddr, 0, sizeof(struct sockaddr_ll));
    sockaddr.sll_ifindex = if_idx.ifr_ifindex;
    sockaddr.sll_halen = ETH_ALEN;

    // 准备以太网头部
    struct ether_header *eth = (struct ether_header *)malloc(sizeof(struct ether_header));
    memset(eth, 0, sizeof(struct ether_header));
    
    // 获取ens33接口的MAC地址
    struct ifreq if_mac;
    memset(&if_mac, 0, sizeof(struct ifreq));
    strncpy(if_mac.ifr_name, "ens33", IFNAMSIZ - 1);
    if (ioctl(sockfd, SIOCGIFHWADDR, &if_mac) < 0) {
        perror("SIOCGIFHWADDR");
        free(eth);
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    // 设置目标MAC地址(这里使用广播地址,实际应用中应使用正确的目标MAC)
    memset(eth->ether_dhost, 0xff, ETH_ALEN);
    // 设置源MAC地址为ens33接口的MAC地址
    memcpy(eth->ether_shost, if_mac.ifr_hwaddr.sa_data, ETH_ALEN);
    eth->ether_type = htons(ETH_P_IPV6);
    memcpy(sockaddr.sll_addr, eth->ether_dhost, ETH_ALEN);

    // 准备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,流量类别0,流标签0
    ip6->ip6_plen = htons(sizeof(struct udphdr) + msg_len); // 负载长度
    ip6->ip6_nxt = IPPROTO_UDP; // 下一个头部是UDP
    ip6->ip6_hlim = 64; // 跳数限制

    // 设置源IPv6地址为本地链路地址,目标IPv6地址为指定地址
    if (inet_pton(AF_INET6, "fe80::1", &ip6->ip6_src) <= 0) {
        perror("inet_pton src_ip");
        free(eth);
        free(ip6);
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    if (inet_pton(AF_INET6, "fe80::328e:a3dc:3971:56b5", &ip6->ip6_dst) <= 0) {
        perror("inet_pton dst_ip");
        free(eth);
        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 = udp_checksum(ip6, udp, message, msg_len);
    if (udp->check == 0) {
        udp->check = 0xffff; // RFC 768规定,校验和为0时用0xffff代替
    }

    // 构建完整的数据包
    int packet_size = sizeof(struct ether_header) + sizeof(struct ip6_hdr) + sizeof(struct udphdr) + msg_len;
    char *packet = malloc(packet_size);
    memset(packet, 0, packet_size);

    // 复制各部分到数据包
    memcpy(packet, eth, sizeof(struct ether_header));
    memcpy(packet + sizeof(struct ether_header), ip6, sizeof(struct ip6_hdr));
    memcpy(packet + sizeof(struct ether_header) + sizeof(struct ip6_hdr), udp, sizeof(struct udphdr));
    memcpy(packet + sizeof(struct ether_header) + sizeof(struct ip6_hdr) + sizeof(struct udphdr), message, msg_len);

    // 发送数据包
    if (sendto(sockfd, packet, packet_size, 0, (struct sockaddr *)&sockaddr, sizeof(struct sockaddr_ll)) < 0) {
        perror("sendto");
        free(eth);
        free(ip6);
        free(udp);
        free(packet);
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    printf("Sent IPv6 UDP packet via ens33 to fe80::328e:a3dc:3971:56b5 %s\n", message);

    // 清理资源
    free(eth);
    free(ip6);
    free(udp);
    free(packet);
    close(sockfd);

    return 0;
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档