为什么我的路由器从我的原始套接字应用程序中丢弃数据包?

Why is my router dropping packets from my raw sockets application?

我有一个应用程序从 PF_PACKET 套接字发送手工制作的 SOCK_RAW 数据包。正在创建和发送数据包,如 Wireshark 的屏幕截图所示。正在发送的数据包是 TCP SYN 个具有预期 TCP SYN/ACK 响应的数据包。但是,再次如屏幕截图所示,没有收到任何响应。我假设这是因为路由器出于某种原因正在丢弃数据包。任何想法可能是什么原因?还是有其他原因导致我没有收到任何回复。

完整代码很长,因为获取 IP 地址和路由器的 MAC 地址以构建以太网 header 需要大量代码。所以我只包含了最相关的代码。如果这还不够,请发表评论,我将 post 完整代码。

fd_socket[z] = socket(PF_PACKET, SOCK_RAW|SOCK_NONBLOCK, htons(ETH_P_ALL));
        if(fd_socket[z] == -1)
        {
            perror("socket");
            return EXIT_FAILURE;
        }
    
        /* clear structure */
        memset(&my_addr[z], 0, sizeof(struct sockaddr_ll));
        my_addr[z].sll_family = PF_PACKET;
        my_addr[z].sll_protocol = htons(ETH_P_ALL);
    
        str_devname = ifname;
        //strcpy (str_devname, ifname);
        
        /* initialize interface struct */
        strncpy (s_ifr.ifr_name, str_devname, sizeof(s_ifr.ifr_name));
    
        /* Get the broad cast address */
        ec = ioctl(fd_socket[z], SIOCGIFINDEX, &s_ifr);
        if(ec == -1)
        {
            perror("iotcl");
            return EXIT_FAILURE;
        }
    
        /* update with interface index */
        i_ifindex = s_ifr.ifr_ifindex;
    
        s_ifr.ifr_mtu = 7200;
        /* update the mtu through ioctl */
        ec = ioctl(fd_socket[z], SIOCSIFMTU, &s_ifr);
        if(ec == -1)
        {
            perror("iotcl");
            return EXIT_FAILURE;
        }
    
        /* set sockaddr info */
        memset(&my_addr[z], 0, sizeof(struct sockaddr_ll));
        my_addr[z].sll_family = AF_PACKET;
        my_addr[z].sll_protocol = ETH_P_ALL;
        my_addr[z].sll_ifindex = i_ifindex;
    
        /* bind port */
        if (bind(fd_socket[z], (struct sockaddr *)&my_addr[z], sizeof(struct sockaddr_ll)) == -1)
        {
            perror("bind");
            return EXIT_FAILURE;
        }
    
        /* prepare Tx ring request */
        s_packet_req.tp_block_size = c_buffer_sz;
        s_packet_req.tp_frame_size = c_buffer_sz;
        s_packet_req.tp_block_nr = c_buffer_nb;
        s_packet_req.tp_frame_nr = c_buffer_nb;
    
        /* calculate memory to mmap in the kernel */
        size = s_packet_req.tp_block_size * s_packet_req.tp_block_nr;
    
        /* set packet loss option */
        tmp = mode_loss;
        if (setsockopt(fd_socket[z], SOL_PACKET, PACKET_LOSS, (char *)&tmp, sizeof(tmp))<0)
        {
            perror("setsockopt: PACKET_LOSS");
            return EXIT_FAILURE;
        }
 
        /* send TX ring request */
        if (setsockopt(fd_socket[z], SOL_PACKET, PACKET_TX_RING, (char *)&s_packet_req, sizeof(s_packet_req))<0)
        {
            perror("setsockopt: PACKET_TX_RING");
            return EXIT_FAILURE;
        }
    
        /* change send buffer size */
        if(c_sndbuf_sz) {
            printf("send buff size = %d\n", c_sndbuf_sz);
            if (setsockopt(fd_socket[z], SOL_SOCKET, SO_SNDBUF, &c_sndbuf_sz, sizeof(c_sndbuf_sz))< 0)
            {
                perror("getsockopt: SO_SNDBUF");
                return EXIT_FAILURE;
            }
        }
    
        /* get data offset */
        data_offset = TPACKET_HDRLEN - sizeof(struct sockaddr_ll);
    
        /* mmap Tx ring buffers memory */
        ps_header_start = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd_socket[z], 0);
        if (ps_header_start == (void*)-1)
        {
            perror("mmap");
            return EXIT_FAILURE;
        }
    
    
        int i,j;
        int i_index = 0;
        char * data;
        int first_loop = 1;
        struct tpacket_hdr * ps_header;
        int ec_send = 0;
 
        for(i=1; i <= c_packet_nb; i++)
        {
            int i_index_start = i_index;
            int loop = 1;
 
            /* get free buffer */
            do {
                ps_header = ((struct tpacket_hdr *)((void *)ps_header_start + (c_buffer_sz*i_index)));
                data = ((void*) ps_header) + data_offset;
                switch((volatile uint32_t)ps_header->tp_status)
                {
                    case TP_STATUS_AVAILABLE:
                        /* fill data in buffer */
                        if(first_loop) {
                            //Datagram to represent the packet
                            char datagram[4096] , source_ip[32] , *data2, *pseudogram;
    
                            //zero out the packet buffer
                            memset (datagram, 0, 4096);
    
                            //Ethernet header
                            struct ether_header *eh = (struct ether_header *) datagram;
        
                            //IP header
                            struct iphdr *iph = (struct iphdr *) (datagram + sizeof (struct ether_header));
    
                            //TCP header
                            struct tcphdr *tcph = (struct tcphdr *) (datagram + sizeof (struct ether_header) + sizeof (struct ip));
                            struct sockaddr_in sin;
                            struct pseudo_header psh;
    
                            //Data part
                            data2 = datagram + sizeof(struct ether_header) + sizeof(struct iphdr) + sizeof(struct tcphdr);
                            strcpy(data2 , "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
    
                            //some address resolution
                            strcpy(source_ip , inet_ntoa(ipaddr->sin_addr));
                            sin.sin_family = AF_INET;
                            sin.sin_port = htons(80);
                            if (fscanf(fp, "%253s", server) == 1)
                                sin.sin_addr.s_addr = inet_addr (server);   
                            else
                            {
                                done = 1;
                                break;
                            }
                        
                            //Fill in the Ethernet Header
                            eh->ether_dhost[0] = arp_resp->sender_mac[0];
                            eh->ether_dhost[1] = arp_resp->sender_mac[1];
                            eh->ether_dhost[2] = arp_resp->sender_mac[2];
                            eh->ether_dhost[3] = arp_resp->sender_mac[3];
                            eh->ether_dhost[4] = arp_resp->sender_mac[4];
                            eh->ether_dhost[5] = arp_resp->sender_mac[5];

                            memcpy(eh->ether_shost, ifr.ifr_hwaddr.sa_data, HWADDR_len);
                            eh->ether_type = 0x0008;
    
                            //Fill in the IP Header
                            iph->ihl = 5;
                            iph->version = 4;
                            iph->tos = 0;
                            iph->tot_len = sizeof (struct iphdr) + sizeof (struct tcphdr) + strlen(data);
                            iph->id = htonl (54321);    //Id of this packet
                            iph->frag_off = 0;
                            iph->ttl = 255;
                            iph->protocol = IPPROTO_TCP;
                            iph->check = 0;     //Set to 0 before calculating checksum
                            iph->saddr = inet_addr ( source_ip );   //Spoof the source ip address
                            iph->daddr = sin.sin_addr.s_addr;
    
                            //Ip checksum
                            iph->check = csum ((unsigned short *) datagram, iph->tot_len);
    
                            //TCP Header
                            tcph->source = htons (1234);
                            tcph->dest = htons (80);
                            tcph->seq = 0;
                            tcph->ack_seq = 0;
                            tcph->doff = 5; //tcp header size
                            tcph->fin=0;
                            tcph->syn=1;
                            tcph->rst=0;
                            tcph->psh=0;
                            tcph->ack=0;
                            tcph->urg=0;
                            tcph->window = htons (5840);    // maximum allowed window size 
                            tcph->check = 0;    //leave checksum 0 now, filled later by pseudo header
                            tcph->urg_ptr = 0;

                            //Now the TCP checksum
                            psh.source_address = inet_addr( source_ip );
                            psh.dest_address = sin.sin_addr.s_addr;
                            psh.placeholder = 0;
                            psh.protocol = IPPROTO_TCP;
                            psh.tcp_length = htons(sizeof(struct tcphdr) + strlen(data) );
    
                            int psize = sizeof(struct pseudo_header) + sizeof(struct tcphdr) + strlen(data);
                            pseudogram = malloc(psize);
        
                            memcpy(pseudogram , (char*) &psh , sizeof (struct pseudo_header));
                            memcpy(pseudogram + sizeof(struct pseudo_header) , tcph , sizeof(struct tcphdr) + strlen(data));
    
                            tcph->check = csum( (unsigned short*) pseudogram , psize);
                        
                            memcpy(data, datagram, 4096);
                            free(pseudogram);
//                      for(j=0;j<c_packet_sz;j++)
//                          data[j] = j;
                        }
                        loop = 0;
                    break;
 
                    case TP_STATUS_WRONG_FORMAT:
                        printf("An error has occured during transfer\n");
                        exit(EXIT_FAILURE);
                    break;
 
                    default:
                        /* nothing to do => schedule : useful if no SMP */
                        usleep(0);
                        break;
                }
            }
            while(loop == 1);
 
            i_index ++;
            if(i_index >= c_buffer_nb)
            {
                i_index = 0;
                first_loop = 0;
            }
 
            /* update packet len */
            ps_header->tp_len = c_packet_sz;
            /* set header flag to USER (trigs xmit)*/
            ps_header->tp_status = TP_STATUS_SEND_REQUEST;
 
            /* if smp mode selected */
            if(!mode_thread)
            {
                /* send all packets */
                if( ((i&c_send_mask)==0) || (ec_send < 0) || (i == c_packet_nb) )
                {
                    /* send all buffers with TP_STATUS_SEND_REQUEST */
                    /* Don't wait end of transfer */
                    //ec_send = (int) task_send((void*)0);
                }
            }
            else if(c_error) {
 
                if(i == (c_packet_nb/2))
                {
                    int ec_close;
                
                    if(c_error == 1) {
                        ec_close = close(fd_socket[z]);
                    }
                    if(c_error == 2) {
                        if (setsockopt(fd_socket[z], SOL_PACKET, PACKET_TX_RING, (char *)&s_packet_req, sizeof(s_packet_req))<0)
                        {
                            perror("setsockopt: PACKET_TX_RING");
                            //return EXIT_FAILURE;
                        }
                    }
                    break;
                }
            }
        }
    
        //int ec_send;
        static int total=0;
        int blocking = 1;
        
        /* send all buffers with TP_STATUS_SEND_REQUEST */
        /* Wait end of transfer */
        ec_send = sendto(fd_socket[z],NULL,0,(blocking? 0 : MSG_DONTWAIT),(struct sockaddr *) ps_sockaddr,sizeof(struct sockaddr_ll));
        
        if(ec_send < 0) {
            perror("sendto");
        }
        else if ( ec_send == 0 ) {
            /* nothing to do => schedule : useful if no SMP */
            usleep(0);
        }
        else {
            total += ec_send/(c_packet_sz);
            printf("send %d packets (+%d bytes)\n",total, ec_send);
            fflush(0);
        }
        //ps_header_start = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd_socket[z], 0);
        if (munmap(ps_header_start, size) == -1)
        {
            perror("munmap");
            exit(EXIT_FAILURE);
        }       
    
        close(fd_socket[z]);

编辑

PF_INET 套接字上使用 SOCK_RAW 的类似程序也会发送手工制作的数据包执行 TCP SYN 并且实际上确实收到了预期的 TCP SYN/ACK 响应。数据包构建方式的唯一区别是 PF_PACKET 版本还必须添加以太网 headers。下面的屏幕截图显示了一个发送的数据包和一个接收的数据包,这表明 PF_INET 版本和 PF_PACKET 版本的以太网 headers 是相同的。因此,导致这种行为的实际差异是什么是一个谜。

PF_INET程序代码如下

/*
    Raw TCP packets
*/
#include <stdio.h>  //for printf
#include <string.h> //memset
#include <sys/socket.h> //for socket ofcourse
#include <stdlib.h> //for exit(0);
#include <errno.h> //For errno - the error number
#include <netinet/tcp.h>    //Provides declarations for tcp header
#include <netinet/ip.h> //Provides declarations for ip header
#include <arpa/inet.h> // inet_addr
#include <unistd.h> // sleep()

/* 
    96 bit (12 bytes) pseudo header needed for tcp header checksum calculation 
*/
struct pseudo_header
{
    u_int32_t source_address;
    u_int32_t dest_address;
    u_int8_t placeholder;
    u_int8_t protocol;
    u_int16_t tcp_length;
};

/*
    Generic checksum calculation function
*/
unsigned short csum(unsigned short *ptr,int nbytes) 
{
    register long sum;
    unsigned short oddbyte;
    register short answer;

    sum=0;
    while(nbytes>1) {
        sum+=*ptr++;
        nbytes-=2;
    }
    if(nbytes==1) {
        oddbyte=0;
        *((u_char*)&oddbyte)=*(u_char*)ptr;
        sum+=oddbyte;
    }

    sum = (sum>>16)+(sum & 0xffff);
    sum = sum + (sum>>16);
    answer=(short)~sum;
    
    return(answer);
}

int main (void)
{
    //Create a raw socket
    int s = socket (PF_INET, SOCK_RAW, IPPROTO_TCP);
    
    if(s == -1)
    {
        //socket creation failed, may be because of non-root privileges
        perror("Failed to create socket");
        exit(1);
    }
    
    //Datagram to represent the packet
    char datagram[4096] , source_ip[32] , *data , *pseudogram;
    
    //zero out the packet buffer
    memset (datagram, 0, 4096);
    
    //IP header
    struct iphdr *iph = (struct iphdr *) datagram;
    
    //TCP header
    struct tcphdr *tcph = (struct tcphdr *) (datagram + sizeof (struct ip));
    struct sockaddr_in sin;
    struct pseudo_header psh;
    
    //Data part
    data = datagram + sizeof(struct iphdr) + sizeof(struct tcphdr);
    strcpy(data , "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
    
    //some address resolution
    strcpy(source_ip , "192.168.1.170");
    sin.sin_family = AF_INET;
    sin.sin_port = htons(80);
    sin.sin_addr.s_addr = inet_addr ("51.89.233.84");
    
    //Fill in the IP Header
    iph->ihl = 5;
    iph->version = 4;
    iph->tos = 0;
    iph->tot_len = sizeof (struct iphdr) + sizeof (struct tcphdr) + strlen(data);
    iph->id = htonl (54321);    //Id of this packet
    iph->frag_off = 0;
    iph->ttl = 255;
    iph->protocol = IPPROTO_TCP;
    iph->check = 0;     //Set to 0 before calculating checksum
    iph->saddr = inet_addr ( source_ip );   //Spoof the source ip address
    iph->daddr = sin.sin_addr.s_addr;
    
    //Ip checksum
    iph->check = csum ((unsigned short *) datagram, iph->tot_len);
    
    //TCP Header
    tcph->source = htons (1234);
    tcph->dest = htons (80);
    tcph->seq = 0;
    tcph->ack_seq = 0;
    tcph->doff = 5; //tcp header size
    tcph->fin=0;
    tcph->syn=1;
    tcph->rst=0;
    tcph->psh=0;
    tcph->ack=0;
    tcph->urg=0;
    tcph->window = htons (5840);    /* maximum allowed window size */
    tcph->check = 0;    //leave checksum 0 now, filled later by pseudo header
    tcph->urg_ptr = 0;
    
    //Now the TCP checksum
    psh.source_address = inet_addr( source_ip );
    psh.dest_address = sin.sin_addr.s_addr;
    psh.placeholder = 0;
    psh.protocol = IPPROTO_TCP;
    psh.tcp_length = htons(sizeof(struct tcphdr) + strlen(data) );
    
    int psize = sizeof(struct pseudo_header) + sizeof(struct tcphdr) + strlen(data);
    pseudogram = malloc(psize);
    
    memcpy(pseudogram , (char*) &psh , sizeof (struct pseudo_header));
    memcpy(pseudogram + sizeof(struct pseudo_header) , tcph , sizeof(struct tcphdr) + strlen(data));
    
    tcph->check = csum( (unsigned short*) pseudogram , psize);
    
    //IP_HDRINCL to tell the kernel that headers are included in the packet
    int one = 1;
    const int *val = &one;
    
    if (setsockopt (s, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0)
    {
        perror("Error setting IP_HDRINCL");
        exit(0);
    }
    
    //loop if you want to flood :)
    while (1)
    {
        //Send the packet
        if (sendto (s, datagram, iph->tot_len , 0, (struct sockaddr *) &sin, sizeof (sin)) < 0)
        {
            perror("sendto failed");
        }
        //Data send successfully
        else
        {
            printf ("Packet Send. Length : %d \n" , iph->tot_len);
        }
        // sleep for 1 seconds
        sleep(1);
    }
    
    return 0;
}

编辑

仔细检查在线路上发送的数据包后,我注意到 PF_PACKET 版本的 IP header 校验和是错误的。此外,对于长度超过一个字节的添加值,需要反转字节顺序。为什么校验和功能不适用于 PF_PACKET 版本。这是 csum 函数:

/*
    Generic checksum calculation function
*/
unsigned short csum(unsigned short *ptr,int nbytes) 
{
    register long sum;
    unsigned short oddbyte;
    register short answer;

    sum=0;
    while(nbytes>1) {
        sum+=*ptr++;
        nbytes-=2;
    }
    if(nbytes==1) {
        oddbyte=0;
        *((u_char*)&oddbyte)=*(u_char*)ptr;
        sum+=oddbyte;
    }

    sum = (sum>>16)+(sum & 0xffff);
    sum = sum + (sum>>16);
    answer=(short)~sum;
    
    return(answer);
}

ip4 校验和仅在 ip header 上计算,如果我正确的话。因此,如果您将整个数据包的总长度传递给校验和计算函数,如果您得到错误的校验和,我不会感到惊讶。我想知道为什么它恰好在第二个程序中起作用。