首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >网络层(L3)原始套接字,IPv4,UDP发送数据包

网络层(L3)原始套接字,IPv4,UDP发送数据包

原创
作者头像
胡椒粉
发布2025-08-29 13:47:18
发布2025-08-29 13:47:18
1170
举报
文章被收录于专栏: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/ip.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;
}

// 计算UDP校验和
unsigned short
udp_checksum(struct iphdr* ip, struct udphdr* udp, char* data, int data_len)
{
    struct pseudo_header
    {
        u_int32_t source_address;
        u_int32_t dest_address;
        u_int8_t placeholder;
        u_int8_t protocol;
        u_int16_t udp_length;
    } pseudo_header;

    pseudo_header.source_address = ip->saddr;
    pseudo_header.dest_address = ip->daddr;
    pseudo_header.placeholder = 0;
    pseudo_header.protocol = IPPROTO_UDP;
    pseudo_header.udp_length = htons(sizeof(struct udphdr) + data_len);

    int psize = sizeof(struct pseudo_header) + sizeof(struct udphdr) + data_len;
    char* pseudogram = malloc(psize);

    memcpy(pseudogram, &pseudo_header, sizeof(struct pseudo_header));
    memcpy(pseudogram + sizeof(struct pseudo_header), udp, sizeof(struct udphdr));
    memcpy(pseudogram + sizeof(struct pseudo_header) + 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>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

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

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

    // 设置IP_HDRINCL选项,我们自己构建IP头部
    int one = 1;
    if (setsockopt(sockfd, IPPROTO_IP, 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_in dest_addr;
    memset(&dest_addr, 0, sizeof(dest_addr));
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(12345); // 目标端口
    if (inet_pton(AF_INET, "192.168.243.1", &dest_addr.sin_addr) <= 0) {
        perror("inet_pton dest_addr");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    // 准备IP头部
    struct iphdr* ip = (struct iphdr*)malloc(sizeof(struct iphdr));
    memset(ip, 0, sizeof(struct iphdr));
    ip->ihl = 5;                                                                 // IP头部长度(5 * 4 = 20字节)
    ip->version = 4;                                                             // IPv4
    ip->tos = 0;                                                                 // 服务类型
    ip->tot_len = htons(sizeof(struct iphdr) + sizeof(struct udphdr) + msg_len); // 总长度
    ip->id = htonl(54321);                                                       // 标识符
    ip->frag_off = 0;                                                            // 分片偏移
    ip->ttl = 64;                                                                // 生存时间
    ip->protocol = IPPROTO_UDP;                                                  // 上层协议为UDP
    ip->check = 0;                                                               // 先设为0,后面计算校验和

    // 设置源IP地址
    if (inet_pton(AF_INET, "10.88.243.156", &ip->saddr) <= 0) {
        perror("inet_pton src_ip");
        free(ip);
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    // 设置目标IP地址
    if (inet_pton(AF_INET, "192.168.243.1", &ip->daddr) <= 0) {
        perror("inet_pton dest_ip");
        free(ip);
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    // 计算IP校验和
    ip->check = checksum((unsigned short*)ip, sizeof(struct iphdr));

    // 准备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(ip, udp, message, msg_len);
    if (udp->check == 0) {
        udp->check = 0xffff; // RFC 768规定,校验和为0时用0xffff代替
    }

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

    // 复制各部分到数据包
    memcpy(packet, ip, sizeof(struct iphdr));
    memcpy(packet + sizeof(struct iphdr), udp, sizeof(struct udphdr));
    memcpy(packet + sizeof(struct iphdr) + sizeof(struct udphdr), message, msg_len);

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

    printf("Sent IPv4 UDP packet from 192.168.243.2:54321 to 192.168.243.1:12345 via ens37: %s\n", message);

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

    return 0;
}

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

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

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

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

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