为什么recvfrom会创建额外的IP头?

时间:2015-10-22 18:42:08

标签: c sockets ip raw-sockets recvfrom

为什么recvfrom会创建额外的IP标头?

我发送:

-------------
- IP header -
-------------
-   Data   -
-------------

但是当我尝试接收数据时,看起来似乎有

-------------
- IP header -
-------------
- IP header -
-------------    
-   Data   -
-------------

客户代码:

#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <netinet/ip.h>

#define DEBUG 1


int main(void) {

    // Create File descrptor for socket
    int socket_fd;
    if ((socket_fd = socket(AF_INET, SOCK_RAW, IPPROTO_IPV6)) < 0) {
        perror("ERROR Socket");
        exit(2);
    }

    char *srcIP = "127.0.0.1";
    char *dstIP = "127.0.0.1";

    // Create address structure
    struct sockaddr_in daddr;
    daddr.sin_family = AF_INET;
    daddr.sin_port = 0; // Raw sockets can't use ports
    inet_pton(AF_INET, "127.0.0.1", &daddr.sin_addr);
    memset(daddr.sin_zero, 0, sizeof(daddr.sin_zero));

    // Create a Packet
    char packet[50];
    memset(packet, 0, sizeof(packet));

    // Structure packet
    struct iphdr *ip_header = (struct iphdr *) packet;
    // Data to be appended at the end of the ip header
    char *data = (char *) (packet + (sizeof(struct iphdr)));
    ip_header->version = 4; // IPv4
    ip_header->tos = 0; // Type of service
    ip_header->ihl = 5; // 5 x 32-bit words
//    ip_header->tot_len = htons(sizeof(struct iphdr) + strlen(data)); // Total IP packet length
    ip_header->tot_len = sizeof(packet); // Total IP packet length
    ip_header->protocol = IPPROTO_IPV6; // 6in4 protocol
    ip_header->frag_off = 0x00; //16 bit field = [0:2] flags + [3:15] offset = 0x0
    ip_header->ttl = 0xFF; // Max number of hops 16bit
//    ip_header->id = htons(54321); // 0x00; //16 bit id
    ip_header->check = 0;  //16 bit checksum of IP header. Can't calculate at this point
    ip_header->saddr = inet_addr(srcIP);
    ip_header->daddr = inet_addr(dstIP);
    data[0] = 'T';
    data[1] = 'E';
    data[2] = 'S';
    data[3] = 'T';
    data[4] = '7';
    data[5] = '\0';

//    ip_header->check = csum((unsigned short *) packet, ip_header->tot_len);
#if DEBUG
    printf("\nIP header checksum: %d\n", ip_header->check);
#endif

    while (1) {
        sleep(1);
        if (sendto(socket_fd, (char *) packet, sizeof(packet), 0,
                   (struct sockaddr *) &daddr, (socklen_t) sizeof(daddr)) < 0)
            perror("Packet send error");
    }

    return 0;
}

服务器代码:

/*** IPPROTO_RAW receiver ***/
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int socket_fd;
    struct sockaddr_in saddr;
    char packet[50];

    if ((socket_fd = socket(AF_INET, SOCK_RAW, IPPROTO_IPV6)) < 0) {
        perror("socket_fd");
        exit(EXIT_FAILURE);
    }

    memset(packet, 0, sizeof(packet));
    socklen_t len = (socklen_t) sizeof(saddr);

    while(1) {
        if (recvfrom(socket_fd, packet, sizeof(packet), 0,
                     (struct sockaddr *)&saddr, &len) < 0)
            perror("recvfrom");

        int i = sizeof(struct iphdr);   ////// WHY IS DATA PAYLOAD ON packet[sizeof(struct iphdr) * 2]
        while (i < sizeof(packet)) {
            fprintf(stderr, "%c", packet[i]);
            i++;
        }
        printf("\n");
    }
    exit(EXIT_SUCCESS);
}

2 个答案:

答案 0 :(得分:0)

在发件人中,您需要设置IP_HDRINCL套接字选项。这告诉API你手动提供IP头。由于您未设置此选项,因此您可以在系统添加后有效地放置自己的IP标头副本。

答案 1 :(得分:0)

IP_HDRINCL套接字选项通常仅在使用IPPROTO_RAW的情况下隐式设置。如果您没有使用IPPROTO_RAW,那么将生成标头,因为不会隐式设置IP_HDRINCL。

请参阅man7.org/linux/man-pages/man7/raw.7.html