1/** 2 * @file 3 * 4 * IPv6 version of ICMP, as per RFC 4443. 5 */ 6 7/* 8 * Copyright (c) 2010 Inico Technologies Ltd. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without modification, 12 * are permitted provided that the following conditions are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright notice, 15 * this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright notice, 17 * this list of conditions and the following disclaimer in the documentation 18 * and/or other materials provided with the distribution. 19 * 3. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 23 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 25 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 27 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 31 * OF SUCH DAMAGE. 32 * 33 * This file is part of the lwIP TCP/IP stack. 34 * 35 * Author: Ivan Delamer <delamer@inicotech.com> 36 * 37 * 38 * Please coordinate changes and requests with Ivan Delamer 39 * <delamer@inicotech.com> 40 */ 41 42#include "lwip/opt.h" 43 44#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ 45 46#include "lwip/icmp6.h" 47#include "lwip/prot/icmp6.h" 48#include "lwip/ip6.h" 49#include "lwip/ip6_addr.h" 50#include "lwip/inet_chksum.h" 51#include "lwip/pbuf.h" 52#include "lwip/netif.h" 53#include "lwip/nd6.h" 54#include "lwip/mld6.h" 55#include "lwip/ip.h" 56#include "lwip/stats.h" 57 58#include <string.h> 59 60#if !LWIP_ICMP6_DATASIZE || (LWIP_ICMP6_DATASIZE > (IP6_MIN_MTU_LENGTH - IP6_HLEN - ICMP6_HLEN)) 61#undef LWIP_ICMP6_DATASIZE 62#define LWIP_ICMP6_DATASIZE (IP6_MIN_MTU_LENGTH - IP6_HLEN - ICMP6_HLEN) 63#endif 64 65/* Forward declarations */ 66static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type); 67static void icmp6_send_response_with_addrs(struct pbuf *p, u8_t code, u32_t data, 68 u8_t type, const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr); 69static void icmp6_send_response_with_addrs_and_netif(struct pbuf *p, u8_t code, u32_t data, 70 u8_t type, const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr, struct netif *netif); 71 72 73/** 74 * Process an input ICMPv6 message. Called by ip6_input. 75 * 76 * Will generate a reply for echo requests. Other messages are forwarded 77 * to nd6_input, or mld6_input. 78 * 79 * @param p the mld packet, p->payload pointing to the icmpv6 header 80 * @param inp the netif on which this packet was received 81 */ 82void 83icmp6_input(struct pbuf *p, struct netif *inp) 84{ 85 struct icmp6_hdr *icmp6hdr; 86 struct pbuf *r; 87 const ip6_addr_t *reply_src; 88 89 ICMP6_STATS_INC(icmp6.recv); 90 91 /* Check that ICMPv6 header fits in payload */ 92 if (p->len < sizeof(struct icmp6_hdr)) { 93 /* drop short packets */ 94 pbuf_free(p); 95 ICMP6_STATS_INC(icmp6.lenerr); 96 ICMP6_STATS_INC(icmp6.drop); 97 return; 98 } 99 100 icmp6hdr = (struct icmp6_hdr *)p->payload; 101 102#if CHECKSUM_CHECK_ICMP6 103 IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_ICMP6) { 104 if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(), 105 ip6_current_dest_addr()) != 0) { 106 /* Checksum failed */ 107 pbuf_free(p); 108 ICMP6_STATS_INC(icmp6.chkerr); 109 ICMP6_STATS_INC(icmp6.drop); 110 return; 111 } 112 } 113#endif /* CHECKSUM_CHECK_ICMP6 */ 114 115 switch (icmp6hdr->type) { 116 case ICMP6_TYPE_NA: /* Neighbor advertisement */ 117 case ICMP6_TYPE_NS: /* Neighbor solicitation */ 118 case ICMP6_TYPE_RA: /* Router advertisement */ 119 case ICMP6_TYPE_RD: /* Redirect */ 120 case ICMP6_TYPE_PTB: /* Packet too big */ 121 nd6_input(p, inp); 122 return; 123 case ICMP6_TYPE_RS: 124#if LWIP_IPV6_FORWARD 125 /* @todo implement router functionality */ 126#endif 127 break; 128#if LWIP_IPV6_MLD 129 case ICMP6_TYPE_MLQ: 130 case ICMP6_TYPE_MLR: 131 case ICMP6_TYPE_MLD: 132 mld6_input(p, inp); 133 return; 134#endif 135 case ICMP6_TYPE_EREQ: 136#if !LWIP_MULTICAST_PING 137 /* multicast destination address? */ 138 if (ip6_addr_ismulticast(ip6_current_dest_addr())) { 139 /* drop */ 140 pbuf_free(p); 141 ICMP6_STATS_INC(icmp6.drop); 142 return; 143 } 144#endif /* LWIP_MULTICAST_PING */ 145 146 /* Allocate reply. */ 147 r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM); 148 if (r == NULL) { 149 /* drop */ 150 pbuf_free(p); 151 ICMP6_STATS_INC(icmp6.memerr); 152 return; 153 } 154 155 /* Copy echo request. */ 156 if (pbuf_copy(r, p) != ERR_OK) { 157 /* drop */ 158 pbuf_free(p); 159 pbuf_free(r); 160 ICMP6_STATS_INC(icmp6.err); 161 return; 162 } 163 164 /* Determine reply source IPv6 address. */ 165#if LWIP_MULTICAST_PING 166 if (ip6_addr_ismulticast(ip6_current_dest_addr())) { 167 reply_src = ip_2_ip6(ip6_select_source_address(inp, ip6_current_src_addr())); 168 if (reply_src == NULL) { 169 /* drop */ 170 pbuf_free(p); 171 pbuf_free(r); 172 ICMP6_STATS_INC(icmp6.rterr); 173 return; 174 } 175 } 176 else 177#endif /* LWIP_MULTICAST_PING */ 178 { 179 reply_src = ip6_current_dest_addr(); 180 } 181 182 /* Set fields in reply. */ 183 ((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP; 184 ((struct icmp6_echo_hdr *)(r->payload))->chksum = 0; 185#if CHECKSUM_GEN_ICMP6 186 IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP6) { 187 ((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r, 188 IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr()); 189 } 190#endif /* CHECKSUM_GEN_ICMP6 */ 191 192 /* Send reply. */ 193 ICMP6_STATS_INC(icmp6.xmit); 194 ip6_output_if(r, reply_src, ip6_current_src_addr(), 195 LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, inp); 196 pbuf_free(r); 197 198 break; 199 default: 200 ICMP6_STATS_INC(icmp6.proterr); 201 ICMP6_STATS_INC(icmp6.drop); 202 break; 203 } 204 205 pbuf_free(p); 206} 207 208 209/** 210 * Send an icmpv6 'destination unreachable' packet. 211 * 212 * This function must be used only in direct response to a packet that is being 213 * received right now. Otherwise, address zones would be lost. 214 * 215 * @param p the input packet for which the 'unreachable' should be sent, 216 * p->payload pointing to the IPv6 header 217 * @param c ICMPv6 code for the unreachable type 218 */ 219void 220icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c) 221{ 222 icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR); 223} 224 225/** 226 * Send an icmpv6 'packet too big' packet. 227 * 228 * This function must be used only in direct response to a packet that is being 229 * received right now. Otherwise, address zones would be lost. 230 * 231 * @param p the input packet for which the 'packet too big' should be sent, 232 * p->payload pointing to the IPv6 header 233 * @param mtu the maximum mtu that we can accept 234 */ 235void 236icmp6_packet_too_big(struct pbuf *p, u32_t mtu) 237{ 238 icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB); 239} 240 241/** 242 * Send an icmpv6 'time exceeded' packet. 243 * 244 * This function must be used only in direct response to a packet that is being 245 * received right now. Otherwise, address zones would be lost. 246 * 247 * @param p the input packet for which the 'time exceeded' should be sent, 248 * p->payload pointing to the IPv6 header 249 * @param c ICMPv6 code for the time exceeded type 250 */ 251void 252icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c) 253{ 254 icmp6_send_response(p, c, 0, ICMP6_TYPE_TE); 255} 256 257/** 258 * Send an icmpv6 'time exceeded' packet, with explicit source and destination 259 * addresses. 260 * 261 * This function may be used to send a response sometime after receiving the 262 * packet for which this response is meant. The provided source and destination 263 * addresses are used primarily to retain their zone information. 264 * 265 * @param p the input packet for which the 'time exceeded' should be sent, 266 * p->payload pointing to the IPv6 header 267 * @param c ICMPv6 code for the time exceeded type 268 * @param src_addr source address of the original packet, with zone information 269 * @param dest_addr destination address of the original packet, with zone 270 * information 271 */ 272void 273icmp6_time_exceeded_with_addrs(struct pbuf *p, enum icmp6_te_code c, 274 const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr) 275{ 276 icmp6_send_response_with_addrs(p, c, 0, ICMP6_TYPE_TE, src_addr, dest_addr); 277} 278 279/** 280 * Send an icmpv6 'parameter problem' packet. 281 * 282 * This function must be used only in direct response to a packet that is being 283 * received right now. Otherwise, address zones would be lost and the calculated 284 * offset would be wrong (calculated against ip6_current_header()). 285 * 286 * @param p the input packet for which the 'param problem' should be sent, 287 * p->payload pointing to the IP header 288 * @param c ICMPv6 code for the param problem type 289 * @param pointer the pointer to the byte where the parameter is found 290 */ 291void 292icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, const void *pointer) 293{ 294 u32_t pointer_u32 = (u32_t)((const u8_t *)pointer - (const u8_t *)ip6_current_header()); 295 icmp6_send_response(p, c, pointer_u32, ICMP6_TYPE_PP); 296} 297 298/** 299 * Send an ICMPv6 packet in response to an incoming packet. 300 * The packet is sent *to* ip_current_src_addr() on ip_current_netif(). 301 * 302 * @param p the input packet for which the response should be sent, 303 * p->payload pointing to the IPv6 header 304 * @param code Code of the ICMPv6 header 305 * @param data Additional 32-bit parameter in the ICMPv6 header 306 * @param type Type of the ICMPv6 header 307 */ 308static void 309icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type) 310{ 311 const struct ip6_addr *reply_src, *reply_dest; 312 struct netif *netif = ip_current_netif(); 313 314 LWIP_ASSERT("icmpv6 packet not a direct response", netif != NULL); 315 reply_dest = ip6_current_src_addr(); 316 317 /* Select an address to use as source. */ 318 reply_src = ip_2_ip6(ip6_select_source_address(netif, reply_dest)); 319 if (reply_src == NULL) { 320 ICMP6_STATS_INC(icmp6.rterr); 321 return; 322 } 323 icmp6_send_response_with_addrs_and_netif(p, code, data, type, reply_src, reply_dest, netif); 324} 325 326/** 327 * Send an ICMPv6 packet in response to an incoming packet. 328 * 329 * Call this function if the packet is NOT sent as a direct response to an 330 * incoming packet, but rather sometime later (e.g. for a fragment reassembly 331 * timeout). The caller must provide the zoned source and destination addresses 332 * from the original packet with the src_addr and dest_addr parameters. The 333 * reason for this approach is that while the addresses themselves are part of 334 * the original packet, their zone information is not, thus possibly resulting 335 * in a link-local response being sent over the wrong link. 336 * 337 * @param p the input packet for which the response should be sent, 338 * p->payload pointing to the IPv6 header 339 * @param code Code of the ICMPv6 header 340 * @param data Additional 32-bit parameter in the ICMPv6 header 341 * @param type Type of the ICMPv6 header 342 * @param src_addr original source address 343 * @param dest_addr original destination address 344 */ 345static void 346icmp6_send_response_with_addrs(struct pbuf *p, u8_t code, u32_t data, u8_t type, 347 const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr) 348{ 349 const struct ip6_addr *reply_src, *reply_dest; 350 struct netif *netif; 351 352 /* Get the destination address and netif for this ICMP message. */ 353 LWIP_ASSERT("must provide both source and destination", src_addr != NULL); 354 LWIP_ASSERT("must provide both source and destination", dest_addr != NULL); 355 356 /* Special case, as ip6_current_xxx is either NULL, or points 357 to a different packet than the one that expired. */ 358 IP6_ADDR_ZONECHECK(src_addr); 359 IP6_ADDR_ZONECHECK(dest_addr); 360 /* Swap source and destination for the reply. */ 361 reply_dest = src_addr; 362 reply_src = dest_addr; 363#ifdef LOSCFG_NET_CONTAINER 364 netif = ip6_route(reply_src, reply_dest, get_root_net_group()); 365#else 366 netif = ip6_route(reply_src, reply_dest); 367#endif 368 if (netif == NULL) { 369 ICMP6_STATS_INC(icmp6.rterr); 370 return; 371 } 372 icmp6_send_response_with_addrs_and_netif(p, code, data, type, reply_src, 373 reply_dest, netif); 374} 375 376/** 377 * Send an ICMPv6 packet (with srd/dst address and netif given). 378 * 379 * @param p the input packet for which the response should be sent, 380 * p->payload pointing to the IPv6 header 381 * @param code Code of the ICMPv6 header 382 * @param data Additional 32-bit parameter in the ICMPv6 header 383 * @param type Type of the ICMPv6 header 384 * @param reply_src source address of the packet to send 385 * @param reply_dest destination address of the packet to send 386 * @param netif netif to send the packet 387 */ 388static void 389icmp6_send_response_with_addrs_and_netif(struct pbuf *p, u8_t code, u32_t data, u8_t type, 390 const ip6_addr_t *reply_src, const ip6_addr_t *reply_dest, struct netif *netif) 391{ 392 struct pbuf *q; 393 struct icmp6_hdr *icmp6hdr; 394 u16_t datalen = LWIP_MIN(p->tot_len, LWIP_ICMP6_DATASIZE); 395 u16_t offset; 396 397 /* ICMPv6 header + datalen (as much of the offending packet as possible) */ 398 q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + datalen, 399 PBUF_RAM); 400 if (q == NULL) { 401 LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n")); 402 ICMP6_STATS_INC(icmp6.memerr); 403 return; 404 } 405 LWIP_ASSERT("check that first pbuf can hold icmp6 header", 406 (q->len >= (sizeof(struct icmp6_hdr)))); 407 408 icmp6hdr = (struct icmp6_hdr *)q->payload; 409 icmp6hdr->type = type; 410 icmp6hdr->code = code; 411 icmp6hdr->data = lwip_htonl(data); 412 413 /* copy fields from original packet (which may be a chain of pbufs) */ 414 offset = sizeof(struct icmp6_hdr); 415 while (p && datalen) { 416 u16_t len = LWIP_MIN(datalen, p->len); 417 err_t res = pbuf_take_at(q, p->payload, len, offset); 418 if (res != ERR_OK) break; 419 datalen -= len; 420 offset += len; 421 p = p->next; 422 } 423 424 /* calculate checksum */ 425 icmp6hdr->chksum = 0; 426#if CHECKSUM_GEN_ICMP6 427 IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) { 428 icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len, 429 reply_src, reply_dest); 430 } 431#endif /* CHECKSUM_GEN_ICMP6 */ 432 433 ICMP6_STATS_INC(icmp6.xmit); 434 ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); 435 pbuf_free(q); 436} 437 438#endif /* LWIP_ICMP6 && LWIP_IPV6 */ 439