2010-09-17 36 views
1

我试图发送一个原始数据包使用UDP,以及在我的代码中构建的IP和UDP标头。原始数据包成功初始化为socket(PF_INET, SOCK_RAW, IPPROTO_UDP),套接字选项使用setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(int))设置。在Linux上使用C的原始套接字sendto失败

问题是当我使用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; 
} 
+1

显示这样做的代码(并且在你的setsockopt中,val的类型是什么? – nos 2010-09-17 17:44:09

回答

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

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

一个简单的20就足够了。 htons(20)将是一个很小的endian机器上的一个非常大的数字。 (如果你想发送其他东西,只是IP头,你应该在长度中包含这个,听起来像你的缓冲区是40字节,而不是20)

+0

nos,谢谢你的解决方案。sendto现在可以返回,我相信现在通过网络发送数据包。现在遇到另一个问题,即在目标主机(我的服务器)没有收到数据包。你认为它可能与我的UDP数据报格式有什么不对吗? – Jeff 2010-09-20 15:15:12

+0

使用wireshark进行验证,通常很好地指出数据包中的错误。校验和可能是一个罪魁祸首,确保它是正确的。你正在设置IPPROTO_TCP,不应该是IPPROTO_UDP? – nos 2010-09-20 16:07:35

+0

我通过Wireshark检查数据包时,我想到了将IPPROTO_TCP更改为IPPROTO_UDP和数据包。 ,但你只是为我节省了几个小时的工作,谢谢你的帮助! – Jeff 2010-09-20 16:33:34

0

你可以使用我自己的函数,吨有你的问题:

#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 
0

到的sendto()的调用并不显得如右图所示,按照意见,甚至更正:
原文:SENDTO(SD,缓冲,htons(20)/ ip_header-> ip_len /,...); Orr as corrected:sendto(sd,buffer,20/ip_header-> ip_len /,...); 数据长度参数应该是头和数据长度的总和:20 + 8 + 12 = 40。一般的解决方案可以是: sendto(sd,buffer,sizeof(struct ip)+ sizeof(struct udphdr)+ DATA_SIZE, ...); 有了这个改变,它开始为我工作。

相关问题