在 Linux 上使用 C 的原始套接字 sendto 失败

发布于 2024-09-24 12:56:28 字数 7557 浏览 14 评论 0原文

我正在尝试使用 UDP 发送原始数据包,其中包含我在代码中构建的 IP 和 UDP 标头。使用 socket(PF_INET, SOCK_RAW, IPPROTO_UDP) 成功初始化原始数据包,并使用 setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(int)) 设置套接字选项。

问题是当我使用 sendto() 发送数据包时,我收到错误“消息太长”。

我的 IP 标头是 20 字节,UDP 标头是 8 字节,我的数据是 12 字节。所以总共只有40个字节。对于单个 UDP 数据包来说,这不可能太长。有人可以帮忙吗?

val 的类型是指向 int 的指针,

这是我的代码:

#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/udp.h>

#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>

#include <netinet/in.h>

//The packet length in byes
#define PCKT_LEN 50

//Date size in bytes
#define DATA_SIZE 12

//PseudoHeader struct used to calculate UDP checksum.
typedef struct PseudoHeader{    
    unsigned long int source_ip;
    unsigned long int dest_ip;
    unsigned char reserved;
    unsigned char protocol;
    unsigned short int udp_length;
}PseudoHeader;

// Ripped from Richard Stevans Book
unsigned short ComputeChecksum(unsigned char *data, int len)
{
    long sum = 0;  /* assume 32 bit long, 16 bit short */
    unsigned short *temp = (unsigned short *)data;

    while(len > 1){
        sum += *temp++;
        if(sum & 0x80000000)   /* if high order bit set, fold */
            sum = (sum & 0xFFFF) + (sum >> 16);
        len -= 2;
    }

    if(len)       /* take care of left over byte */
        sum += (unsigned short) *((unsigned char *)temp);

    while(sum>>16)
        sum = (sum & 0xFFFF) + (sum >> 16);

    return ~sum;
}

int BindRawSocketToInterface(int rawsock, char *addr, short int port)
{
    struct sockaddr_in s_addr;
    s_addr.sin_family = AF_INET;
    s_addr.sin_addr.s_addr = inet_addr(addr);
    s_addr.sin_port = htons(port);

    if((bind(rawsock, (struct sockaddr *)&s_addr, sizeof(s_addr)))== -1)
    {
        perror("Error binding raw socket to interface\n");
        exit(-1);
    }

    return 1;
}


// Fabricate the IP header or we can use the
// standard header structures but assign our own values.
struct ip *CreateIPHeader(char *srcip, char *destip)
{
    struct ip *ip_header;

    ip_header = (struct ip *)malloc(sizeof(struct ip));

    ip_header->ip_v = 4;
    ip_header->ip_hl = 5;
    ip_header->ip_tos = 0;
    ip_header->ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + DATA_SIZE);
    ip_header->ip_id = htons(111);
    ip_header->ip_off = 0;
    ip_header->ip_ttl = 111;
    ip_header->ip_p = IPPROTO_TCP;
    ip_header->ip_sum = 0; /* We will calculate the checksum later */
    inet_pton(AF_INET, srcip, &ip_header->ip_src);
    inet_pton(AF_INET, destip, &ip_header->ip_dst);

    /* Calculate the IP checksum now : 
    The IP Checksum is only over the IP header */
    ip_header->ip_sum = ComputeChecksum((unsigned char *)ip_header, ip_header->ip_hl*4);

    return (ip_header);
}

// Creates a the UDP header.
struct udphdr *CreateUdpHeader(char *srcport, char *destport )
{
    struct udphdr *udp_header;

    /* Check netinet/udp.h for header definiation */

    udp_header = (struct udphdr *)malloc(sizeof(struct udphdr));

    udp_header->source = htons(atoi(srcport));
    udp_header->dest = htons(atoi(destport));
    udp_header->len = htons(sizeof(struct udphdr) + DATA_SIZE); //TODO: need to specify this
    udp_header->check = htons(0);

    return (udp_header);
}

