简体   繁体   中英

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

I have an application that is sending hand crafted SOCK_RAW packets from a PF_PACKET socket. The packets are being created and sent as the screenshot from Wireshark shows. The packets being sent are TCP SYN packets with an expected TCP SYN/ACK response. However, no response is being received, again as the screenshot shows. I assume that this is because the router is dropping the packets for some reason. Any ideas what the reason could be? Or is there some other reason why I am not receiving any responses.

文本

The full code is quite long because it takes a lot of code to get the IP address and the MAC address of the router to build the ethernet header with. So I have only included the most relevant code. If that is not enough please leave a comment and I will post the full code.

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]);

EDIT

A similar program that uses SOCK_RAW on a PF_INET socket also sends hand crafted packets doing a TCP SYN and does in fact receive the expected TCP SYN/ACK response. The only difference in the way the packets are constructed is that the PF_PACKET version has to have the ethernet headers added as well. The below screenshot shows a packet sent and a packet received which shows that the ethernet headers are the same for both the PF_INET version and the PF_PACKET version. So it is a mystery what the actual difference is that leads to this behaviour.

PF_INET 版本 Wireshark 捕获

The PF_INET program code is as follows

/*
    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;
}

EDIT

On closer inspection of the packets being sent out on the wire I noticed that the IP header checksum is wrong for the PF_PACKET version. Also the byte order needs to be reversed for values added that are more than a single byte in length. Why doesn't the checksum function work for the PF_PACKET version. Here is the csum function:

/*
    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);
}

The ip4 checksum is only calculated over the ip header, if I get it correctly. So if you pass the total lenght of the whole packet to the checlsum calculation function, I would not be surprised, if you get a wrong checksum. I wonder though why it happend to work in the second program.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM