Rtos API example

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers lwip_icmp6.c Source File

lwip_icmp6.c

Go to the documentation of this file.
00001 /**
00002  * @file
00003  *
00004  * IPv6 version of ICMP, as per RFC 4443.
00005  */
00006 
00007 /*
00008  * Copyright (c) 2010 Inico Technologies Ltd.
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: Ivan Delamer <delamer@inicotech.com>
00036  *
00037  *
00038  * Please coordinate changes and requests with Ivan Delamer
00039  * <delamer@inicotech.com>
00040  */
00041 
00042 #include "lwip/opt.h"
00043 
00044 #if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
00045 
00046 #include "lwip/icmp6.h"
00047 #include "lwip/prot/icmp6.h"
00048 #include "lwip/ip6.h"
00049 #include "lwip/ip6_addr.h"
00050 #include "lwip/inet_chksum.h"
00051 #include "lwip/pbuf.h"
00052 #include "lwip/netif.h"
00053 #include "lwip/nd6.h"
00054 #include "lwip/mld6.h"
00055 #include "lwip/ip.h"
00056 #include "lwip/stats.h"
00057 
00058 #include <string.h>
00059 
00060 #ifndef LWIP_ICMP6_DATASIZE
00061 #define LWIP_ICMP6_DATASIZE   8
00062 #endif
00063 #if LWIP_ICMP6_DATASIZE == 0
00064 #define LWIP_ICMP6_DATASIZE   8
00065 #endif
00066 
00067 /* Forward declarations */
00068 static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type);
00069 
00070 
00071 /**
00072  * Process an input ICMPv6 message. Called by ip6_input.
00073  *
00074  * Will generate a reply for echo requests. Other messages are forwarded
00075  * to nd6_input, or mld6_input.
00076  *
00077  * @param p the mld packet, p->payload pointing to the icmpv6 header
00078  * @param inp the netif on which this packet was received
00079  */
00080 void
00081 icmp6_input(struct pbuf *p, struct netif *inp)
00082 {
00083   struct icmp6_hdr *icmp6hdr;
00084   struct pbuf *r;
00085   const ip6_addr_t *reply_src;
00086 
00087   ICMP6_STATS_INC(icmp6.recv);
00088 
00089   /* Check that ICMPv6 header fits in payload */
00090   if (p->len < sizeof(struct icmp6_hdr)) {
00091     /* drop short packets */
00092     pbuf_free(p);
00093     ICMP6_STATS_INC(icmp6.lenerr);
00094     ICMP6_STATS_INC(icmp6.drop);
00095     return;
00096   }
00097 
00098   icmp6hdr = (struct icmp6_hdr *)p->payload;
00099 
00100 #if CHECKSUM_CHECK_ICMP6
00101   IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_ICMP6) {
00102     if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(),
00103                           ip6_current_dest_addr()) != 0) {
00104       /* Checksum failed */
00105       pbuf_free(p);
00106       ICMP6_STATS_INC(icmp6.chkerr);
00107       ICMP6_STATS_INC(icmp6.drop);
00108       return;
00109     }
00110   }
00111 #endif /* CHECKSUM_CHECK_ICMP6 */
00112 
00113   switch (icmp6hdr->type) {
00114   case ICMP6_TYPE_NA: /* Neighbor advertisement */
00115   case ICMP6_TYPE_NS: /* Neighbor solicitation */
00116   case ICMP6_TYPE_RA: /* Router advertisement */
00117   case ICMP6_TYPE_RD: /* Redirect */
00118   case ICMP6_TYPE_PTB: /* Packet too big */
00119     nd6_input(p, inp);
00120     return;
00121     break;
00122   case ICMP6_TYPE_RS:
00123 #if LWIP_IPV6_FORWARD
00124     /* @todo implement router functionality */
00125 #endif
00126     break;
00127 #if LWIP_IPV6_MLD
00128   case ICMP6_TYPE_MLQ:
00129   case ICMP6_TYPE_MLR:
00130   case ICMP6_TYPE_MLD:
00131     mld6_input(p, inp);
00132     return;
00133     break;
00134 #endif
00135   case ICMP6_TYPE_EREQ:
00136 #if !LWIP_MULTICAST_PING
00137     /* multicast destination address? */
00138     if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
00139       /* drop */
00140       pbuf_free(p);
00141       ICMP6_STATS_INC(icmp6.drop);
00142       return;
00143     }
00144 #endif /* LWIP_MULTICAST_PING */
00145 
00146     /* Allocate reply. */
00147     r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM);
00148     if (r == NULL) {
00149       /* drop */
00150       pbuf_free(p);
00151       ICMP6_STATS_INC(icmp6.memerr);
00152       return;
00153     }
00154 
00155     /* Copy echo request. */
00156     if (pbuf_copy(r, p) != ERR_OK) {
00157       /* drop */
00158       pbuf_free(p);
00159       pbuf_free(r);
00160       ICMP6_STATS_INC(icmp6.err);
00161       return;
00162     }
00163 
00164     /* Determine reply source IPv6 address. */
00165 #if LWIP_MULTICAST_PING
00166     if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
00167       reply_src = ip_2_ip6(ip6_select_source_address(inp, ip6_current_src_addr()));
00168       if (reply_src == NULL) {
00169         /* drop */
00170         pbuf_free(p);
00171         pbuf_free(r);
00172         ICMP6_STATS_INC(icmp6.rterr);
00173         return;
00174       }
00175     }
00176     else
00177 #endif /* LWIP_MULTICAST_PING */
00178     {
00179       reply_src = ip6_current_dest_addr();
00180     }
00181 
00182     /* Set fields in reply. */
00183     ((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP;
00184     ((struct icmp6_echo_hdr *)(r->payload))->chksum = 0;
00185 #if CHECKSUM_GEN_ICMP6
00186     IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP6) {
00187       ((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r,
00188           IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr());
00189     }
00190 #endif /* CHECKSUM_GEN_ICMP6 */
00191 
00192     /* Send reply. */
00193     ICMP6_STATS_INC(icmp6.xmit);
00194     ip6_output_if(r, reply_src, ip6_current_src_addr(),
00195         LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, inp);
00196     pbuf_free(r);
00197 
00198     break;
00199   default:
00200     ICMP6_STATS_INC(icmp6.proterr);
00201     ICMP6_STATS_INC(icmp6.drop);
00202     break;
00203   }
00204 
00205   pbuf_free(p);
00206 }
00207 
00208 
00209 /**
00210  * Send an icmpv6 'destination unreachable' packet.
00211  *
00212  * @param p the input packet for which the 'unreachable' should be sent,
00213  *          p->payload pointing to the IPv6 header
00214  * @param c ICMPv6 code for the unreachable type
00215  */
00216 void
00217 icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c)
00218 {
00219   icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR);
00220 }
00221 
00222 /**
00223  * Send an icmpv6 'packet too big' packet.
00224  *
00225  * @param p the input packet for which the 'packet too big' should be sent,
00226  *          p->payload pointing to the IPv6 header
00227  * @param mtu the maximum mtu that we can accept
00228  */
00229 void
00230 icmp6_packet_too_big(struct pbuf *p, u32_t mtu)
00231 {
00232   icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB);
00233 }
00234 
00235 /**
00236  * Send an icmpv6 'time exceeded' packet.
00237  *
00238  * @param p the input packet for which the 'unreachable' should be sent,
00239  *          p->payload pointing to the IPv6 header
00240  * @param c ICMPv6 code for the time exceeded type
00241  */
00242 void
00243 icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c)
00244 {
00245   icmp6_send_response(p, c, 0, ICMP6_TYPE_TE);
00246 }
00247 
00248 /**
00249  * Send an icmpv6 'parameter problem' packet.
00250  *
00251  * @param p the input packet for which the 'param problem' should be sent,
00252  *          p->payload pointing to the IP header
00253  * @param c ICMPv6 code for the param problem type
00254  * @param pointer the pointer to the byte where the parameter is found
00255  */
00256 void
00257 icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer)
00258 {
00259   icmp6_send_response(p, c, pointer, ICMP6_TYPE_PP);
00260 }
00261 
00262 /**
00263  * Send an ICMPv6 packet in response to an incoming packet.
00264  *
00265  * @param p the input packet for which the response should be sent,
00266  *          p->payload pointing to the IPv6 header
00267  * @param code Code of the ICMPv6 header
00268  * @param data Additional 32-bit parameter in the ICMPv6 header
00269  * @param type Type of the ICMPv6 header
00270  */
00271 static void
00272 icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type)
00273 {
00274   struct pbuf *q;
00275   struct icmp6_hdr *icmp6hdr;
00276   const ip6_addr_t *reply_src;
00277   ip6_addr_t *reply_dest;
00278   ip6_addr_t reply_src_local, reply_dest_local;
00279   struct ip6_hdr *ip6hdr;
00280   struct netif *netif;
00281 
00282   /* ICMPv6 header + IPv6 header + data */
00283   q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE,
00284                  PBUF_RAM);
00285   if (q == NULL) {
00286     LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n"));
00287     ICMP6_STATS_INC(icmp6.memerr);
00288     return;
00289   }
00290   LWIP_ASSERT("check that first pbuf can hold icmp 6message",
00291              (q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE)));
00292 
00293   icmp6hdr = (struct icmp6_hdr *)q->payload;
00294   icmp6hdr->type = type;
00295   icmp6hdr->code = code;
00296   icmp6hdr->data = data;
00297 
00298   /* copy fields from original packet */
00299   SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload,
00300           IP6_HLEN + LWIP_ICMP6_DATASIZE);
00301 
00302   /* Get the destination address and netif for this ICMP message. */
00303   if ((ip_current_netif() == NULL) ||
00304       ((code == ICMP6_TE_FRAG) && (type == ICMP6_TYPE_TE))) {
00305     /* Special case, as ip6_current_xxx is either NULL, or points
00306      * to a different packet than the one that expired.
00307      * We must use the addresses that are stored in the expired packet. */
00308     ip6hdr = (struct ip6_hdr *)p->payload;
00309     /* copy from packed address to aligned address */
00310     ip6_addr_copy(reply_dest_local, ip6hdr->src);
00311     ip6_addr_copy(reply_src_local, ip6hdr->dest);
00312     reply_dest = &reply_dest_local;
00313     reply_src = &reply_src_local;
00314     netif = ip6_route(reply_src, reply_dest);
00315     if (netif == NULL) {
00316       /* drop */
00317       pbuf_free(q);
00318       ICMP6_STATS_INC(icmp6.rterr);
00319       return;
00320     }
00321   }
00322   else {
00323     netif = ip_current_netif();
00324     reply_dest = ip6_current_src_addr();
00325 
00326     /* Select an address to use as source. */
00327     reply_src = ip_2_ip6(ip6_select_source_address(netif, reply_dest));
00328     if (reply_src == NULL) {
00329       /* drop */
00330       pbuf_free(q);
00331       ICMP6_STATS_INC(icmp6.rterr);
00332       return;
00333     }
00334   }
00335 
00336   /* calculate checksum */
00337   icmp6hdr->chksum = 0;
00338 #if CHECKSUM_GEN_ICMP6
00339   IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
00340     icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len,
00341       reply_src, reply_dest);
00342   }
00343 #endif /* CHECKSUM_GEN_ICMP6 */
00344 
00345   ICMP6_STATS_INC(icmp6.xmit);
00346   ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
00347   pbuf_free(q);
00348 }
00349 
00350 #endif /* LWIP_ICMP6 && LWIP_IPV6 */