void CreatePseudoHeaderAndComputeUdpChecksum(struct udphdr *udp_header, struct ip *ip_header, unsigned char *data)
{
    /*The TCP Checksum is calculated over the PseudoHeader + TCP header +Data*/

    /* Find the size of the TCP Header + Data */
    int segment_len = ntohs(ip_header->ip_len) - ip_header->ip_hl*4; 

    /* Total length over which TCP checksum will be computed */
    int header_len = sizeof(PseudoHeader) + segment_len;

    /* Allocate the memory */

    unsigned char *hdr = (unsigned char *)malloc(header_len);

    /* Fill in the pseudo header first */

    PseudoHeader *pseudo_header = (PseudoHeader *)hdr;

    pseudo_header->source_ip = ip_header->ip_src.s_addr;
    pseudo_header->dest_ip = ip_header->ip_dst.s_addr;
    pseudo_header->reserved = 0;
    pseudo_header->protocol = ip_header->ip_p;
    pseudo_header->udp_length = htons(segment_len);


    /* Now copy TCP */

    memcpy((hdr + sizeof(PseudoHeader)), (void *)udp_header, 8);

    /* Now copy the Data */

    memcpy((hdr + sizeof(PseudoHeader) + 8), data, DATA_SIZE);

    /* Calculate the Checksum */

    udp_header->check = ComputeChecksum(hdr, header_len);

    /* Free the PseudoHeader */
    free(hdr);
}

// Source IP, source port, target IP, target port from the command line arguments
int main(int argc, char *argv[])
{
    int sd;
    char buffer[PCKT_LEN];
    char *data = "Hello World!";

    // Source and destination addresses: IP and port
    struct sockaddr_in to_addr;
    int one = 1;
    const int *val = &one;

    printf("IP Header Size: %lu \n", sizeof(struct ip));
    printf("UDP Header Size: %lu \n", sizeof(struct udphdr));
    printf("Data Size: %d\n", DATA_SIZE);
    printf("IP Total: %lu \n", sizeof(struct ip) + sizeof(struct udphdr) + DATA_SIZE);

    memset(buffer, 0, PCKT_LEN);

    if(argc != 5)
    {
        printf("- Invalid parameters!!!\n");
        printf("- Usage %s <source hostname/IP> <source port> <target hostname/IP> <target port>\n", argv[0]);
        exit(-1);
    }

    // Create a raw socket with UDP protocol
    sd = socket(PF_INET, SOCK_RAW, IPPROTO_UDP);
    if(sd < 0)
    {
        perror("socket() error");
        exit(-1);
    }
    else
        printf("socket() - Using SOCK_RAW socket and UDP protocol is OK.\n");

    //Bind the socket to the source address and port.
    BindRawSocketToInterface(sd, argv[1], atoi(argv[2]));

    // Inform the kernel do not fill up the packet structure. we will build our own...
    if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(int)) < 0)
    {
        perror("setsockopt() error");
        close(sd);
        exit(-1);
    }
    else
        printf("setsockopt() is OK.\n");


    // The source is redundant, may be used later if needed
    // The address family
    to_addr.sin_family = AF_INET;
    to_addr.sin_addr.s_addr = inet_addr(argv[3]);
    to_addr.sin_port = htons(atoi(argv[4]));

    //Create the IP header.
    struct ip *ip_header = CreateIPHeader(argv[1], argv[3]);
    //Create the UDP header.
    struct udphdr *udp_header = CreateUdpHeader(argv[2], argv[4]);
    //Compute UDP checksum
    CreatePseudoHeaderAndComputeUdpChecksum(udp_header, ip_header, (unsigned char*)data);

    //Copy IP header, UDP header, and data to the packet buffer.
    memcpy(buffer, ip_header, sizeof(struct ip));
    memcpy(buffer + sizeof(struct ip), udp_header, 8 /*sizeof(struct udphdr)*/);
    memcpy(buffer + sizeof(struct ip) + 8, data, DATA_SIZE);

    printf("Using raw socket and UDP protocol\n");
    printf("Using Source IP: %s port: %u, Target IP: %s port: %u.\n", argv[1], atoi(argv[2]), argv[3], atoi(argv[4]));


    if(sendto(sd, buffer, htons(20)/*ip_header->ip_len*/, 0, (struct sockaddr *)&to_addr, sizeof(to_addr)) < 0)
    {
        perror("sendto() error");
    }
    else
    {
        printf("sendto() is OK.\n");
    }

    free(ip_header);
    free(udp_header);
    close(sd);
    return 0;
}

