• C

ARP listening (Linux, C language)

Helo!

I am writing ARP listener, query and server (the code is below).
Query and server are working, but I have problems with
server.

I am testing it in VMWARE with Devil Linux distribution.
I have an ethernet network with 2 PC's.
PC1: interface eth0 :
        10.0.0.2/24
PC2: interface eth0:
        10.0.0.3/24

On PC1 I ran:
# ./arp eth0 listen

On PC2 I ran:
# ./arp eth0 query  10.0.0.2   10.0.0.3
10.0.0.2 is-at "here is ethernet address"

And on PC1 result is:
who-is 10.0.0.2 tell 10.0.0.3 at "here is ethernet address"

When I then ran (on PC2):
# ping 10.0.0.2

on PC1 result is:
10.0.0.2 is-at "ethernet address"

Listener should at begining catch both
who-is 10.0.0.2 tell 10.0.0.3 at "here is ethernet address"
10.0.0.2 is-at "ethernet address"

but it catches only one packet not both.

So, my question would be, where could be the problem?

thanks!

Darko

Here is the source code:

---SOURCE BEGIN---

#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/time.h>

#include <net/ethernet.h>
/*#include <net/if_arp.h>*/
/*#include <net/if.h>*/

#include <arpa/inet.h>

#include <linux/if_arp.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define PCKT_SIZE 60
#define IP_LEN 16
#define EA_LEN 24

#define N_RETRY 3
#define TMOUT_SEC 2

#define ETH_BCAST "\xff\xff\xff\xff\xff\xff"

int getifindex(const char *ifname) {
      int s;
      struct ifreq ifdev;

      if((s=socket(AF_INET,SOCK_DGRAM,0))<0) {
            return -1;
      }
      memset(&ifdev,0,sizeof(struct ifreq));
      strncpy(ifdev.ifr_name,ifname,IFNAMSIZ);
      if(ioctl(s,SIOCGIFINDEX,&ifdev)<0) {
            return -1;
      }
      close(s);
      return ifdev.ifr_ifindex;
}

int getethaddr(char *ethaddr, const char *ifname) {
      int s;
      struct ifreq ifdev;

      if((s=socket(AF_INET,SOCK_DGRAM,0))<0) {
            return -1;
      }
      memset(&ifdev,0,sizeof(struct ifreq));
      strncpy(ifdev.ifr_name,ifname,IFNAMSIZ);
      if(ioctl(s,SIOCGIFHWADDR,&ifdev)<0) {
            return -1;
      }
      close(s);
      memcpy(ethaddr,ifdev.ifr_hwaddr.sa_data,ETH_ALEN);
      return 0;
}

struct arpdb {
      char ip[IP_LEN]; /* pohrani kao string */
      char eth[ETH_ALEN]; /* pohrani kao 6 byte-ova (HWADDR) */
      struct arpdb *next;
};

struct arpdb *head=NULL;

int readarpdb() {
      struct arpdb *p,*new;

      FILE *f;
      char buf[128];
      char temp[32];

      if((f=fopen("arp.db","r"))==NULL) {
            return -1;
      }
      while(fgets(buf,128,f)) {
            buf[strlen(buf)-1]='\0';
            new=malloc(sizeof(struct arpdb));
            if(new==NULL) {
                  fclose(f);
                  return -1;
            }
            new->next=NULL;
            sscanf(buf,"%s\t%s",new->ip,temp);
            sscanf(temp,"%02x:%02x:%02x:%02x:%02x:%02x",
                                    &new->eth[0],
                                    &new->eth[1],
                                    &new->eth[2],
                                    &new->eth[3],
                                    &new->eth[4],
                                    &new->eth[5]);
            if(head==NULL) {
                  head=new;
                  p=new;
            } else {
                  p->next=new;
                  p=new;
            }
      }
      fclose(f);
      return 0;
}

int gethwaddr(char *eth,const char *ip) {
      struct arpdb *p;

      for(p=head;p!=NULL;p=p->next) {
            if(strcmp(ip,p->ip)==0) {
                  memcpy(eth,p->eth,ETH_ALEN);
                  return 0;
            }
      }
      return -1;
}

