A version of LWIP, provided for backwards compatibility.

Dependents:   AA_DemoBoard DemoBoard HelloServerDemo DemoBoard_RangeIndicator ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers icmp.c Source File

icmp.c

Go to the documentation of this file.
00001 /**
00002  * @file
00003  * ICMP - Internet Control Message Protocol
00004  *
00005  */
00006 
00007 /*
00008  * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
00009  * All rights reserved.
00010  *
00011  * Redistribution and use in source and binary forms, with or without modification,
00012  * are permitted provided that the following conditions are met:
00013  *
00014  * 1. Redistributions of source code must retain the above copyright notice,
00015  *    this list of conditions and the following disclaimer.
00016  * 2. Redistributions in binary form must reproduce the above copyright notice,
00017  *    this list of conditions and the following disclaimer in the documentation
00018  *    and/or other materials provided with the distribution.
00019  * 3. The name of the author may not be used to endorse or promote products
00020  *    derived from this software without specific prior written permission.
00021  *
00022  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
00023  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00024  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
00025  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00026  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
00027  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00028  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00029  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
00030  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
00031  * OF SUCH DAMAGE.
00032  *
00033  * This file is part of the lwIP TCP/IP stack.
00034  *
00035  * Author: Adam Dunkels <adam@sics.se>
00036  *
00037  */
00038 
00039 /* Some ICMP messages should be passed to the transport protocols. This
00040    is not implemented. */
00041 
00042 #include "lwip/opt.h"
00043 
00044 #if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */
00045 
00046 #include "lwip/icmp.h"
00047 #include "lwip/inet.h"
00048 #include "lwip/inet_chksum.h"
00049 #include "lwip/ip.h"
00050 #include "lwip/def.h"
00051 #include "lwip/stats.h"
00052 #include "lwip/snmp.h"
00053 
00054 #include <string.h>
00055 
00056 /* The amount of data from the original packet to return in a dest-unreachable */
00057 #define ICMP_DEST_UNREACH_DATASIZE 8
00058 
00059 /**
00060  * Processes ICMP input packets, called from ip_input().
00061  *
00062  * Currently only processes icmp echo requests and sends
00063  * out the echo response.
00064  *
00065  * @param p the icmp echo request packet, p->payload pointing to the ip header
00066  * @param inp the netif on which this packet was received
00067  */
00068 void
00069 icmp_input(struct pbuf *p, struct netif *inp)
00070 {
00071   u8_t type;
00072 #ifdef LWIP_DEBUG
00073   u8_t code;
00074 #endif /* LWIP_DEBUG */
00075   struct icmp_echo_hdr *iecho;
00076   struct ip_hdr *iphdr;
00077   struct ip_addr tmpaddr;
00078   s16_t hlen;
00079 
00080   ICMP_STATS_INC(icmp.recv);
00081   snmp_inc_icmpinmsgs();
00082 
00083 
00084   iphdr = (struct ip_hdr *)(p->payload); // static_cast<struct ip_hdr *>(x)
00085   hlen = IPH_HL(iphdr) * 4;
00086   if (pbuf_header(p, -hlen) || (p->tot_len < sizeof(u16_t)*2)) {
00087     LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len));
00088     goto lenerr;
00089   }
00090 
00091   type = *((u8_t *)p->payload);
00092 #ifdef LWIP_DEBUG
00093   code = *(((u8_t *)p->payload)+1);
00094 #endif /* LWIP_DEBUG */
00095   switch (type) {
00096   case ICMP_ECHO:
00097 #if !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING
00098     {
00099       int accepted = 1;
00100 #if !LWIP_MULTICAST_PING
00101       /* multicast destination address? */
00102       if (ip_addr_ismulticast(&(iphdr->dest))) {
00103         accepted = 0;
00104       }
00105 #endif /* LWIP_MULTICAST_PING */
00106 #if !LWIP_BROADCAST_PING
00107       /* broadcast destination address? */
00108       if (ip_addr_isbroadcast(&(iphdr->dest), inp)) {
00109         accepted = 0;
00110       }
00111 #endif /* LWIP_BROADCAST_PING */
00112       /* broadcast or multicast destination address not acceptd? */
00113       if (!accepted) {
00114         LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast or broadcast pings\n"));
00115         ICMP_STATS_INC(icmp.err);
00116         pbuf_free(p);
00117         return;
00118       }
00119     }
00120 #endif /* !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */
00121     LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n"));
00122     if (p->tot_len < sizeof(struct icmp_echo_hdr)) {
00123       LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n"));
00124       goto lenerr;
00125     }
00126     if (inet_chksum_pbuf(p) != 0) {
00127       LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n"));
00128       pbuf_free(p);
00129       ICMP_STATS_INC(icmp.chkerr);
00130       snmp_inc_icmpinerrors();
00131       return;
00132     }
00133     if (pbuf_header(p, (PBUF_IP_HLEN + PBUF_LINK_HLEN))) {
00134       /* p is not big enough to contain link headers
00135        * allocate a new one and copy p into it
00136        */
00137       struct pbuf *r;
00138       /* switch p->payload to ip header */
00139       if (pbuf_header(p, hlen)) {
00140         LWIP_ASSERT("icmp_input: moving p->payload to ip header failed\n", 0);
00141         goto memerr;
00142       }
00143       /* allocate new packet buffer with space for link headers */
00144       r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
00145       if (r == NULL) {
00146         LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n"));
00147         goto memerr;
00148       }
00149       LWIP_ASSERT("check that first pbuf can hold struct the ICMP header",
00150                   (r->len >= hlen + sizeof(struct icmp_echo_hdr)));
00151       /* copy the whole packet including ip header */
00152       if (pbuf_copy(r, p) != ERR_OK) {
00153         LWIP_ASSERT("icmp_input: copying to new pbuf failed\n", 0);
00154         goto memerr;
00155       }
00156       iphdr = (struct ip_hdr *)(r->payload); // static_cast<struct ip_hdr *>(x)
00157       /* switch r->payload back to icmp header */
00158       if (pbuf_header(r, -hlen)) {
00159         LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
00160         goto memerr;
00161       }
00162       /* free the original p */
00163       pbuf_free(p);
00164       /* we now have an identical copy of p that has room for link headers */
00165       p = r;
00166     } else {
00167       /* restore p->payload to point to icmp header */
00168       if (pbuf_header(p, -(s16_t)(PBUF_IP_HLEN + PBUF_LINK_HLEN))) {
00169         LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
00170         goto memerr;
00171       }
00172     }
00173     /* At this point, all checks are OK. */
00174     /* We generate an answer by switching the dest and src ip addresses,
00175      * setting the icmp type to ECHO_RESPONSE and updating the checksum. */
00176     iecho = (struct icmp_echo_hdr *)(p->payload); // static_cast<struct icmp_echo_hdr *>(x)
00177     tmpaddr.addr = iphdr->src.addr;
00178     iphdr->src.addr = iphdr->dest.addr;
00179     iphdr->dest.addr = tmpaddr.addr;
00180     ICMPH_TYPE_SET(iecho, ICMP_ER);
00181     /* adjust the checksum */
00182     if (iecho->chksum >= htons(0xffff - (ICMP_ECHO << 8))) {
00183       iecho->chksum += htons(ICMP_ECHO << 8) + 1;
00184     } else {
00185       iecho->chksum += htons(ICMP_ECHO << 8);
00186     }
00187 
00188     /* Set the correct TTL and recalculate the header checksum. */
00189     IPH_TTL_SET(iphdr, ICMP_TTL);
00190     IPH_CHKSUM_SET(iphdr, 0);
00191 #if CHECKSUM_GEN_IP
00192     IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
00193 #endif /* CHECKSUM_GEN_IP */
00194 
00195     ICMP_STATS_INC(icmp.xmit);
00196     /* increase number of messages attempted to send */
00197     snmp_inc_icmpoutmsgs();
00198     /* increase number of echo replies attempted to send */
00199     snmp_inc_icmpoutechoreps();
00200 
00201     if(pbuf_header(p, hlen)) {
00202       LWIP_ASSERT("Can't move over header in packet", 0);
00203     } else {
00204       err_t ret;
00205       struct ip_addr iphdrsrc;                                // XXX: __packed hack
00206       iphdrsrc.addr = iphdr->src.addr;
00207       ret = ip_output_if(p, &(iphdrsrc), IP_HDRINCL,
00208                    ICMP_TTL, 0, IP_PROTO_ICMP, inp);
00209       if (ret != ERR_OK) {
00210         LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %c.\n", ret));
00211       }
00212     }
00213     break;
00214   default:
00215     LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n", 
00216                 (s16_t)type, (s16_t)code));
00217     ICMP_STATS_INC(icmp.proterr);
00218     ICMP_STATS_INC(icmp.drop);
00219   }
00220   pbuf_free(p);
00221   return;
00222 lenerr:
00223   pbuf_free(p);
00224   ICMP_STATS_INC(icmp.lenerr);
00225   snmp_inc_icmpinerrors();
00226   return;
00227 memerr:
00228   pbuf_free(p);
00229   ICMP_STATS_INC(icmp.err);
00230   snmp_inc_icmpinerrors();
00231   return;
00232 }
00233 
00234 /**
00235  * Send an icmp 'destination unreachable' packet, called from ip_input() if
00236  * the transport layer protocol is unknown and from udp_input() if the local
00237  * port is not bound.
00238  *
00239  * @param p the input packet for which the 'unreachable' should be sent,
00240  *          p->payload pointing to the IP header
00241  * @param t type of the 'unreachable' packet
00242  */
00243 void
00244 icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
00245 {
00246   struct pbuf *q;
00247   struct ip_hdr *iphdr;
00248   struct icmp_dur_hdr *idur;
00249 
00250   /* ICMP header + IP header + 8 bytes of data */
00251   q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_dur_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE,
00252                  PBUF_RAM);
00253   if (q == NULL) {
00254     LWIP_DEBUGF(ICMP_DEBUG, ("icmp_dest_unreach: failed to allocate pbuf for ICMP packet.\n"));
00255     return;
00256   }
00257   LWIP_ASSERT("check that first pbuf can hold icmp message",
00258              (q->len >= (sizeof(struct icmp_dur_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE)));
00259 
00260   iphdr = (struct ip_hdr *)(p->payload); // static_cast<struct ip_hdr *>(x)
00261 
00262   idur = (struct icmp_dur_hdr *)(q->payload); // static_cast<struct icmp_dur_hdr *>(x)
00263   ICMPH_TYPE_SET(idur, ICMP_DUR);
00264   ICMPH_CODE_SET(idur, t);
00265 
00266   SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_dur_hdr), p->payload,
00267           IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);
00268 
00269   /* calculate checksum */
00270   idur->chksum = 0;
00271   idur->chksum = inet_chksum(idur, q->len);
00272   ICMP_STATS_INC(icmp.xmit);
00273   /* increase number of messages attempted to send */
00274   snmp_inc_icmpoutmsgs();
00275   /* increase number of destination unreachable messages attempted to send */
00276   snmp_inc_icmpoutdestunreachs();
00277 
00278   struct ip_addr iphdrsrc;
00279   iphdrsrc.addr = iphdr->src.addr;
00280   ip_output(q, NULL, &(iphdrsrc), ICMP_TTL, 0, IP_PROTO_ICMP);
00281   pbuf_free(q);
00282 }
00283 
00284 #if IP_FORWARD || IP_REASSEMBLY
00285 /**
00286  * Send a 'time exceeded' packet, called from ip_forward() if TTL is 0.
00287  *
00288  * @param p the input packet for which the 'time exceeded' should be sent,
00289  *          p->payload pointing to the IP header
00290  * @param t type of the 'time exceeded' packet
00291  */
00292 void
00293 icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
00294 {
00295   struct pbuf *q;
00296   struct ip_hdr *iphdr;
00297   struct icmp_te_hdr *tehdr;
00298 
00299   /* ICMP header + IP header + 8 bytes of data */
00300   q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_dur_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE,
00301                  PBUF_RAM);
00302   if (q == NULL) {
00303     LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n"));
00304     return;
00305   }
00306   LWIP_ASSERT("check that first pbuf can hold icmp message",
00307              (q->len >= (sizeof(struct icmp_dur_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE)));
00308 
00309   iphdr = (struct ip_hdr *)(p->payload); // static_cast<struct ip_hdr *>(x)
00310   LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from "));
00311   ip_addr_debug_print(ICMP_DEBUG, &(iphdr->src));
00312   LWIP_DEBUGF(ICMP_DEBUG, (" to "));
00313   ip_addr_debug_print(ICMP_DEBUG, &(iphdr->dest));
00314   LWIP_DEBUGF(ICMP_DEBUG, ("\n"));
00315 
00316   tehdr = (struct icmp_te_hdr *)(q->payload); // static_cast<struct icmp_te_hdr *>(x)
00317   ICMPH_TYPE_SET(tehdr, ICMP_TE);
00318   ICMPH_CODE_SET(tehdr, t);
00319 
00320   /* copy fields from original packet */
00321   SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_dur_hdr), (u8_t *)p->payload,
00322           IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);
00323 
00324   /* calculate checksum */
00325   tehdr->chksum = 0;
00326   tehdr->chksum = inet_chksum(tehdr, q->len);
00327   ICMP_STATS_INC(icmp.xmit);
00328   /* increase number of messages attempted to send */
00329   snmp_inc_icmpoutmsgs();
00330   /* increase number of destination unreachable messages attempted to send */
00331   snmp_inc_icmpouttimeexcds();
00332   static ip_addr iphdrsrc;
00333   iphdrsrc.addr = iphdr->src.addr;
00334   ip_output(q, NULL, &iphdrsrc, ICMP_TTL, 0, IP_PROTO_ICMP);
00335   pbuf_free(q);
00336 }
00337 
00338 #endif /* IP_FORWARD */
00339 
00340 #endif /* LWIP_ICMP */