I'm trying to send a raw packet using UDP, with the IP and UDP headers that I have constructed in my code. Raw packet successfully initialized with socket(PF_INET, SOCK_RAW, IPPROTO_UDP) and socket option set using setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(int)).

The problem is when I send the packet using sendto(), I get the error 'Message too long'.

My IP header is 20 bytes and UDP header 8 bytes and my data is 12 bytes. So the total is only 40 bytes. This can't possibly be too long for a single UDP packet. Can someone help?

The type of val is a pointer to an int

Here is my code:

#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/udp.h>

#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>

#include <netinet/in.h>

//The packet length in byes
#define PCKT_LEN 50

//Date size in bytes
#define DATA_SIZE 12

//PseudoHeader struct used to calculate UDP checksum.
typedef struct PseudoHeader{    
    unsigned long int source_ip;
    unsigned long int dest_ip;
    unsigned char reserved;
    unsigned char protocol;
    unsigned short int udp_length;
}PseudoHeader;

// Ripped from Richard Stevans Book
unsigned short ComputeChecksum(unsigned char *data, int len)
{
    long sum = 0;  /* assume 32 bit long, 16 bit short */
    unsigned short *temp = (unsigned short *)data;

    while(len > 1){
        sum += *temp++;
        if(sum & 0x80000000)   /* if high order bit set, fold */
            sum = (sum & 0xFFFF) + (sum >> 16);
        len -= 2;
    }

    if(len)       /* take care of left over byte */
        sum += (unsigned short) *((unsigned char *)temp);

    while(sum>>16)
        sum = (sum & 0xFFFF) + (sum >> 16);

    return ~sum;
}

int BindRawSocketToInterface(int rawsock, char *addr, short int port)
{
    struct sockaddr_in s_addr;
    s_addr.sin_family = AF_INET;
    s_addr.sin_addr.s_addr = inet_addr(addr);
    s_addr.sin_port = htons(port);

    if((bind(rawsock, (struct sockaddr *)&s_addr, sizeof(s_addr)))== -1)
    {
        perror("Error binding raw socket to interface\n");
        exit(-1);
    }

    return 1;
}


// Fabricate the IP header or we can use the
// standard header structures but assign our own values.
struct ip *CreateIPHeader(char *srcip, char *destip)
{
    struct ip *ip_header;

    ip_header = (struct ip *)malloc(sizeof(struct ip));

    ip_header->ip_v = 4;
    ip_header->ip_hl = 5;
    ip_header->ip_tos = 0;
    ip_header->ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + DATA_SIZE);
    ip_header->ip_id = htons(111);
    ip_header->ip_off = 0;
    ip_header->ip_ttl = 111;
    ip_header->ip_p = IPPROTO_TCP;
    ip_header->ip_sum = 0; /* We will calculate the checksum later */
    inet_pton(AF_INET, srcip, &ip_header->ip_src);
    inet_pton(AF_INET, destip, &ip_header->ip_dst);

    /* Calculate the IP checksum now : 
    The IP Checksum is only over the IP header */
    ip_header->ip_sum = ComputeChecksum((unsigned char *)ip_header, ip_header->ip_hl*4);

    return (ip_header);
}

// Creates a the UDP header.
struct udphdr *CreateUdpHeader(char *srcport, char *destport )
{
    struct udphdr *udp_header;

    /* Check netinet/udp.h for header definiation */

    udp_header = (struct udphdr *)malloc(sizeof(struct udphdr));

    udp_header->source = htons(atoi(srcport));
    udp_header->dest = htons(atoi(destport));
    udp_header->len = htons(sizeof(struct udphdr) + DATA_SIZE); //TODO: need to specify this
    udp_header->check = htons(0);

