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 #ifndef LWIP_ICMP6_DATASIZE 61 #define LWIP_ICMP6_DATASIZE 8 62 #endif 63 #if LWIP_ICMP6_DATASIZE == 0 64 #define LWIP_ICMP6_DATASIZE 8 65 #endif 66 67 /* Forward declarations */ 68 static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type); 69 static void icmp6_send_response_with_addrs(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); 71 static void icmp6_send_response_with_addrs_and_netif(struct pbuf *p, u8_t code, u32_t data, 72 u8_t type, const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr, struct netif *netif); 73 74 75 /** 76 * Process an input ICMPv6 message. Called by ip6_input. 77 * 78 * Will generate a reply for echo requests. Other messages are forwarded 79 * to nd6_input, or mld6_input. 80 * 81 * @param p the mld packet, p->payload pointing to the icmpv6 header 82 * @param inp the netif on which this packet was received 83 */ 84 void 85 icmp6_input(struct pbuf *p, struct netif *inp) 86 { 87 struct icmp6_hdr *icmp6hdr; 88 struct pbuf *r; 89 const ip6_addr_t *reply_src; 90 91 ICMP6_STATS_INC(icmp6.recv); 92 93 /* Check that ICMPv6 header fits in payload */ 94 if (p->len < sizeof(struct icmp6_hdr)) { 95 /* drop short packets */ 96 pbuf_free(p); 97 ICMP6_STATS_INC(icmp6.lenerr); 98 ICMP6_STATS_INC(icmp6.drop); 99 return; 100 } 101 102 icmp6hdr = (struct icmp6_hdr *)p->payload; 103 104 #if CHECKSUM_CHECK_ICMP6 105 IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_ICMP6) { 106 if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(), 107 ip6_current_dest_addr()) != 0) { 108 /* Checksum failed */ 109 pbuf_free(p); 110 ICMP6_STATS_INC(icmp6.chkerr); 111 ICMP6_STATS_INC(icmp6.drop); 112 return; 113 } 114 } 115 #endif /* CHECKSUM_CHECK_ICMP6 */ 116 117 switch (icmp6hdr->type) { 118 case ICMP6_TYPE_NA: /* Neighbor advertisement */ 119 case ICMP6_TYPE_NS: /* Neighbor solicitation */ 120 #if !defined(__minix) 121 case ICMP6_TYPE_RA: /* Router advertisement */ 122 #endif /* !defined(__minix) */ 123 case ICMP6_TYPE_RD: /* Redirect */ 124 case ICMP6_TYPE_PTB: /* Packet too big */ 125 nd6_input(p, inp); 126 return; 127 break; 128 case ICMP6_TYPE_RS: 129 #if LWIP_IPV6_FORWARD 130 /* @todo implement router functionality */ 131 #endif 132 break; 133 #if LWIP_IPV6_MLD 134 case ICMP6_TYPE_MLQ: 135 case ICMP6_TYPE_MLR: 136 case ICMP6_TYPE_MLD: 137 mld6_input(p, inp); 138 return; 139 break; 140 #endif 141 case ICMP6_TYPE_EREQ: 142 #if !LWIP_MULTICAST_PING 143 /* multicast destination address? */ 144 if (ip6_addr_ismulticast(ip6_current_dest_addr())) { 145 /* drop */ 146 pbuf_free(p); 147 ICMP6_STATS_INC(icmp6.drop); 148 return; 149 } 150 #endif /* LWIP_MULTICAST_PING */ 151 152 /* Allocate reply. */ 153 r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM); 154 if (r == NULL) { 155 /* drop */ 156 pbuf_free(p); 157 ICMP6_STATS_INC(icmp6.memerr); 158 return; 159 } 160 161 /* Copy echo request. */ 162 if (pbuf_copy(r, p) != ERR_OK) { 163 /* drop */ 164 pbuf_free(p); 165 pbuf_free(r); 166 ICMP6_STATS_INC(icmp6.err); 167 return; 168 } 169 170 /* Determine reply source IPv6 address. */ 171 #if LWIP_MULTICAST_PING 172 if (ip6_addr_ismulticast(ip6_current_dest_addr())) { 173 reply_src = ip_2_ip6(ip6_select_source_address(inp, ip6_current_src_addr())); 174 if (reply_src == NULL) { 175 /* drop */ 176 pbuf_free(p); 177 pbuf_free(r); 178 ICMP6_STATS_INC(icmp6.rterr); 179 return; 180 } 181 } 182 else 183 #endif /* LWIP_MULTICAST_PING */ 184 { 185 reply_src = ip6_current_dest_addr(); 186 } 187 188 /* Set fields in reply. */ 189 ((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP; 190 ((struct icmp6_echo_hdr *)(r->payload))->chksum = 0; 191 #if CHECKSUM_GEN_ICMP6 192 IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP6) { 193 ((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r, 194 IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr()); 195 } 196 #endif /* CHECKSUM_GEN_ICMP6 */ 197 198 /* Send reply. */ 199 ICMP6_STATS_INC(icmp6.xmit); 200 ip6_output_if(r, reply_src, ip6_current_src_addr(), 201 LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, inp); 202 pbuf_free(r); 203 204 break; 205 default: 206 ICMP6_STATS_INC(icmp6.proterr); 207 ICMP6_STATS_INC(icmp6.drop); 208 break; 209 } 210 211 pbuf_free(p); 212 } 213 214 215 /** 216 * Send an icmpv6 'destination unreachable' packet. 217 * 218 * This function must be used only in direct response to a packet that is being 219 * received right now. Otherwise, address zones would be lost. 220 * 221 * @param p the input packet for which the 'unreachable' should be sent, 222 * p->payload pointing to the IPv6 header 223 * @param c ICMPv6 code for the unreachable type 224 */ 225 void 226 icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c) 227 { 228 icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR); 229 } 230 231 /** 232 * Send an icmpv6 'packet too big' packet. 233 * 234 * This function must be used only in direct response to a packet that is being 235 * received right now. Otherwise, address zones would be lost. 236 * 237 * @param p the input packet for which the 'packet too big' should be sent, 238 * p->payload pointing to the IPv6 header 239 * @param mtu the maximum mtu that we can accept 240 */ 241 void 242 icmp6_packet_too_big(struct pbuf *p, u32_t mtu) 243 { 244 icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB); 245 } 246 247 /** 248 * Send an icmpv6 'time exceeded' packet. 249 * 250 * This function must be used only in direct response to a packet that is being 251 * received right now. Otherwise, address zones would be lost. 252 * 253 * @param p the input packet for which the 'time exceeded' should be sent, 254 * p->payload pointing to the IPv6 header 255 * @param c ICMPv6 code for the time exceeded type 256 */ 257 void 258 icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c) 259 { 260 icmp6_send_response(p, c, 0, ICMP6_TYPE_TE); 261 } 262 263 /** 264 * Send an icmpv6 'time exceeded' packet, with explicit source and destination 265 * addresses. 266 * 267 * This function may be used to send a response sometime after receiving the 268 * packet for which this response is meant. The provided source and destination 269 * addresses are used primarily to retain their zone information. 270 * 271 * @param p the input packet for which the 'time exceeded' should be sent, 272 * p->payload pointing to the IPv6 header 273 * @param c ICMPv6 code for the time exceeded type 274 * @param src_addr source address of the original packet, with zone information 275 * @param dest_addr destination address of the original packet, with zone 276 * information 277 */ 278 void 279 icmp6_time_exceeded_with_addrs(struct pbuf *p, enum icmp6_te_code c, 280 const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr) 281 { 282 icmp6_send_response_with_addrs(p, c, 0, ICMP6_TYPE_TE, src_addr, dest_addr); 283 } 284 285 /** 286 * Send an icmpv6 'parameter problem' packet. 287 * 288 * This function must be used only in direct response to a packet that is being 289 * received right now. Otherwise, address zones would be lost. 290 * 291 * @param p the input packet for which the 'param problem' should be sent, 292 * p->payload pointing to the IP header 293 * @param c ICMPv6 code for the param problem type 294 * @param pointer the pointer to the byte where the parameter is found 295 */ 296 void 297 icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer) 298 { 299 icmp6_send_response(p, c, pointer, ICMP6_TYPE_PP); 300 } 301 302 /** 303 * Send an ICMPv6 packet in response to an incoming packet. 304 * The packet is sent *to* ip_current_src_addr() on ip_current_netif(). 305 * 306 * @param p the input packet for which the response should be sent, 307 * p->payload pointing to the IPv6 header 308 * @param code Code of the ICMPv6 header 309 * @param data Additional 32-bit parameter in the ICMPv6 header 310 * @param type Type of the ICMPv6 header 311 */ 312 static void 313 icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type) 314 { 315 const struct ip6_addr *reply_src, *reply_dest; 316 struct netif *netif = ip_current_netif(); 317 318 LWIP_ASSERT("icmpv6 packet not a direct response", netif != NULL); 319 reply_dest = ip6_current_src_addr(); 320 321 /* Select an address to use as source. */ 322 reply_src = ip_2_ip6(ip6_select_source_address(netif, reply_dest)); 323 if (reply_src == NULL) { 324 ICMP6_STATS_INC(icmp6.rterr); 325 return; 326 } 327 icmp6_send_response_with_addrs_and_netif(p, code, data, type, reply_src, reply_dest, netif); 328 } 329 330 /** 331 * Send an ICMPv6 packet in response to an incoming packet. 332 * 333 * Call this function if the packet is NOT sent as a direct response to an 334 * incoming packet, but rather sometime later (e.g. for a fragment reassembly 335 * timeout). The caller must provide the zoned source and destination addresses 336 * from the original packet with the src_addr and dest_addr parameters. The 337 * reason for this approach is that while the addresses themselves are part of 338 * the original packet, their zone information is not, thus possibly resulting 339 * in a link-local response being sent over the wrong link. 340 * 341 * @param p the input packet for which the response should be sent, 342 * p->payload pointing to the IPv6 header 343 * @param code Code of the ICMPv6 header 344 * @param data Additional 32-bit parameter in the ICMPv6 header 345 * @param type Type of the ICMPv6 header 346 * @param src_addr original source address 347 * @param dest_addr original destination address 348 */ 349 static void 350 icmp6_send_response_with_addrs(struct pbuf *p, u8_t code, u32_t data, u8_t type, 351 const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr) 352 { 353 const struct ip6_addr *reply_src, *reply_dest; 354 struct netif *netif; 355 356 /* Get the destination address and netif for this ICMP message. */ 357 LWIP_ASSERT("must provide both source and destination", src_addr != NULL); 358 LWIP_ASSERT("must provide both source and destination", dest_addr != NULL); 359 360 /* Special case, as ip6_current_xxx is either NULL, or points 361 to a different packet than the one that expired. */ 362 IP6_ADDR_ZONECHECK(src_addr); 363 IP6_ADDR_ZONECHECK(dest_addr); 364 /* Swap source and destination for the reply. */ 365 reply_dest = src_addr; 366 reply_src = dest_addr; 367 netif = ip6_route(reply_src, reply_dest); 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 */ 388 static void 389 icmp6_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 395 /* ICMPv6 header + IPv6 header + data */ 396 q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE, 397 PBUF_RAM); 398 if (q == NULL) { 399 LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n")); 400 ICMP6_STATS_INC(icmp6.memerr); 401 return; 402 } 403 LWIP_ASSERT("check that first pbuf can hold icmp 6message", 404 (q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE))); 405 406 icmp6hdr = (struct icmp6_hdr *)q->payload; 407 icmp6hdr->type = type; 408 icmp6hdr->code = code; 409 icmp6hdr->data = data; 410 411 /* copy fields from original packet */ 412 SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload, 413 IP6_HLEN + LWIP_ICMP6_DATASIZE); 414 415 /* calculate checksum */ 416 icmp6hdr->chksum = 0; 417 #if CHECKSUM_GEN_ICMP6 418 IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) { 419 icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len, 420 reply_src, reply_dest); 421 } 422 #endif /* CHECKSUM_GEN_ICMP6 */ 423 424 ICMP6_STATS_INC(icmp6.xmit); 425 ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); 426 pbuf_free(q); 427 } 428 429 #endif /* LWIP_ICMP6 && LWIP_IPV6 */ 430