2 years ago

#40242

test-img

James Read

Why is recvfrom blocking forever with PF_PACKET socket?

The following code reads an IPv4 address from an input file which has an IPv4 address of a known web server on each line. It sends a TCP SYN with a PF_PACKET type socket. The main function launches two threads. One to send the TCP SYN packet and one thread to receive responses. The receive thread is blocking forever on recvfom call. This is not the desired behaviour. A screenshot of Wireshark capture shows that TCP SYN/ACK responses are being received by the machine:

Wireshark capture

So why isn't my program returning from recvfrom?

EDIT:

I include a cut down listing of the code below. MAC address and IP address and interface name are hard coded. You will have to change them to your setup to get the code to run.

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/tcp.h>    //Provides declarations for tcp header
#include <netinet/ip.h> //Provides declarations for ip header
#include <netinet/ether.h>
#include <ifaddrs.h>
#include <asm/types.h>
#include <linux/if_ether.h>
//#include <linux/if_arp.h>
#include <arpa/inet.h>  //htons etc
#include <time.h>
#include <linux/rtnetlink.h>
#include <sys/resource.h>
#include <pthread.h>

#define MAX_CONNECTIONS 10000
#define HWADDR_len 6

#define debug(x...) printf(x);printf("\n");
#define info(x...) printf(x);printf("\n");
#define warn(x...) printf(x);printf("\n");
#define err(x...) printf(x);printf("\n");

static char * str_devname= NULL;

volatile int fd_socket;
volatile struct sockaddr_ll *ps_sockaddr = NULL;
int done = 0;
char ifname[512];
char ip[512];
struct timespec startTime, stopTime;
long total_bytes = 0;
int sent = 0;
int lines_read = 0;
FILE * fp;
char server[254];
size_t len;
struct ifreq ifr,ifr2;
struct sockaddr_in* ipaddr = (struct sockaddr_in*)&ifr2.ifr_addr;
int count = 0;
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;

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

unsigned short checksum2(const char *buf, unsigned size)
{
    unsigned long long sum = 0;
    const unsigned long long *b = (unsigned long long *) buf;

    unsigned t1, t2;
    unsigned short t3, t4;

    /* Main loop - 8 bytes at a time */
    while (size >= sizeof(unsigned long long))
    {
        unsigned long long s = *b++;
        sum += s;
        if (sum < s) sum++;
        size -= 8;
    }

    /* Handle tail less than 8-bytes long */
    buf = (const char *) b;
    if (size & 4)
    {
        unsigned s = *(unsigned *)buf;
        sum += s;
        if (sum < s) sum++;
        buf += 4;
    }

    if (size & 2)
    {
        unsigned short s = *(unsigned short *) buf;
        sum += s;
        if (sum < s) sum++;
        buf += 2;
    }

    if (size)
    {
        unsigned char s = *(unsigned char *) buf;
        sum += s;
        if (sum < s) sum++;
    }

    /* Fold down to 16 bits */
    t1 = sum;
    t2 = sum >> 32;
    t1 += t2;
    if (t1 < t2) t1++;
    t3 = t1;
    t4 = t1 >> 16;
    t3 += t4;
    if (t3 < t4) t3++;

    return ~t3;
}