    return (udp_header);
}

void CreatePseudoHeaderAndComputeUdpChecksum(struct udphdr *udp_header, struct ip *ip_header, unsigned char *data)
{
    /*The TCP Checksum is calculated over the PseudoHeader + TCP header +Data*/

    /* Find the size of the TCP Header + Data */
    int segment_len = ntohs(ip_header->ip_len) - ip_header->ip_hl*4; 

    /* Total length over which TCP checksum will be computed */
    int header_len = sizeof(PseudoHeader) + segment_len;

    /* Allocate the memory */

    unsigned char *hdr = (unsigned char *)malloc(header_len);

    /* Fill in the pseudo header first */

    PseudoHeader *pseudo_header = (PseudoHeader *)hdr;

    pseudo_header->source_ip = ip_header->ip_src.s_addr;
    pseudo_header->dest_ip = ip_header->ip_dst.s_addr;
    pseudo_header->reserved = 0;
    pseudo_header->protocol = ip_header->ip_p;
    pseudo_header->udp_length = htons(segment_len);


    /* Now copy TCP */

    memcpy((hdr + sizeof(PseudoHeader)), (void *)udp_header, 8);

    /* Now copy the Data */

    memcpy((hdr + sizeof(PseudoHeader) + 8), data, DATA_SIZE);

    /* Calculate the Checksum */

    udp_header->check = ComputeChecksum(hdr, header_len);

    /* Free the PseudoHeader */
    free(hdr);
}

// Source IP, source port, target IP, target port from the command line arguments
int main(int argc, char *argv[])
{
    int sd;
    char buffer[PCKT_LEN];
    char *data = "Hello World!";

    // Source and destination addresses: IP and port
    struct sockaddr_in to_addr;
    int one = 1;
    const int *val = &one;

    printf("IP Header Size: %lu \n", sizeof(struct ip));
    printf("UDP Header Size: %lu \n", sizeof(struct udphdr));
    printf("Data Size: %d\n", DATA_SIZE);
    printf("IP Total: %lu \n", sizeof(struct ip) + sizeof(struct udphdr) + DATA_SIZE);

    memset(buffer, 0, PCKT_LEN);

    if(argc != 5)
    {
        printf("- Invalid parameters!!!\n");
        printf("- Usage %s <source hostname/IP> <source port> <target hostname/IP> <target port>\n", argv[0]);
        exit(-1);
    }

    // Create a raw socket with UDP protocol
    sd = socket(PF_INET, SOCK_RAW, IPPROTO_UDP);
    if(sd < 0)
    {
        perror("socket() error");
        exit(-1);
    }
    else
        printf("socket() - Using SOCK_RAW socket and UDP protocol is OK.\n");

    //Bind the socket to the source address and port.
    BindRawSocketToInterface(sd, argv[1], atoi(argv[2]));

    // Inform the kernel do not fill up the packet structure. we will build our own...
    if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(int)) < 0)
    {
        perror("setsockopt() error");
        close(sd);
        exit(-1);
    }
    else
        printf("setsockopt() is OK.\n");


    // The source is redundant, may be used later if needed
    // The address family
    to_addr.sin_family = AF_INET;
    to_addr.sin_addr.s_addr = inet_addr(argv[3]);
    to_addr.sin_port = htons(atoi(argv[4]));

    //Create the IP header.
    struct ip *ip_header = CreateIPHeader(argv[1], argv[3]);
    //Create the UDP header.
    struct udphdr *udp_header = CreateUdpHeader(argv[2], argv[4]);
    //Compute UDP checksum
    CreatePseudoHeaderAndComputeUdpChecksum(udp_header, ip_header, (unsigned char*)data);

    //Copy IP header, UDP header, and data to the packet buffer.
    memcpy(buffer, ip_header, sizeof(struct ip));
    memcpy(buffer + sizeof(struct ip), udp_header, 8 /*sizeof(struct udphdr)*/);
    memcpy(buffer + sizeof(struct ip) + 8, data, DATA_SIZE);

    printf("Using raw socket and UDP protocol\n");
    printf("Using Source IP: %s port: %u, Target IP: %s port: %u.\n", argv[1], atoi(argv[2]), argv[3], atoi(argv[4]));


    if(sendto(sd, buffer, htons(20)/*ip_header->ip_len*/, 0, (struct sockaddr *)&to_addr, sizeof(to_addr)) < 0)
    {
        perror("sendto() error");
    }
    else
    {
        printf("sendto() is OK.\n");
    }

    free(ip_header);
    free(udp_header);
    close(sd);
    return 0;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

