root/net/inet/icmp.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. print_icmp
  2. icmp_send
  3. icmp_unreach
  4. icmp_redirect
  5. icmp_echo
  6. icmp_info
  7. icmp_address
  8. icmp_rcv
  9. icmp_ioctl

   1 /*
   2  * INET         An implementation of the TCP/IP protocol suite for the LINUX
   3  *              operating system.  INET is implemented using the  BSD Socket
   4  *              interface as the means of communication with the user level.
   5  *
   6  *              Internet Control Message Protocol (ICMP)
   7  *
   8  * Version:     @(#)icmp.c      1.0.11  06/02/93
   9  *
  10  * Authors:     Ross Biro, <bir7@leland.Stanford.Edu>
  11  *              Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  12  *              Mark Evans, <evansmp@uhura.aston.ac.uk>
  13  *
  14  *              This program is free software; you can redistribute it and/or
  15  *              modify it under the terms of the GNU General Public License
  16  *              as published by the Free Software Foundation; either version
  17  *              2 of the License, or (at your option) any later version.
  18  */
  19 #include <linux/types.h>
  20 #include <linux/sched.h>
  21 #include <linux/kernel.h>
  22 #include <linux/fcntl.h>
  23 #include <linux/socket.h>
  24 #include <linux/in.h>
  25 #include "inet.h"
  26 #include "dev.h"
  27 #include "ip.h"
  28 #include "route.h"
  29 #include "protocol.h"
  30 #include "icmp.h"
  31 #include "tcp.h"
  32 #include "skbuff.h"
  33 #include "sock.h"
  34 #include <linux/errno.h>
  35 #include <linux/timer.h>
  36 #include <asm/system.h>
  37 #include <asm/segment.h>
  38 
  39 
  40 #define min(a,b)        ((a)<(b)?(a):(b))
  41 
  42 
  43 /* An array of errno for error messages from dest unreach. */
  44 struct icmp_err icmp_err_convert[] = {
  45   { ENETUNREACH,        1 },
  46   { EHOSTUNREACH,       1 },
  47   { ENOPROTOOPT,        1 },
  48   { ECONNREFUSED,       1 },
  49   { EOPNOTSUPP,         0 },
  50   { EOPNOTSUPP,         0 },
  51   { ENETUNREACH,        1 },
  52   { EHOSTDOWN,          1 },
  53   { ENONET,             1 },
  54   { ENETUNREACH,        1 },
  55   { EHOSTUNREACH,       1 },
  56   { EOPNOTSUPP,         0 },
  57   { EOPNOTSUPP,         0 }
  58 };
  59 
  60 
  61 /* Display the contents of an ICMP header. */
  62 static void
  63 print_icmp(struct icmphdr *icmph)
     /* [previous][next][first][last][top][bottom][index][help] */
  64 {
  65   if (inet_debug != DBG_ICMP) return;
  66 
  67   printk("ICMP: type = %d, code = %d, checksum = %X\n",
  68                         icmph->type, icmph->code, icmph->checksum);
  69   printk("      gateway = %s\n", in_ntoa(icmph->un.gateway));
  70 }
  71 
  72 
  73 /* Send an ICMP message. */
  74 void
  75 icmp_send(struct sk_buff *skb_in, int type, int code, struct device *dev)
     /* [previous][next][first][last][top][bottom][index][help] */
  76 {
  77   struct sk_buff *skb;
  78   struct iphdr *iph;
  79   int offset;
  80   struct icmphdr *icmph;
  81   int len;
  82 
  83   DPRINTF((DBG_ICMP, "icmp_send(skb_in = %X, type = %d, code = %d, dev=%X)\n",
  84                                                 skb_in, type, code, dev));
  85 
  86   /* Get some memory for the reply. */
  87   len = sizeof(struct sk_buff) + dev->hard_header_len +
  88         sizeof(struct iphdr) + sizeof(struct icmphdr) +
  89         sizeof(struct iphdr) + 8;       /* amount of header to return */
  90            
  91   skb = (struct sk_buff *) kmalloc(len, GFP_ATOMIC);
  92   if (skb == NULL) return;
  93 
  94   skb->lock = 0;
  95   skb->sk = NULL;
  96   skb->mem_addr = skb;
  97   skb->mem_len = len;
  98   len -= sizeof(struct sk_buff);
  99 
 100   /* Find the IP header. */
 101   iph = (struct iphdr *) (skb_in + 1);
 102   iph = (struct iphdr *) ((unsigned char *) iph + dev->hard_header_len);
 103 
 104   /* Build Layer 2-3 headers for message back to source. */
 105   offset = ip_build_header(skb, iph->daddr, iph->saddr,
 106                            &dev, IPPROTO_ICMP, NULL, len);
 107   if (offset < 0) {
 108         skb->sk = NULL;
 109         kfree_skb(skb, FREE_READ);
 110         return;
 111   }
 112 
 113   /* Re-adjust length according to actual IP header size. */
 114   skb->len = offset + sizeof(struct icmphdr) + sizeof(struct iphdr) + 8;
 115   icmph = (struct icmphdr *) ((unsigned char *) (skb + 1) + offset);
 116   icmph->type = type;
 117   icmph->code = code;
 118   icmph->checksum = 0;
 119   icmph->un.gateway = 0;
 120   memcpy(icmph + 1, iph, sizeof(struct iphdr) + 8);
 121 
 122   icmph->checksum = ip_compute_csum((unsigned char *)icmph,
 123                          sizeof(struct icmphdr) + sizeof(struct iphdr) + 8);
 124 
 125   DPRINTF((DBG_ICMP, ">>\n"));
 126   print_icmp(icmph);
 127 
 128   /* Send it and free it. */
 129   ip_queue_xmit(NULL, dev, skb, 1);
 130 }
 131 
 132 
 133 /* Handle ICMP_UNREACH and ICMP_QUENCH. */
 134 static void
 135 icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb)
     /* [previous][next][first][last][top][bottom][index][help] */
 136 {
 137   struct inet_protocol *ipprot;
 138   struct iphdr *iph;
 139   unsigned char hash;
 140   int err;
 141 
 142   err = (icmph->type << 8) | icmph->code;
 143   iph = (struct iphdr *) (icmph + 1);
 144   switch(icmph->code & 7) {
 145         case ICMP_NET_UNREACH:
 146                 DPRINTF((DBG_ICMP, "ICMP: %s: network unreachable.\n",
 147                                                         in_ntoa(iph->daddr)));
 148                 break;
 149         case ICMP_HOST_UNREACH:
 150                 DPRINTF((DBG_ICMP, "ICMP: %s: host unreachable.\n",
 151                                                 in_ntoa(iph->daddr)));
 152                 break;
 153         case ICMP_PROT_UNREACH:
 154                 printk("ICMP: %s:%d: protocol unreachable.\n",
 155                         in_ntoa(iph->daddr), ntohs(iph->protocol));
 156                 break;
 157         case ICMP_PORT_UNREACH:
 158                 DPRINTF((DBG_ICMP, "ICMP: %s:%d: port unreachable.\n",
 159                         in_ntoa(iph->daddr), -1 /* FIXME: ntohs(iph->port) */));
 160                 break;
 161         case ICMP_FRAG_NEEDED:
 162                 printk("ICMP: %s: fragmentation needed and DF set.\n",
 163                                                         in_ntoa(iph->daddr));
 164                 break;
 165         case ICMP_SR_FAILED:
 166                 printk("ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr));
 167                 break;
 168         default:
 169                 DPRINTF((DBG_ICMP, "ICMP: Unreachable: CODE=%d from %s\n",
 170                                 (icmph->code & 7), in_ntoa(iph->daddr)));
 171                 break;
 172   }
 173 
 174   /* Get the protocol(s). */
 175   hash = iph->protocol & (MAX_INET_PROTOS -1);
 176 
 177   /* This can change while we are doing it. */
 178   ipprot = (struct inet_protocol *) inet_protos[hash];
 179   while(ipprot != NULL) {
 180         struct inet_protocol *nextip;
 181 
 182         nextip = (struct inet_protocol *) ipprot->next;
 183 
 184         /* Pass it off to everyone who wants it. */
 185         if (iph->protocol == ipprot->protocol && ipprot->err_handler) {
 186                 ipprot->err_handler(err, (unsigned char *)(icmph + 1),
 187                                     iph->daddr, iph->saddr, ipprot);
 188         }
 189 
 190         ipprot = nextip;
 191   }
 192   skb->sk = NULL;
 193   kfree_skb(skb, FREE_READ);
 194 }
 195 
 196 
 197 /* Handle ICMP_REDIRECT. */
 198 static void
 199 icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 200 {
 201   struct iphdr *iph;
 202   unsigned long ip;
 203 
 204   iph = (struct iphdr *) (icmph + 1);
 205   ip = iph->daddr;
 206   switch(icmph->code & 7) {
 207         case ICMP_REDIR_NET:
 208                 rt_add((RTF_DYNAMIC | RTF_MODIFIED),
 209                         ip, icmph->un.gateway, dev);
 210                 break;
 211         case ICMP_REDIR_HOST:
 212                 rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_HOST),
 213                         ip, icmph->un.gateway, dev);
 214                 break;
 215         case ICMP_REDIR_NETTOS:
 216         case ICMP_REDIR_HOSTTOS:
 217                 printk("ICMP: cannot handle TOS redirects yet!\n");
 218                 break;
 219         default:
 220                 DPRINTF((DBG_ICMP, "ICMP: Unreach: CODE=%d\n",
 221                                                 (icmph->code & 7)));
 222                 break;
 223   }
 224   skb->sk = NULL;
 225   kfree_skb(skb, FREE_READ);
 226 }
 227 
 228 
 229 /* Handle ICMP_ECHO ("ping") requests. */
 230 static void
 231 icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
     /* [previous][next][first][last][top][bottom][index][help] */
 232           unsigned long saddr, unsigned long daddr, int len,
 233           struct options *opt)
 234 {
 235   struct icmphdr *icmphr;
 236   struct sk_buff *skb2;
 237   int size, offset;
 238 
 239   size = sizeof(struct sk_buff) + dev->hard_header_len + 64 + len;
 240   skb2 = (struct sk_buff *) kmalloc(size, GFP_ATOMIC);
 241   if (skb2 == NULL) {
 242         skb->sk = NULL;
 243         kfree_skb(skb, FREE_READ);
 244         return;
 245   }
 246   skb2->sk = NULL;
 247   skb2->lock = 0;
 248   skb2->mem_addr = skb2;
 249   skb2->mem_len = size;
 250 
 251   /* Build Layer 2-3 headers for message back to source */
 252   offset = ip_build_header(skb2, daddr, saddr, &dev,
 253                                 IPPROTO_ICMP, opt, len);
 254   if (offset < 0) {
 255         printk("ICMP: Could not build IP Header for ICMP ECHO Response\n");
 256         kfree_s(skb2->mem_addr, skb2->mem_len);
 257         skb->sk = NULL;
 258         kfree_skb(skb, FREE_READ);
 259         return;
 260   }
 261 
 262   /* Re-adjust length according to actual IP header size. */
 263   skb2->len = offset + len;
 264 
 265   /* Build ICMP_ECHO Response message. */
 266   icmphr = (struct icmphdr *) ((char *) (skb2 + 1) + offset);
 267   memcpy((char *) icmphr, (char *) icmph, len);
 268   icmphr->type = ICMP_ECHOREPLY;
 269   icmphr->code = 0;
 270   icmphr->checksum = 0;
 271 
 272   if (icmph->checksum) {        /* Calculate Checksum */
 273         icmphr->checksum = ip_compute_csum((unsigned char *)icmphr, len);
 274   }
 275 
 276   /* Ship it out - free it when done */
 277   ip_queue_xmit((struct sock *)NULL, dev, skb2, 1);
 278 
 279   skb->sk = NULL;
 280   kfree_skb(skb, FREE_READ);
 281 }
 282 
 283 
 284 /* Handle the ICMP INFORMATION REQUEST. */
 285 static void
 286 icmp_info(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
     /* [previous][next][first][last][top][bottom][index][help] */
 287           unsigned long saddr, unsigned long daddr, int len,
 288           struct options *opt)
 289 {
 290   /* NOT YET */
 291   skb->sk = NULL;
 292   kfree_skb(skb, FREE_READ);
 293 }
 294 
 295 
 296 /* Handle ICMP_ADRESS_MASK requests. */
 297 static void
 298 icmp_address(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
     /* [previous][next][first][last][top][bottom][index][help] */
 299           unsigned long saddr, unsigned long daddr, int len,
 300           struct options *opt)
 301 {
 302   struct icmphdr *icmphr;
 303   struct sk_buff *skb2;
 304   int size, offset;
 305 
 306   size = sizeof(struct sk_buff) + dev->hard_header_len + 64 + len;
 307   skb2 = (struct sk_buff *) kmalloc(size, GFP_ATOMIC);
 308   if (skb2 == NULL) {
 309         skb->sk = NULL;
 310         kfree_skb(skb, FREE_READ);
 311         return;
 312   }
 313   skb2->sk = NULL;
 314   skb2->lock = 0;
 315   skb2->mem_addr = skb2;
 316   skb2->mem_len = size;
 317 
 318   /* Build Layer 2-3 headers for message back to source */
 319   offset = ip_build_header(skb2, daddr, saddr, &dev,
 320                                 IPPROTO_ICMP, opt, len);
 321   if (offset < 0) {
 322         printk("ICMP: Could not build IP Header for ICMP ADDRESS Response\n");
 323         kfree_s(skb2->mem_addr, skb2->mem_len);
 324         skb->sk = NULL;
 325         kfree_skb(skb, FREE_READ);
 326         return;
 327   }
 328 
 329   /* Re-adjust length according to actual IP header size. */
 330   skb2->len = offset + len;
 331 
 332   /* Build ICMP ADDRESS MASK Response message. */
 333   icmphr = (struct icmphdr *) ((char *) (skb2 + 1) + offset);
 334   icmphr->type = ICMP_ADDRESSREPLY;
 335   icmphr->code = 0;
 336   icmphr->checksum = 0;
 337   icmphr->un.echo.id = icmph->un.echo.id;
 338   icmphr->un.echo.sequence = icmph->un.echo.sequence;
 339   memcpy((char *) (icmphr + 1), (char *) &dev->pa_mask, sizeof(dev->pa_mask));
 340 
 341   if (icmph->checksum) {        /* Calculate Checksum */
 342         icmphr->checksum = ip_compute_csum((unsigned char *)icmphr, len);
 343   }
 344 
 345   /* Ship it out - free it when done */
 346   ip_queue_xmit((struct sock *)NULL, dev, skb2, 1);
 347 
 348   skb->sk = NULL;
 349   kfree_skb(skb, FREE_READ);
 350 }
 351 
 352 
 353 /* Deal with incoming ICMP packets. */
 354 int
 355 icmp_rcv(struct sk_buff *skb1, struct device *dev, struct options *opt,
     /* [previous][next][first][last][top][bottom][index][help] */
 356          unsigned long daddr, unsigned short len,
 357          unsigned long saddr, int redo, struct inet_protocol *protocol)
 358 {
 359   struct icmphdr *icmph;
 360   unsigned char *buff;
 361 
 362   /* Drop broadcast packets. */
 363   if (chk_addr(daddr) == IS_BROADCAST) {
 364         DPRINTF((DBG_ICMP, "ICMP: Discarded broadcast from %s\n",
 365                                                         in_ntoa(saddr)));
 366         skb1->sk = NULL;
 367         kfree_skb(skb1, FREE_READ);
 368         return(0);
 369   }
 370 
 371   buff = skb1->h.raw;
 372   icmph = (struct icmphdr *) buff;
 373 
 374   /* Validate the packet first */
 375   if (icmph->checksum) {        /* Checksums Enabled? */
 376         if (ip_compute_csum((unsigned char *) icmph, len)) {
 377                 /* Failed checksum! */
 378                 printk("ICMP: failed checksum from %s!\n", in_ntoa(saddr));
 379                 skb1->sk = NULL;
 380                 kfree_skb(skb1, FREE_READ);
 381                 return(0);
 382         }
 383   }
 384   print_icmp(icmph);
 385 
 386   /* Parse the ICMP message */
 387   switch(icmph->type) {
 388         case ICMP_TIME_EXCEEDED:
 389         case ICMP_DEST_UNREACH:
 390         case ICMP_SOURCE_QUENCH:
 391                 icmp_unreach(icmph, skb1);
 392                 return(0);
 393         case ICMP_REDIRECT:
 394                 icmp_redirect(icmph, skb1, dev);
 395                 return(0);
 396         case ICMP_ECHO: 
 397                 icmp_echo(icmph, skb1, dev, saddr, daddr, len, opt);
 398                 return 0;
 399         case ICMP_ECHOREPLY:
 400                 skb1->sk = NULL;
 401                 kfree_skb(skb1, FREE_READ);
 402                 return(0);
 403         case ICMP_INFO_REQUEST:
 404                 icmp_info(icmph, skb1, dev, saddr, daddr, len, opt);
 405                 return 0;
 406         case ICMP_INFO_REPLY:
 407                 skb1->sk = NULL;
 408                 kfree_skb(skb1, FREE_READ);
 409                 return(0);
 410         case ICMP_ADDRESS:
 411                 icmp_address(icmph, skb1, dev, saddr, daddr, len, opt);
 412                 return 0;
 413         case ICMP_ADDRESSREPLY:
 414                 skb1->sk = NULL;
 415                 kfree_skb(skb1, FREE_READ);
 416                 return(0);
 417         default:
 418                 DPRINTF((DBG_ICMP,
 419                         "ICMP: Unsupported ICMP from %s, type = 0x%X\n",
 420                                                 in_ntoa(saddr), icmph->type));
 421                 skb1->sk = NULL;
 422                 kfree_skb(skb1, FREE_READ);
 423                 return(0);
 424   }
 425   /*NOTREACHED*/
 426   skb1->sk = NULL;
 427   kfree_skb(skb1, FREE_READ);
 428   return(-1);
 429 }
 430 
 431 
 432 /* Perform any ICMP-related I/O control requests. */
 433 int
 434 icmp_ioctl(struct sock *sk, int cmd, unsigned long arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 435 {
 436   switch(cmd) {
 437         case DDIOCSDBG:
 438                 return(dbg_ioctl((void *) arg, DBG_ICMP));
 439         default:
 440                 return(-EINVAL);
 441   }
 442   return(0);
 443 }

/* [previous][next][first][last][top][bottom][index][help] */