void printarpdb() {
      struct arpdb *p;
      for(p=head;p!=NULL;p=p->next) {
            printf("%s\t\t%s\n",p->ip,p->eth);
      }
}

void freearpdb() {
      struct arpdb *p,*del;
      for(p=head;p->next!=NULL;) {
            del=p;
            p=p->next;
            free(del);
      }
}

usage(const char *appname) {
      fprintf(stderr,"usage: %s <sucelje> query <trazena ip adresa> <ip adresa sucelja>\n"
                           "       %s <sucelje> listen\n"
                           "       %s <sucelje> server\n\n",appname,appname,appname);
}

#define CALLID_QUERY 0
#define CALLID_LISTEN 1
#define CALLID_SERVER 2

struct arp_hdr {
      unsigned short int ar_hrd;          /* Format of hardware address.  */
      unsigned short int ar_pro;          /* Format of protocol address.  */
      unsigned char ar_hln;               /* Length of hardware address.  */
      unsigned char ar_pln;               /* Length of protocol address.  */
      unsigned short int ar_op;           /* ARP opcode (command).  */

      unsigned char ar_sha[ETH_ALEN];   /* Sender hardware address.  */
      unsigned char ar_sip[4];          /* Sender IP address.  */
      unsigned char ar_tha[ETH_ALEN];   /* Target hardware address.  */
      unsigned char ar_tip[4];          /* Target IP address.  */
};

