如何防止网络堆栈修改我的IP标头?

时间:2016-09-05 20:09:29

标签: c linux raw-sockets

我正在使用原始套接字发送UDP类型的IP数据包。

我的目标是发送DHCP请求。 RFC定义了尚未拥有IP的客户端的源IP地址必须设置为0。 所以,我正在做的是创建一个缓冲区,为IP Header,UDP Header和Payload保留足够的空间。 然后我修改IP标头并将源IP地址设置为0,如下所示:

   struct iphdr ip;

ip.version = 4;                               // Ipv4
ip.frag_off = 0;                              // IP Fragmentation
ip.tos     = 0;                               // Type of Service - We don't need this
ip.ttl = 255;                                 // Maxmimum value
ip.protocol = IPPROTO_UDP;                    // DHCP uses UDP
ip.check = 0;                                 // The checksum needs to be 0 before being calculated

// We don't yet have an IP Address
if((ip.saddr = inet_addr("0.0.0.0")) == -1) {
    perror("Failed to convert IP address");
    exit(1);
}

ip.daddr = htonl(INADDR_BROADCAST);           // we have to do a broadcast
ip.id = 489;                                  // The packets ID
ip.ihl = 5;                                   // Header length - 5 means no additional options are being sent in the IP header
ip.tot_len = sizeof(struct packet);           // The total length of the Packet

   // Secondly, create the udp header and fill out the information
struct udphdr udp;

udp.source = htons(DHCP_CLIENT_PORT);              // We are a client sending a request
udp.dest   = htons(DHCP_SERVER_PORT);              // We want to send the packet to a DHCP server
udp.check  = 0;                                    // zero the checksum until it's calculated

// The total length of the UDP packet is the size of the UDP header combined with the DHCP message struct
udp.len = htons(sizeof(struct udphdr) + sizeof(struct dhcp_message));

然后我执行我的DHCP并将所有结构复制到一个缓冲区中,如下所示:

    //Now, let's copy the structs inside our packet struct we want to return
struct packet *packet = (struct packet *)malloc(sizeof(struct packet));

memcpy(&packet->iph, &ip, sizeof(struct iphdr));
memcpy(&packet->udph, &udp, sizeof(struct udphdr));
memcpy(&packet->dhcpm, dhcp, sizeof(struct dhcp_message));

然后我的代码的最后一部分发送我创建的数据包:

struct packet *pckt = minidhc_make_packet(DHCP_DISCOVER, "enp7s0");

// Create socket
int sockd;
if((sockd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) {
    perror("Failed to create socket");
    exit(1);
}

// Enable broadcasting
int broadcast = 1;
if(setsockopt(sockd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast))) {
    perror("Failed to enable broadcasting on socket");
    exit(1);
}

int hdrincl=1;
if (setsockopt(sockd,IPPROTO_IP,IP_HDRINCL,&hdrincl,sizeof(hdrincl))==-1) {
    perror("Failed to set IP_HDRINCL");
    exit(1);
}


union sockunion {
    struct sockaddr sa;
    struct sockaddr_in sin;
};

union sockunion su;

su.sin.sin_family = AF_INET;
su.sin.sin_port = DHCP_SERVER_PORT;
su.sin.sin_addr.s_addr = htonl(INADDR_BROADCAST);

// Send to
int bytes;
if((bytes = sendto(sockd, (void *)pckt, sizeof(struct packet), 0, &su.sa, sizeof(struct sockaddr_in))) == -1) {
    perror("Failed to send DHCP request");
    exit(1);
}

printf("Bytes sent: %d\n", bytes);

printf("Sent raw packet\n");

free(pckt);

我的问题:使用Wireshark嗅探网络流量时,我可以捕获我发送的广播。这是一个有效的DHCP请求。但是,源IP地址是我的真实IP而不是0.0.0.0。

我已经尝试将源IP地址设置为某个任意一个(例如192.168.192.1),并且它的工作完全正常。 所以我的假设是,网络堆栈检查IP头并看到源IP地址的值为0,然后将我的真实IP添加到它。这只是一个假设,我不知道是否是这种情况。

我还尝试将IP头中的目标IP地址设置为与我将sendto作为参数的目标IP地址不同的地址,并且它采用IP头的目标IP。

如何防止网络堆栈更改我的代码? 我是否采取了错误的做法?这怎么可行?

1 个答案:

答案 0 :(得分:0)

  

所以我的假设是,网络堆栈检查IP头并看到源IP地址的值为0,然后将我的真实IP添加到它。

你的假设是对的。见raw(7)

如果源地址等于0.0.0.0(与INADDR_ANY相同)并且启用套接字选项IP_HDRINCL,则内核会自动填充源地址。

我认为除非你想修改和重新编译你的内核,否则无法改变这种行为。但是你应该能够通过创建自己的以太网帧来解决这个问题,或者至少添加一个有效的以太网头。

在尝试执行此操作之前,您应阅读相应的packet(7)手册页。概要部分显示了您的需求:

#include <sys/socket.h>
#include <linux/if_packet.h>
#include <net/ethernet.h> /* the L2 protocols */

packet_socket = socket(AF_PACKET, int socket_type, int protocol);

考虑到您的使用案例,您很可能会为htons(ETH_P_ALL)插入protocol。这应该是足够的信息开始。我相信你会在互联网上找到大量有关如何正确构建和发送帧的代码示例。处理ARP的源代码可能也是一个很好的参考,因为它发生在同一级别。

希望这有帮助。