Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: TYBLE16_simple_data_logger TYBLE16_MP3_Air
lwip_icmp6.c
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 */
Generated on Tue Jul 12 2022 13:54:28 by
1.7.2