main(int argc, char **argv) {
      struct ether_header *ether; /* uint8 ether_dhost, ether_shost; uint16 ether_type */
      struct arp_hdr *arp;

      char *ifname,*search_ip,*if_ip;
      int callid; /* koji modul */

      int s_raw;
      struct sockaddr_ll sall;
      struct sockaddr_ll to;
      int buflen;
      unsigned char buffer[PCKT_SIZE]={0};
      unsigned char sndbuf[PCKT_SIZE]={0};
      struct ether_header *sndether;
      struct arp_hdr *sndarp;

      char ip_recvr[IP_LEN];
      char ip_sender[IP_LEN];
      char eth_sender[EA_LEN];
      char eth_recvr[EA_LEN];
      char eth_db[EA_LEN];

      struct timeval timeout;
      fd_set readfds;

      int foo;

      /* begin arguments */
      if(argc == 5) {
            if(strcmp("query",argv[2])) {
                  fprintf(stderr,"Invalid call!\n");
                  usage(argv[0]);
                  exit(1);
            }
            search_ip=argv[3];
            if_ip=argv[4];
            callid=CALLID_QUERY;
      } else if(argc == 3) {
            if(strcmp("listen",argv[2])==0) {
                  callid=CALLID_LISTEN;
            } else if(strcmp("server",argv[2])==0) {
                  callid=CALLID_SERVER;
            } else {
                  fprintf(stderr,"Invalid call!\n");
                  usage(argv[0]);
                  exit(1);
            }
      } else {
            if(argc>1) {
                  fprintf(stderr,"Invalid arguments count!\n");
            }
            usage(argv[0]);
            exit(1);
      }
      ifname=argv[1];
      /* end arguments */

      if((s_raw=socket(AF_PACKET,SOCK_RAW,htons(ETH_P_ARP)))<0) {
            perror("socket");
            exit(1);
      }
      memset(&sall,0,sizeof(sall));
      sall.sll_family=AF_PACKET;
      if((sall.sll_ifindex=getifindex(ifname))<0) {
            perror("getifindex");
            exit(1);
      }
      sall.sll_protocol=htons(ETH_P_ARP);
      if(bind(s_raw,(struct sockaddr *)&sall,sizeof(sall))<0) {
            perror("bind");
            exit(1);
      }
      ether=(struct ether_header *)buffer;
      arp=(struct arp_hdr *)((void *)ether+sizeof(struct ether_header));

      switch(callid) {
            case CALLID_QUERY:
                  if(getethaddr((char *)&(ether->ether_shost),ifname)<0) {
                        perror("getethaddr");
                        exit(1);
                  }
                  memcpy(ether->ether_dhost,ETH_BCAST,ETH_ALEN);
                  ether->ether_type=htons(ETH_P_ARP);
                  arp->ar_hrd=htons(ARPHRD_ETHER);
                  arp->ar_pro=htons(ETH_P_IP);
                  arp->ar_hln=ETH_ALEN;
                  arp->ar_pln=4;
                  arp->ar_op=htons(ARPOP_REQUEST);
                  /* arp->ar_sha=ether->ether_shost */
                  memcpy(arp->ar_sha,ether->ether_shost,ETH_ALEN);
                  inet_pton(AF_INET,if_ip,(struct in_addr *)&arp->ar_sip);
                  inet_pton(AF_INET,search_ip,(struct in_addr *)&arp->ar_tip);

                  memset(&to,0,sizeof(to));
                  to.sll_family=AF_PACKET;
                  if((to.sll_ifindex=getifindex(ifname))<0) {
                        perror("getifindex");
                        exit(1);
                  }
                  to.sll_protocol=htons(ETH_P_ARP);

                  FD_ZERO(&readfds);
                  FD_SET(s_raw,&readfds);

                  for(foo=0;foo<N_RETRY;) {
                        if(sendto(s_raw,buffer,PCKT_SIZE,0,(struct sockaddr *)&to,sizeof(to))<0) {
                              perror("sendto");
                              exit(1);
                        }
                        while(1) {
                              timeout.tv_sec=TMOUT_SEC;
                              timeout.tv_usec=0;
                              if(select(s_raw+1,&readfds,NULL,NULL,&timeout)<0) {
                                    perror("select");
                                    exit(1);
                              }
                              if(!FD_ISSET(s_raw,&readfds)) {
                                    printf("Timed out. Retrying...\n");
                                    ++foo;
                                    break;
                              }
                              if((buflen=recvfrom(s_raw,buffer,PCKT_SIZE,0,NULL,NULL))<0) {
                                    perror("recvfrom");
                                    exit(1);
                              }
                              if(memcmp(ether->ether_dhost, ETH_BCAST, ETH_ALEN)==0 ||
                                 ether->ether_type!=htons(ETH_P_ARP) ||
                                 arp->ar_op!=htons(ARPOP_REPLY)) {
                                    continue;
                              }
                              inet_ntop(AF_INET,(struct in_addr *)&arp->ar_sip,ip_sender,IP_LEN);
                              sprintf(eth_sender,"%02x:%02x:%02x:%02x:%02x:%02x",
                                                arp->ar_sha[0],arp->ar_sha[1],
                                                arp->ar_sha[2],arp->ar_sha[3],
                                                arp->ar_sha[4],arp->ar_sha[5]);
                              printf("%s is-at %s\n",ip_sender,eth_sender);
                              foo=N_RETRY;
                              break;
                        } /* while(1) */
                  } /* for */
                  break;
            default:
                  if(callid==CALLID_SERVER) {
                        readarpdb();
                  }
                  while(1) {
                        /* cekaj pakete */
                        if((buflen=recvfrom(s_raw,buffer,PCKT_SIZE,0,NULL,NULL))<0) {
                              perror("recvfrom");
                              exit(1);
                        }
                        /* imamo paket */
                        if(ether->ether_type==htons(ETH_P_ARP)) {
                              inet_ntop(AF_INET,(struct in_addr *)&arp->ar_tip,ip_recvr,IP_LEN);
                              inet_ntop(AF_INET,(struct in_addr *)&arp->ar_sip,ip_sender,IP_LEN);
                              sprintf(eth_sender,"%02x:%02x:%02x:%02x:%02x:%02x",
                                                      arp->ar_sha[0],arp->ar_sha[1],
                                                      arp->ar_sha[2],arp->ar_sha[3],
                                                      arp->ar_sha[4],arp->ar_sha[5]);
                              sprintf(eth_recvr,"%02x:%02x:%02x:%02x:%02x:%02x",
                                                      arp->ar_tha[0],arp->ar_tha[1],
                                                      arp->ar_tha[2],arp->ar_tha[3],
                                                      arp->ar_tha[4],arp->ar_tha[5]);
                              if(arp->ar_op==htons(ARPOP_REQUEST)) {
                                    printf("who-has %s tell %s at %s\n",
                                                ip_recvr,
                                                ip_sender,
                                                eth_sender);

                                    if(callid==CALLID_SERVER) {
                                          /* vidi je li u bazi */
                                          if(gethwaddr(eth_db,ip_recvr)==0) {
                                                /* posalji odgovor */
                                                sndether=(struct ether_header *)sndbuf;
                                                sndarp=(struct arp_hdr *)((void *)sndether+sizeof(struct ether_header));

                                                memcpy(sndether->ether_shost,eth_db,ETH_ALEN);
                                                memcpy(sndether->ether_dhost,ether->ether_shost,ETH_ALEN);
                                                sndether->ether_type=htons(ETH_P_ARP);
                                                sndarp->ar_hrd=htons(ARPHRD_ETHER);
                                                sndarp->ar_pro=htons(ETH_P_IP);
                                                sndarp->ar_hln=ETH_ALEN;
                                                sndarp->ar_pln=4;
                                                sndarp->ar_op=htons(ARPOP_REPLY);
                                                /* arp->ar_sha=ether->ether_shost */
                                                memcpy(sndarp->ar_sha,eth_db,ETH_ALEN);
                                                memcpy(sndarp->ar_tha,arp->ar_sha,ETH_ALEN);
                                                memcpy(sndarp->ar_sip,arp->ar_tip,4);
                                                memcpy(sndarp->ar_tip,arp->ar_sip,4);
            
                                                memset(&to,0,sizeof(to));
                                                to.sll_family=AF_PACKET;
                                                if((to.sll_ifindex=getifindex(ifname))<0) {
                                                      perror("getifindex");
                                                      freearpdb();
                                                      exit(1);
                                                }
                                                to.sll_protocol=htons(ETH_P_ARP);

                                                if(sendto(s_raw,sndbuf,PCKT_SIZE,0,(struct sockaddr *)&to,sizeof(to))<0) {
                                                      perror("sendto");
                                                      freearpdb();
                                                      exit(1);
                                                }
                                          } /* if u bazi */
                                    } /* if server */

                              } else if(arp->ar_op==htons(ARPOP_REPLY)) {
                                    printf("%s is-at %s\n",ip_sender,eth_sender);
                              }
                        } /* if paket ARP */
                  } /* while(1) */
                  break;
      } /* switch */
      return 0;
}


