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

C 如何防止网络堆栈修改我的IP头?,c,linux,raw-sockets,C,Linux,Raw Sockets,我使用原始套接字发送UDP类型的IP数据包 我的目标是发送DHCP请求。RFC定义,尚未具有IP的客户端的源IP地址必须设置为0。 所以,我要做的是创建一个缓冲区,为IP报头、UDP报头和有效负载保留足够的空间。 然后修改IP头并将源IP地址设置为0,如下所示: struct iphdr ip; ip.version = 4; // Ipv4 ip.frag_off = 0;

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

我的目标是发送DHCP请求。RFC定义,尚未具有IP的客户端的源IP地址必须设置为0。 所以,我要做的是创建一个缓冲区,为IP报头、UDP报头和有效负载保留足够的空间。 然后修改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));
    //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));
然后我执行DHCP操作并将所有结构复制到一个缓冲区中,如下所示:

   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));
    //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报头的destination IP

如何防止网络堆栈更改代码? 我是不是走错了路?这怎么可能

因此,我的假设是,网络堆栈检查IP头并看到源IP地址的值为0,然后将我的真实IP添加到其中

你的假设是对的。见:

如果源地址等于
0.0.0.0
(与
INADDR\u ANY
)并且启用了套接字选项
IP\u HDRINCL
,则内核将自动填充源地址

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

在尝试这样做之前,您应该阅读相应的手册页。大纲部分显示了您需要的内容:

#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);
#包括
#包括
#包括/*L2协议*/
数据包\u套接字=套接字(AF\u数据包,int套接字\u类型,int协议);
考虑到您的用例,您很可能会为
协议插入
hton(ETH\u p\u ALL)
。这应该是足够的信息开始。我相信你会在互联网上找到大量关于如何正确构造和发送帧的代码示例。处理ARP的源代码可能也是一个很好的参考,因为它发生在同一级别

希望这有帮助