尽管RAW套接字和HDRINCL,IP源地址仍然由系统填充

时间:2015-05-08 07:08:17

标签: c sockets ip dhcp raw-sockets

我正在编写简单的DHCP客户端,因此,根据RFC 951,我需要发送IP源地址=" 0.0.0.0"的数据包。我知道可以使用RAW套接字,但显然我的代码中有些东西是错误的 - 源地址总是由内核填充(到其他接口的定义地址)。

   if (sockfd==0)
    {
        sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
        if (sockfd<0)
        {
            return result.setError(-1, "Can't create socket.");
        }
        status = setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, m_ifName, strlen(m_ifName));
        if (status<0)
        {
            return result.setError(status, "Can't bind socket to the interface.");
        }

        int optVal = 1;
        status = setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &optVal, sizeof(optVal));
        if (status != 0)
        {
            return result.setError(status, "Can't set IP_HDRINCL option on a socket");
        }

        int broadcastVal = 1;
        status = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST,
                            reinterpret_cast<const void*>(&broadcastVal), sizeof(broadcastVal));
        if (status!=0)
        {
            return result.setError(status, "Can't set a broadcast option on a socket.");
        }

        int reuseAddrVal = 1;
        status = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuseAddrVal, sizeof(reuseAddrVal));
        if(status != 0)
            return result.setError(status, "Can't set a SO_REUSEADDR opt on socket.");

        struct sockaddr_in src_addr;
        src_addr.sin_family = AF_INET;
        src_addr.sin_addr.s_addr = inet_addr("0.0.0.0");
        src_addr.sin_port = htons(sourcePort);
        status = bind(sockfd, reinterpret_cast<sockaddr *>(&src_addr), sizeof(sockaddr_in));
        if (status != 0)
        {
            return result.setError(status, "Can't bind to the socket.");
        }
    }

    unsigned char buffer[8192];
    struct sockaddr_in dest_addr;
    char* packetData = (char*)(buffer + sizeof(struct ip) + sizeof(struct udphdr));
    struct ip* ip_header = (struct ip*) buffer;
    struct udphdr* udp_header = (struct udphdr*) (buffer + sizeof(struct ip));

    memset(buffer, 0, sizeof(buffer));
    memset(&dest_addr, 0, sizeof(dest_addr));

    dest_addr.sin_family = AF_INET;
    dest_addr.sin_addr.s_addr = INADDR_BROADCAST;
    dest_addr.sin_port = htons(67);

    ip_header->ip_v = 4;
    ip_header->ip_hl = 5;
    ip_header->ip_tos = 0;
    ip_header->ip_id = 0;
    ip_header->ip_ttl = 63;
    ip_header->ip_p = IPPROTO_UDP;
    ip_header->ip_off = 0;
    ip_header->ip_sum = 0;
    ip_header->ip_src.s_addr = inet_addr("0.0.0.0");
    ip_header->ip_dst.s_addr = htonl(INADDR_BROADCAST);

    udp_header->source = htons(68);
    udp_header->dest = htons(67);
    udp_header->check = htons(0);

    DhcpData data(sizeof(DhcpHdr), 0);
    prepareDhcpPacket(type, data, result);
    if (result.errCode != 0)
    {
        return result;
    }
    strcpy(packetData, (const char*) data.constData());

    ip_header->ip_len = htons(sizeof(struct iphdr) + sizeof(struct udphdr) + data.size());
    udp_header->len = htons(sizeof(struct udphdr) + data.size());

    ip_header->ip_sum = ComputeChecksum((unsigned char *)ip_header, ip_header->ip_hl*4);

    int segment_len = (sizeof(struct iphdr) + sizeof(struct udphdr) + data.size()) - ip_header->ip_hl*4;
    int header_len = sizeof(PseudoHeader) + segment_len;

    unsigned char* hdr = (unsigned char *)malloc(header_len);
    PseudoHeader* pseudo_header;
    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);

    memcpy((hdr + sizeof(PseudoHeader)), (void *)udp_header, 8);
    memcpy((hdr + sizeof(PseudoHeader) + 8), packetData, data.size());
    udp_header->check = ComputeChecksum(hdr, header_len);

    free(hdr);

    int res = sendto(sockfd, buffer, (sizeof(struct ip) + sizeof(struct udphdr) + data.size()), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    if(res < 0)
    {
        printf("Errno=%s", strerror(errno));
    }

/* taken from TCP/IP Illustrated Vol. 2(1995) by Gary R. Wright and W. Richard Stevens. Page 236 */
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;
}

1 个答案:

答案 0 :(得分:1)

Reptile

不允许更改0.0.0.0上的IP,请参阅man:raw(7)

  

来源地址│零时填写

(0.0.0.0)是zerro

您必须使用:

sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);

是的,您必须再生成14个字节,现在是以太网标头

简单示例请参阅:simple DHCP client