---SOURCE END---
darko_poljakAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

grg99Commented:
I'd suspect some other network element in the stack is grabbing the ARP reply for itself.  You do have an awful lot of network code loaded when you have Linux and VMWare running!

I'd try it on something simpler, like two REAL PC's, no VMWare


0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
van_dyCommented:
Your program works perfectly fine.

default:
               if(callid==CALLID_SERVER) {
                    readarpdb();
               }
               while(1) {
                    /* cekaj pakete */
                    if((buflen=recvfrom(s_raw,buffer,PCKT_SIZE,0,NULL,NULL))<0) {  <<---you are reading only packets that are received on the socket.  
                         perror("recvfrom");
                         exit(1);
                    }


>> PC1: interface eth0 :
        10.0.0.2/24
>>On PC1 I ran:
>># ./arp eth0 listen

>>When I then ran (on PC2):
>># ping 10.0.0.2

>>on PC1 result is:
>>10.0.0.2 is-at "ethernet address"

>>Listener should at begining catch both
>>who-is 10.0.0.2 tell 10.0.0.3 at "here is ethernet address"
>>10.0.0.2 is-at "ethernet address"   <<-- this packet will be sent out to 10.0.0.3, you should not expect to be able to      read it

after running your code here on my linux pc i get results, i get results like
>>who-is 10.0.0.2 tell 10.0.0.3 at "here is ethernet address"
>>10.0.0.3 is-at "ethernet address"      << -- this additional packet is received by PC1(10.0.0.2)

0
darko_poljakAuthor Commented:
hi!
i changed this:

sall.sll_protocol=htons(ETH_P_ARP);
     if(bind(s_raw,(struct sockaddr *)&sall,sizeof(sall))<0) {
          perror("bind");
          exit(1);
     }

to:

sall.sll_protocol=htons(ETH_P_ALL);
     if(bind(s_raw,(struct sockaddr *)&sall,sizeof(sall))<0) {
          perror("bind");
          exit(1);
     }

and then it worked like it should!
0
darko_poljakAuthor Commented:
i'll give my points to one that has less points. :))
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.