Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

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 #if LWIP_ICMP6_DATASIZE == 0
00061 #undef LWIP_ICMP6_DATASIZE
00062 #define LWIP_ICMP6_DATASIZE   8
00063 #endif
00064 
00065 /* Forward declarations */
00066 static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type);
00067 static void icmp6_send_response_with_addrs(struct pbuf *p, u8_t code, u32_t data,
00068     u8_t type, const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr);
00069 static void icmp6_send_response_with_addrs_and_netif(struct pbuf *p, u8_t code, u32_t data,
00070     u8_t type, const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr, struct netif *netif);
00071 
00072 
00073 /**
00074  * Process an input ICMPv6 message. Called by ip6_input.
00075  *
00076  * Will generate a reply for echo requests. Other messages are forwarded
00077  * to nd6_input, or mld6_input.
00078  *
00079  * @param p the mld packet, p->payload pointing to the icmpv6 header
00080  * @param inp the netif on which this packet was received
00081  */
00082 void
00083 icmp6_input(struct pbuf *p, struct netif *inp)
00084 {
00085   struct icmp6_hdr *icmp6hdr;
00086   struct pbuf *r;
00087   const ip6_addr_t *reply_src;
00088 
00089   ICMP6_STATS_INC(icmp6.recv);
00090 
00091   /* Check that ICMPv6 header fits in payload */
00092   if (p->len < sizeof(struct icmp6_hdr)) {
00093     /* drop short packets */
00094     pbuf_free(p);
00095     ICMP6_STATS_INC(icmp6.lenerr);
00096     ICMP6_STATS_INC(icmp6.drop);
00097     return;
00098   }
00099 
00100   icmp6hdr = (struct icmp6_hdr *)p->payload;
00101 
00102 #if CHECKSUM_CHECK_ICMP6
00103   IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_ICMP6) {
00104     if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(),
00105                           ip6_current_dest_addr()) != 0) {
00106       /* Checksum failed */
00107       pbuf_free(p);
00108       ICMP6_STATS_INC(icmp6.chkerr);
00109       ICMP6_STATS_INC(icmp6.drop);
00110       return;
00111     }
00112   }
00113 #endif /* CHECKSUM_CHECK_ICMP6 */
00114 
00115   switch (icmp6hdr->type) {
00116   case ICMP6_TYPE_NA: /* Neighbor advertisement */
00117   case ICMP6_TYPE_NS: /* Neighbor solicitation */
00118   case ICMP6_TYPE_RA: /* Router advertisement */
00119   case ICMP6_TYPE_RD: /* Redirect */
00120   case ICMP6_TYPE_PTB: /* Packet too big */
00121     nd6_input(p, inp);
00122     return;
00123   case ICMP6_TYPE_RS:
00124 #if LWIP_IPV6_FORWARD
00125     /* @todo implement router functionality */
00126 #endif
00127     break;
00128 #if LWIP_IPV6_MLD
00129   case ICMP6_TYPE_MLQ:
00130   case ICMP6_TYPE_MLR:
00131   case ICMP6_TYPE_MLD:
00132     mld6_input(p, inp);
00133     return;
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  * This function must be used only in direct response to a packet that is being
00213  * received right now. Otherwise, address zones would be lost.
00214  *
00215  * @param p the input packet for which the 'unreachable' should be sent,
00216  *          p->payload pointing to the IPv6 header
00217  * @param c ICMPv6 code for the unreachable type
00218  */
00219 void
00220 icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c)
00221 {
00222   icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR);
00223 }
00224 
00225 /**
00226  * Send an icmpv6 'packet too big' packet.
00227  *
00228  * This function must be used only in direct response to a packet that is being
00229  * received right now. Otherwise, address zones would be lost.
00230  *
00231  * @param p the input packet for which the 'packet too big' should be sent,
00232  *          p->payload pointing to the IPv6 header
00233  * @param mtu the maximum mtu that we can accept
00234  */
00235 void
00236 icmp6_packet_too_big(struct pbuf *p, u32_t mtu)
00237 {
00238   icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB);
00239 }
00240 
00241 /**
00242  * Send an icmpv6 'time exceeded' packet.
00243  *
00244  * This function must be used only in direct response to a packet that is being
00245  * received right now. Otherwise, address zones would be lost.
00246  *
00247  * @param p the input packet for which the 'time exceeded' should be sent,
00248  *          p->payload pointing to the IPv6 header
00249  * @param c ICMPv6 code for the time exceeded type
00250  */
00251 void
00252 icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c)
00253 {
00254   icmp6_send_response(p, c, 0, ICMP6_TYPE_TE);
00255 }
00256 
00257 /**
00258  * Send an icmpv6 'time exceeded' packet, with explicit source and destination
00259  * addresses.
00260  *
00261  * This function may be used to send a response sometime after receiving the
00262  * packet for which this response is meant. The provided source and destination
00263  * addresses are used primarily to retain their zone information.
00264  *
00265  * @param p the input packet for which the 'time exceeded' should be sent,
00266  *          p->payload pointing to the IPv6 header
00267  * @param c ICMPv6 code for the time exceeded type
00268  * @param src_addr source address of the original packet, with zone information
00269  * @param dest_addr destination address of the original packet, with zone
00270  *                  information
00271  */
00272 void
00273 icmp6_time_exceeded_with_addrs(struct pbuf *p, enum icmp6_te_code c,
00274     const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr)
00275 {
00276   icmp6_send_response_with_addrs(p, c, 0, ICMP6_TYPE_TE, src_addr, dest_addr);
00277 }
00278 
00279 /**
00280  * Send an icmpv6 'parameter problem' packet.
00281  *
00282  * This function must be used only in direct response to a packet that is being
00283  * received right now. Otherwise, address zones would be lost and the calculated
00284  * offset would be wrong (calculated against ip6_current_header()).
00285  *
00286  * @param p the input packet for which the 'param problem' should be sent,
00287  *          p->payload pointing to the IP header
00288  * @param c ICMPv6 code for the param problem type
00289  * @param pointer the pointer to the byte where the parameter is found
00290  */
00291 void
00292 icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, const void *pointer)
00293 {
00294   u32_t pointer_u32 = (u32_t)((const u8_t *)pointer - (const u8_t *)ip6_current_header());
00295   icmp6_send_response(p, c, pointer_u32, ICMP6_TYPE_PP);
00296 }
00297 
00298 /**
00299  * Send an ICMPv6 packet in response to an incoming packet.
00300  * The packet is sent *to* ip_current_src_addr() on ip_current_netif().
00301  *
00302  * @param p the input packet for which the response should be sent,
00303  *          p->payload pointing to the IPv6 header
00304  * @param code Code of the ICMPv6 header
00305  * @param data Additional 32-bit parameter in the ICMPv6 header
00306  * @param type Type of the ICMPv6 header
00307  */
00308 static void
00309 icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type)
00310 {
00311   const struct ip6_addr *reply_src, *reply_dest;
00312   struct netif *netif = ip_current_netif();
00313 
00314   LWIP_ASSERT("icmpv6 packet not a direct response", netif != NULL);
00315   reply_dest = ip6_current_src_addr();
00316 
00317   /* Select an address to use as source. */
00318   reply_src = ip_2_ip6(ip6_select_source_address(netif, reply_dest));
00319   if (reply_src == NULL) {
00320     ICMP6_STATS_INC(icmp6.rterr);
00321     return;
00322   }
00323   icmp6_send_response_with_addrs_and_netif(p, code, data, type, reply_src, reply_dest, netif);
00324 }
00325 
00326 /**
00327  * Send an ICMPv6 packet in response to an incoming packet.
00328  *
00329  * Call this function if the packet is NOT sent as a direct response to an
00330  * incoming packet, but rather sometime later (e.g. for a fragment reassembly
00331  * timeout). The caller must provide the zoned source and destination addresses
00332  * from the original packet with the src_addr and dest_addr parameters. The
00333  * reason for this approach is that while the addresses themselves are part of
00334  * the original packet, their zone information is not, thus possibly resulting
00335  * in a link-local response being sent over the wrong link.
00336  *
00337  * @param p the input packet for which the response should be sent,
00338  *          p->payload pointing to the IPv6 header
00339  * @param code Code of the ICMPv6 header
00340  * @param data Additional 32-bit parameter in the ICMPv6 header
00341  * @param type Type of the ICMPv6 header
00342  * @param src_addr original source address
00343  * @param dest_addr original destination address
00344  */
00345 static void
00346 icmp6_send_response_with_addrs(struct pbuf *p, u8_t code, u32_t data, u8_t type,
00347     const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr)
00348 {
00349   const struct ip6_addr *reply_src, *reply_dest;
00350   struct netif *netif;
00351 
00352   /* Get the destination address and netif for this ICMP message. */
00353   LWIP_ASSERT("must provide both source and destination", src_addr != NULL);
00354   LWIP_ASSERT("must provide both source and destination", dest_addr != NULL);
00355 
00356   /* Special case, as ip6_current_xxx is either NULL, or points
00357      to a different packet than the one that expired. */
00358   IP6_ADDR_ZONECHECK(src_addr);
00359   IP6_ADDR_ZONECHECK(dest_addr);
00360   /* Swap source and destination for the reply. */
00361   reply_dest = src_addr;
00362   reply_src = dest_addr;
00363   netif = ip6_route(reply_src, reply_dest);
00364   if (netif == NULL) {
00365     ICMP6_STATS_INC(icmp6.rterr);
00366     return;
00367   }
00368   icmp6_send_response_with_addrs_and_netif(p, code, data, type, reply_src,
00369     reply_dest, netif);
00370 }
00371 
00372 /**
00373  * Send an ICMPv6 packet (with srd/dst address and netif given).
00374  *
00375  * @param p the input packet for which the response should be sent,
00376  *          p->payload pointing to the IPv6 header
00377  * @param code Code of the ICMPv6 header
00378  * @param data Additional 32-bit parameter in the ICMPv6 header
00379  * @param type Type of the ICMPv6 header
00380  * @param reply_src source address of the packet to send
00381  * @param reply_dest destination address of the packet to send
00382  * @param netif netif to send the packet
00383  */
00384 static void
00385 icmp6_send_response_with_addrs_and_netif(struct pbuf *p, u8_t code, u32_t data, u8_t type,
00386     const ip6_addr_t *reply_src, const ip6_addr_t *reply_dest, struct netif *netif)
00387 {
00388   struct pbuf *q;
00389   struct icmp6_hdr *icmp6hdr;
00390 
00391   /* ICMPv6 header + IPv6 header + data */
00392   q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE,
00393                  PBUF_RAM);
00394   if (q == NULL) {
00395     LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n"));
00396     ICMP6_STATS_INC(icmp6.memerr);
00397     return;
00398   }
00399   LWIP_ASSERT("check that first pbuf can hold icmp 6message",
00400              (q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE)));
00401 
00402   icmp6hdr = (struct icmp6_hdr *)q->payload;
00403   icmp6hdr->type = type;
00404   icmp6hdr->code = code;
00405   icmp6hdr->data = lwip_htonl(data);
00406 
00407   /* copy fields from original packet */
00408   SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload,
00409           IP6_HLEN + LWIP_ICMP6_DATASIZE);
00410 
00411   /* calculate checksum */
00412   icmp6hdr->chksum = 0;
00413 #if CHECKSUM_GEN_ICMP6
00414   IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
00415     icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len,
00416       reply_src, reply_dest);
00417   }
00418 #endif /* CHECKSUM_GEN_ICMP6 */
00419 
00420   ICMP6_STATS_INC(icmp6.xmit);
00421   ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
00422   pbuf_free(q);
00423 }
00424 
00425 #endif /* LWIP_ICMP6 && LWIP_IPV6 */