心舞飞扬 2024-10-01 12:56:28
if(sendto(sd, buffer, htons(20)/*ip_header->ip_len*/, 0, (struct sockaddr *)&to_addr, sizeof(to_addr)) < 0)

这里的长度是错误的,你不应该以网络字节顺序指定它。

简单的20个就够了。 htons(20) 在小端机器上将是一个非常大的数字。
(如果你想发送除 IP 标头之外的其他内容,你也应该将其包含在长度中,听起来你的缓冲区是 40 字节,而不是 20)

if(sendto(sd, buffer, htons(20)/*ip_header->ip_len*/, 0, (struct sockaddr *)&to_addr, sizeof(to_addr)) < 0)

The length here is wrong, you should not specify that in network byte order.

A simple 20 is enough. htons(20) will be a very large number on a little endian machine.
( if you want to send other things that just the IP header, you should include that too in the length,, sounds like your buffer is 40 bytes, not 20)

一笑百媚生 2024-10-01 12:56:28

您可以使用我自己的功能,我没有您的问题:

    #define NDEBUG
#define COUNTMAX 3000
#define   MAX_FAKE 100
#define   ERROR    -1
#define   TYPE_A   1
#define   TYPE_PTR 12
#define   CLASS_INET 1
#define ERROR -1

#ifndef IPVERSION
  #define IPVERSION 4
#endif                  /* IPVERISON */

#ifndef IP_MAXPACKET
  #define IP_MAXPACKET 65535
#endif                  /* IP_MAXPACKET */

#define DNSHDRSIZE 12

#ifndef IP_MAXPACKET
#define IP_MAXPACKET 65535
#endif                   /* IP_MAXPACKET */

#define   IPHDRSIZE     sizeof(struct iphdr)
#define   UDPHDRSIZE    sizeof(struct udphdr)
    int udp_send(int s, unsigned long saddr, unsigned long daddr,unsigned short sport,unsigned short dport,char *datagram, unsigned datasize) 
{
    struct sockaddr_in sin;
    struct   iphdr   *ip;
    struct   udphdr  *udp;
    unsigned char    *data;
    unsigned char    packet[4024];


    ip     = (struct iphdr     *)packet; 
    udp    = (struct udphdr    *)(packet+IPHDRSIZE);
    data   = (unsigned char    *)(packet+IPHDRSIZE+UDPHDRSIZE);

    memset(packet,0,sizeof(packet));

    udp->source  = htons(sport); 
    udp->dest    = htons(dport);
    udp->len     = htons(UDPHDRSIZE+datasize);
    udp->check   = 0;         

    memcpy(data,datagram,datasize);

    ip->saddr.s_addr  = saddr;
    ip->daddr.s_addr  = daddr;
    ip->version  = 4;             /*ip version*/
    ip->ihl      = 5;
    ip->ttl      = 245;
    ip->id       = random()%5985;
    ip->protocol = IPPROTO_UDP;   /*protocol type*/
    ip->tot_len  = htons((IPHDRSIZE + UDPHDRSIZE + datasize));
    ip->check    = 0;
    ip->check    = in_cksum((char *)packet,IPHDRSIZE);

    sin.sin_family=AF_INET;
    sin.sin_addr.s_addr=daddr;
    sin.sin_port=udp->dest;
        printf ("socket: %d, packet: %s,size: %d, struct addr: %p, size: %i", s, packet, IPHDRSIZE+UDPHDRSIZE+datasize, (struct sockaddr*)&sin, sizeof(struct sockaddr));
    return sendto(s, packet, IPHDRSIZE+UDPHDRSIZE+datasize, 0, (struct sockaddr*)&sin, sizeof(struct sockaddr));
}// end of udp_send function

