0

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

James Read
  • 419
  • 2
  • 7
  • 13
  • 2
    There's way too much code here, can you post a simpler [mre]? – Barmar Jan 12 '22 at 04:11
  • @Barmar If you can supply me with your MAC address, your IP address, the MAC address of your router and the IP address of your router I can hard code those values and then the code will be much shorter. But that would only work for you and not anyone else reading the question. That's why I said to just skip all the preamble. The preamble is working perfectly and bug free. The problem is with the my_recv function which is only a few lines of code. – James Read Jan 12 '22 at 04:21
  • What exactly is the "premable"? You've probably been looking at this code for a long time so are very familiar with it. But for us it's too daunting to even attempt to decipher. Just working out what to ignore and what not to is a task in itself. And just because you claim certain parts are working doesn't mean it is wise for us to believe that (too many times people have made such claims without merit). The best way to prove your claim and to make your question more answerable is to pull out as much of the code as possible whilst still ensuring the issue is reproducible. That is, a [mre] – kaylum Jan 12 '22 at 04:26
  • Small example: there are two `recvfrom` calls. Which one are you referring to? And why show both of them if only one is relevant? That's why it is best if you do the work to reduce the code rather than every one of us having to slog thru it. – kaylum Jan 12 '22 at 04:29
  • If it needs addresses, you can simply hard-code them in the example code. If we want to try to run it we can edit those lines ourselves. There's also threading code -- is that relevant? – Barmar Jan 12 '22 at 04:30
  • @kaylum The problem is in the ```my_recv``` function. – James Read Jan 12 '22 at 04:35
  • @Barmar The threading is relevant. One thread increases the ```count``` variable the other should decrease the ```count``` variable but never does because ```recvfrom``` blocks forever. Thus the program makes no progress after sending out 10,000 ```SYN``` packets. – James Read Jan 12 '22 at 04:39
  • The `count` is irrelevant as that is after the fact. That is, the `count` may be wrong but that's an effect of what you are trying to fix and not a cause. So any other reason why threading is relevant? Or are you perhaps just reluctant to do the (admittedly non-trivial) work to reduce the code? – kaylum Jan 12 '22 at 04:47
  • @kaylum I have edited the question to give a cut down version of the code. IP and MAC address are now hard coded. These will have to be changed if you want the code to run on your machine. – James Read Jan 12 '22 at 05:04
  • Might try creating and binding a separate socket for the receiver. I don't know that doing an ordinary `recv` call on a `PACKET_TX_RING`-configured socket won't work, but always simpler to separate things out. Likewise, at least for a first pass, I would just move the receiver out to a completely separate process. Will be easier to debug that way. – Gil Hamilton Jan 13 '22 at 22:24

0 Answers0