ping.c

00001 #include <linux/kernel.h>
00002 #include <linux/module.h>
00003 #include <rtl_sched.h>
00004 #include <unistd.h>
00005 
00006 static unsigned char *IP;
00007 
00008 MODULE_LICENSE("GPL");
00009 MODULE_AUTHOR("Sergio Perez <serpeal@upvnet.upv.es>");
00010 MODULE_DESCRIPTION("Ping implementation for testing Ethernet drivers.");
00011 MODULE_PARM(IP,"s");
00012 MODULE_PARM_DESC(IP, "The IP address used to be \"pinged\"."); 
00013 
00014 #define MAX_MTU      1536
00015 #define ECHO_REPLY   0x00
00016 #define IP_PACKET    0x0800
00017 #define ARP_PACKET   0x0806
00018 #define ARP_REPLY    0x02
00019 #define ICMP_PACKET  0x01
00020 #define ECHO_REQUEST 0x08
00021 
00022 struct memory{
00023   void *mem;
00024 };
00025 
00026 pthread_t pingpong_thread;
00027 unsigned char out_buf[MAX_MTU];
00028 int fd;
00029 
00030 static int atoi(const char* s);
00031 static void *memcopy(void* dst, const void* src, size_t count);
00032 static unsigned int convert_ip_string_to_int(unsigned char *ip, int len);
00033 static void prepare_icmp_echo_reply(unsigned char *out_buf, unsigned char *in_buf,
00034 int len);
00035 static void prepare_arp_reply(unsigned char *out_buf, unsigned char *in_buf, int len,
00036 int sender_ip, unsigned char *sender_ha);
00037 
00038 /*------------------------------------------------------------------*/
00039 static void * ping_pong(void *arg){
00040   struct memory receive_buffer;
00041   unsigned char *in_buf;
00042   int len;
00043   int my_ip;
00044   unsigned char MAC[6], NIC_IP[16];
00045 
00046   //This code is used to read properly the module parameter IP.
00047   memcopy(NIC_IP,IP,strlen(IP));
00048   NIC_IP[strlen(IP)]='\0';
00049 
00050   my_ip = convert_ip_string_to_int(NIC_IP,strlen(IP));
00051 
00052   //Obtain the network interface's MAC address. 
00053   ioctl(fd, 2, (unsigned long) MAC);
00054   
00055   while(1){
00056     
00057     // Obtain the size of the packet and put it into the "len" variable
00058     len = read(fd,(void *) &receive_buffer,1536);
00059     //This implements the ZERO COPY technique. In one hand, instead of copying 
00060     //the buffer into the one passed by us the driver give us a pointer to the 
00061     //internal buffer, thus saving the expense of a copy. On the other hand, 
00062     //we should NOT modify that buffer.
00063     in_buf = (char *) receive_buffer.mem;
00064     
00065     //Is an IP Packet??
00066     if(htons(*(unsigned short *)&(in_buf[12])) == IP_PACKET){
00067       
00068       //Is an ICMP Packet??
00069       if(in_buf[23] == ICMP_PACKET){
00070         
00071         //Is an ICMP echo request??
00072         if(in_buf[34] == ECHO_REQUEST){
00073           
00074           //Let's prepare the ICMP echo reply
00075           prepare_icmp_echo_reply(out_buf, in_buf, len);
00076 
00077           //Send the reply
00078           write(fd,out_buf,len);
00079         }
00080       }
00081     }
00082     else{ //It is not an IP packet
00083       //Is an ARP Packet??
00084       if(htons(*(unsigned short *)&(in_buf[12])) == ARP_PACKET){
00085         
00086         //Asks for my IP address??
00087         if(htonl(*(unsigned int *)&(in_buf[38])) == my_ip){
00088           
00089           //Let's prepare the ARP reply
00090           prepare_arp_reply(out_buf, in_buf, len, my_ip, MAC);
00091           
00092           //Send the reply
00093           write(fd,out_buf,len);
00094         }
00095       }
00096     }
00097   }
00098   
00099   return NULL;
00100 }
00101 
00102 
00103 /*-----------------------------------------------------------------------------------
00104 */
00105 int init_module(void){
00106   pthread_attr_t attr;
00107 
00108   if(IP == NULL){
00109 
00110     printk("\nThis module requires parameters.\n\n"); 
00111     printk("Usage:\n");
00112     printk("    insmod ping.o IP=ip_address\n\n");
00113     printk("Example:\n");
00114     printk("    insmod ping.o IP=\"162.58.0.1\"\n");
00115     printk("\nFor more information type as root:\n");
00116     printk("    modinfo ping.o\n\n");
00117     
00118     return -1;
00119   }else{
00120   
00121     printk("\n\n Hard Ping example module inserted.\n\n"); 
00122     
00123     if((fd=open("/dev/eth0",0)) == -1){
00124       rtl_printf("ERROR OPENING /dev/eth0\n");
00125       return -1;
00126     }
00127     
00128     pthread_attr_init(&attr);
00129     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00130     if(pthread_create(&(pingpong_thread),&attr, ping_pong, NULL)) {
00131       rtl_printf("ERROR: cannot create pthread!\n");
00132       return -1;
00133     }
00134   
00135     return 0;
00136   }
00137 }
00138 
00139 /*-----------------------------------------------------------------------------------
00140 */
00141 void cleanup_module(void){
00142 
00143   pthread_delete_np(pingpong_thread);
00144   close(fd);
00145   
00146   printk("\n\nHard Ping example module removed.\n\n"); 
00147 }
00148 
00149 /*-----------------------------------------------------------------------------------
00150 */
00151 static int atoi(const char* s) {
00152   long int v=0;
00153   int sign=1;
00154   while ( *s == ' '  ||  (unsigned int)(*s - 9) < 5u) s++;
00155   switch (*s) {
00156   case '-': sign=-1;
00157   case '+': ++s;
00158   }
00159   while ((unsigned int) (*s - '0') < 10u) {
00160     v=v*10+*s-'0'; ++s;
00161   }
00162   return sign==-1?-v:v;
00163 }
00164 
00165 /*-----------------------------------------------------------------------------------
00166 */
00167 static void *memcopy(void* dst, const void* src, size_t count) {
00168   register char *d=dst;
00169   register const char *s=src;
00170   ++count;      /* this actually produces better code than using count-- */
00171   while (--count) {
00172     *d = *s;
00173     ++d; ++s;
00174   }
00175   return dst;
00176 }
00177 
00178 
00179 /*-----------------------------------------------------------------------------------
00180 */
00181 static unsigned int convert_ip_string_to_int(unsigned char *ip, int len){
00182   unsigned int integer_ip=0, last_dot_pos=-1;
00183   unsigned short i, n_dots = 0;
00184   unsigned char part[3];
00185 
00186 
00187   for(i = 0; i<len; i++)
00188     if(ip[i] == '.'){
00189       memcopy(part, &ip[last_dot_pos+1], i-last_dot_pos-1);
00190       n_dots++;
00191       last_dot_pos = i;
00192       integer_ip |= atoi(part)<<(32-(n_dots*8));
00193       part[0] = part[1] = part[2] = 0x00;
00194       if(n_dots == 3) break;
00195     }
00196 
00197   memcopy(part, &ip[last_dot_pos+1], len-last_dot_pos+1);
00198 
00199   integer_ip |= atoi(part);
00200 
00201   return integer_ip;
00202 }
00203 
00204 
00205 /*-----------------------------------------------------------------------------------
00206 */
00207 void prepare_icmp_echo_reply(unsigned char *out_buf, unsigned char *in_buf, int len){
00208   //Copy the incoming packet into the out buffer
00209   memcopy(out_buf, in_buf, len);
00210   
00211   //Swap ethernet source and destination addresses
00212   memcopy(out_buf, &in_buf[6], 6);       //ethout_dst = ethin_src
00213   memcopy(&out_buf[6],in_buf, 6);        //ethout_src = ethin_dst
00214   
00215   //Swap IP source and destination addresses
00216   memcopy(&out_buf[26], &in_buf[30], 4); //ipout_src = ipin_dst
00217   memcopy(&out_buf[30], &in_buf[26], 4); //ipout_dst = ipin_src
00218   
00219   //The IP checksum does not need to be recomputed because 
00220   //swaping source and destination addresses does not change 
00221   //the result of the checksum computation.
00222   
00223   //Change the type of the ICMP packet
00224   out_buf[34] = ECHO_REPLY;             
00225   
00226   //Although in the case of ICMP we change some fields, the 
00227   //checksum field does not need to be recomputed because
00228   //the Linux TCP/IP stack does not check if it is right.
00229 }
00230 
00231 /*-----------------------------------------------------------------------------------
00232 */
00233 void prepare_arp_reply(unsigned char *out_buf, unsigned char *in_buf, int len, int
00234 sender_ip, unsigned char *sender_ha){
00235   unsigned int net_ip = ntohl(sender_ip);
00236 
00237   //Copy the incoming packet into the out buffer
00238   memcopy(out_buf, in_buf, len);
00239   
00240   //Swap ethernet source and destination addresses
00241   memcopy(out_buf, &in_buf[6], 6);       //ethout_dst = ethin_src
00242   memcopy(&out_buf[6], sender_ha, 6);    //ethout_src = our MAC
00243   
00244   //Change the operation type to an ARP reply
00245   out_buf[21] = ARP_REPLY;
00246           
00247   //Swap sender IP and sender MAC with target IP and target MAC
00248   memcopy(&out_buf[38], &in_buf[28], 4); //target_ip = sender_ip
00249   memcopy(&out_buf[32], &in_buf[22], 6); //target_ha = sender_ha
00250   memcopy(&out_buf[28], &net_ip, 4);     //sender_ip = my_ip
00251   memcopy(&out_buf[22], sender_ha, 6);   //sender_ha = MAC
00252 }