You can use my own function that i don't have your problem:

    #define NDEBUG
#define COUNTMAX 3000
#define   MAX_FAKE 100
#define   ERROR    -1
#define   TYPE_A   1
#define   TYPE_PTR 12
#define   CLASS_INET 1
#define ERROR -1

#ifndef IPVERSION
  #define IPVERSION 4
#endif                  /* IPVERISON */

#ifndef IP_MAXPACKET
  #define IP_MAXPACKET 65535
#endif                  /* IP_MAXPACKET */

#define DNSHDRSIZE 12

#ifndef IP_MAXPACKET
#define IP_MAXPACKET 65535
#endif                   /* IP_MAXPACKET */

#define   IPHDRSIZE     sizeof(struct iphdr)
#define   UDPHDRSIZE    sizeof(struct udphdr)
    int udp_send(int s, unsigned long saddr, unsigned long daddr,unsigned short sport,unsigned short dport,char *datagram, unsigned datasize) 
{
    struct sockaddr_in sin;
    struct   iphdr   *ip;
    struct   udphdr  *udp;
    unsigned char    *data;
    unsigned char    packet[4024];


    ip     = (struct iphdr     *)packet; 
    udp    = (struct udphdr    *)(packet+IPHDRSIZE);
    data   = (unsigned char    *)(packet+IPHDRSIZE+UDPHDRSIZE);

    memset(packet,0,sizeof(packet));

    udp->source  = htons(sport); 
    udp->dest    = htons(dport);
    udp->len     = htons(UDPHDRSIZE+datasize);
    udp->check   = 0;         

    memcpy(data,datagram,datasize);

    ip->saddr.s_addr  = saddr;
    ip->daddr.s_addr  = daddr;
    ip->version  = 4;             /*ip version*/
    ip->ihl      = 5;
    ip->ttl      = 245;
    ip->id       = random()%5985;
    ip->protocol = IPPROTO_UDP;   /*protocol type*/
    ip->tot_len  = htons((IPHDRSIZE + UDPHDRSIZE + datasize));
    ip->check    = 0;
    ip->check    = in_cksum((char *)packet,IPHDRSIZE);

    sin.sin_family=AF_INET;
    sin.sin_addr.s_addr=daddr;
    sin.sin_port=udp->dest;
        printf ("socket: %d, packet: %s,size: %d, struct addr: %p, size: %i", s, packet, IPHDRSIZE+UDPHDRSIZE+datasize, (struct sockaddr*)&sin, sizeof(struct sockaddr));
    return sendto(s, packet, IPHDRSIZE+UDPHDRSIZE+datasize, 0, (struct sockaddr*)&sin, sizeof(struct sockaddr));
}// end of udp_send function
故笙诉离歌 2024-10-01 12:56:28

对 sendto() 的调用似乎并不如图所示正确,甚至根据评论进行了更正:
原文:sendto(sd, buffer, htons(20)/ip_header->ip_len/,...);
Orr 已更正:sendto(sd, buffer, 20/ip_header->ip_len/,...);
数据长度参数应该是标头和数据长度的总和:20+8+12 = 40。一般解决方案可以是:
sendto(sd, 缓冲区, sizeof(struct ip) + sizeof(struct udphdr) + DATA_SIZE,...);
有了这个改变,它开始为我工作。

The call to sendto() doesn't appear to be right as shown, even corrected per the comments:
Original: sendto(sd, buffer, htons(20)/ip_header->ip_len/,...);
Orr as corrected: sendto(sd, buffer, 20/ip_header->ip_len/,...);
The data length parameter should be the sum of header and data lengths: 20+8+12 = 40. A general solution could be:
sendto(sd, buffer, sizeof(struct ip) + sizeof(struct udphdr) + DATA_SIZE,...);
With this change it started working for me.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文