?
Solved

ARP listening (Linux, C language)

Posted on 2004-11-05
4
Medium Priority
?
1,639 Views
Last Modified: 2010-05-18
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---
0
Comment
Question by:darko_poljak
  • 2
4 Comments
 
LVL 22

Accepted Solution

by:
grg99 earned 375 total points
ID: 12505694
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
 
LVL 5

Expert Comment

by:van_dy
ID: 12505851
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
 

Author Comment

by:darko_poljak
ID: 12542035
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
 

Author Comment

by:darko_poljak
ID: 12542042
i'll give my points to one that has less points. :))
0

Featured Post

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Preface I don't like visual development tools that are supposed to write a program for me. Even if it is Xcode and I can use Interface Builder. Yes, it is a perfect tool and has helped me a lot, mainly, in the beginning, when my programs were small…
This is a short and sweet, but (hopefully) to the point article. There seems to be some fundamental misunderstanding about the function prototype for the "main" function in C and C++, more specifically what type this function should return. I see so…
The goal of this video is to provide viewers with basic examples to understand how to use strings and some functions related to them in the C programming language.
The goal of this video is to provide viewers with basic examples to understand and use switch statements in the C programming language.
Suggested Courses

864 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question