void *my_send (){
    while (!done)
    {   
        if (count >= MAX_CONNECTIONS)
        {
            usleep(100);
            continue;
        }
        char * data;
        int first_loop = 1;
        struct tpacket_hdr * ps_header;
        int ec_send = 0;
        
        //Datagram to represent the packet
        char datagram[4096] , source_ip[32] , *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;
    
        //some address resolution
        strcpy(source_ip , "192.168.1.70");
        sin.sin_family = AF_INET;
        sin.sin_port = htons(80);
        if (fscanf(fp, "%253s", server) == 1)
        {
            sin.sin_addr.s_addr = inet_addr (server);
            lines_read++;
            //printf("%s\n", server);
        }   
        else
        {
            done = 1;
            break;
        }
                        
        //Fill in the Ethernet Header
        eh->ether_dhost[0] = 0x62;
        eh->ether_dhost[1] = 0x97;
        eh->ether_dhost[2] = 0x41;
        eh->ether_dhost[3] = 0x4b;
        eh->ether_dhost[4] = 0x1e;
        eh->ether_dhost[5] = 0xc0;

        eh->ether_shost[0] = 0xc4;
        eh->ether_shost[1] = 0x65;
        eh->ether_shost[2] = 0x16;
        eh->ether_shost[3] = 0x24;
        eh->ether_shost[4] = 0xd5;
        eh->ether_shost[5] = 0x9a;

        eh->ether_type = htons(0x0800);
                        
        //Fill in the IP Header
        iph->ihl = 5;
        iph->version = 4;
        iph->tos = 0;
        iph->tot_len = htons(sizeof (struct iphdr) + sizeof (struct tcphdr));
        iph->id = htons (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 );
        iph->daddr = sin.sin_addr.s_addr;
    
        //Ip checksum
        iph->check = checksum2 (datagram + sizeof (struct ether_header), sizeof (struct iphdr));
    
        //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));
    
        int psize = sizeof(struct pseudo_header) + sizeof(struct tcphdr);
        pseudogram = malloc(psize);
    
        memcpy(pseudogram , (char*) &psh , sizeof (struct pseudo_header));
        memcpy(pseudogram + sizeof(struct pseudo_header) , tcph , sizeof(struct tcphdr));
    
        tcph->check = checksum2(pseudogram , psize);
                        
        //memcpy(data, datagram, (sizeof(struct ether_header) + sizeof(struct iphdr) + sizeof(struct tcphdr)));
        free(pseudogram);
        len = sizeof(struct ether_header) + sizeof(struct iphdr) + sizeof(struct tcphdr);
        
        static int total=0;
        
        /* send all buffers with TP_STATUS_SEND_REQUEST */
        /* Wait end of transfer */
        ec_send = sendto(fd_socket,datagram,len,0,(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 */
            //printf("Sleeping\n");
            usleep(0);
        }
        else {
            total += ec_send/(len);
            total_bytes += ec_send;
            sent++;
            pthread_mutex_lock( &mutex1 );
            count++;
            pthread_mutex_unlock( &mutex1 );
            clock_gettime(CLOCK_MONOTONIC, &stopTime);
            uint64_t msElapsed = (stopTime.tv_nsec - startTime.tv_nsec) / 1000000 + (stopTime.tv_sec - startTime.tv_sec) * 1000;
            double seconds = (double)msElapsed / 1000.0;
            printf("Lines read=%d Packets sent=%d, Bytes=%ld, Time=%fseconds, MBit/s=%f Packets/s=%f\r",
                    lines_read, sent, total_bytes, seconds, 8*total_bytes/1024/1024/seconds, sent/seconds);
            //printf("send %d packets (+%d bytes)\n",total, ec_send);
            fflush(0);
        }
    }    
}

void * my_recv (){
    char buffer[8192];
    int ec_recv;
    struct sockaddr_ll from;
    socklen_t fromlen = sizeof(from);
    while (1){
        ec_recv = recvfrom(fd_socket, buffer, sizeof(buffer), 0, (struct sockaddr *) &from, &fromlen);
        printf("recv completed\n");
        if (ec_recv > 0){
            printf("Received something\n");
            pthread_mutex_lock( &mutex1 );
            count--;
            pthread_mutex_unlock( &mutex1 );
        }
        if (ec_recv == 0){
            printf("Received nothing\n");
        }
        if (ec_recv < 0){
            perror("recv");
        }
    }
}

int main( int argc, char ** argv )
{
    uint32_t size;
    struct sockaddr_ll my_addr;
    int i_ifindex;
    int ec;
    struct ifreq s_ifr; /* points to one interface returned from ioctl */
    pthread_t t_send, t_recv;
    
    int s,s2,i;
    int ret = -1;
    struct rlimit lim;
    
    if (argc != 2) {
        printf("Usage: %s <INPUT_FILE>\n", argv[0]);
        return 1;
    }

    getrlimit(RLIMIT_NOFILE, &lim);
    printf("Soft: %d Hard: %d\n", (int)lim.rlim_cur, (int)lim.rlim_max);
    lim.rlim_cur = lim.rlim_max;
    
    
    if (setrlimit(RLIMIT_NOFILE, &lim) == -1) {
        printf("rlimit failed\n");
        return -1;
    }
    getrlimit(RLIMIT_NOFILE, &lim);
    printf("New Soft: %d New Hard: %d\n", (int)lim.rlim_cur, (int)lim.rlim_max);
    
    s = socket(AF_INET, SOCK_DGRAM, 0);
    s2 = socket(AF_INET, SOCK_DGRAM, 0);
    strcpy(ifr.ifr_name, ifname);
    strcpy(ifr2.ifr_name, ifname);
    ioctl(s, SIOCGIFHWADDR, &ifr);
    ioctl(s2, SIOCGIFADDR, &ifr2);
    close(s);

    fp = fopen(argv[1], "r");
    if (!fp)
        exit(EXIT_FAILURE);


    fd_socket = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if(fd_socket == -1)
    {
        perror("socket");
        return EXIT_FAILURE;
    }
    
    /* clear structure */
    memset(&my_addr, 0, sizeof(struct sockaddr_ll));
    my_addr.sll_family = PF_PACKET;
    my_addr.sll_protocol = htons(ETH_P_ALL);
    
    str_devname = "enp3s0";
        
    /* initialize interface struct */
    strncpy (s_ifr.ifr_name, str_devname, sizeof(s_ifr.ifr_name));
    
    /* Get the broad cast address */
    ec = ioctl(fd_socket, 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, SIOCSIFMTU, &s_ifr);
    if(ec == -1)
    {
        perror("iotcl");
        return EXIT_FAILURE;
    }
    
    /* set sockaddr info */
    memset(&my_addr, 0, sizeof(struct sockaddr_ll));
    my_addr.sll_family = AF_PACKET;
    my_addr.sll_protocol = ETH_P_ALL;
    my_addr.sll_ifindex = i_ifindex;
    
    /* bind port */
    if (bind(fd_socket, (struct sockaddr *)&my_addr, sizeof(struct sockaddr_ll)) == -1)
    {
        perror("bind");
        return EXIT_FAILURE;
    }
    
    clock_gettime(CLOCK_MONOTONIC, &startTime);

    if (pthread_create(&t_recv, NULL, my_recv, NULL) != 0){
        perror("pthread_create()");
        exit(errno);
    }
 
    if (pthread_create(&t_send, NULL, my_send, NULL) != 0){
        perror("pthread_create()");
        exit(errno);
    }
    
    pthread_join (t_recv, NULL);    
    pthread_join (t_send, NULL);
    
    close(fd_socket);
    printf("\nFinished without error.\n");
    return 1;
}

Here is a file of IPv4 addresses to test with:

IPv4 addresses

c

sockets

raw

0 Answers

Your Answer

Accepted video resources