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_RIPPLE
61 #include "lwip/lwip_rpl.h"
62 #endif
63
64 #if LWIP_MPL
65 #include "mcast6.h"
66
67 #ifndef MCAST6_MPL_IN
68 #define MCAST6_MPL_IN mcast6_esmrf_icmp_in
69 #endif /* MCAST6_MPL_IN */
70 #endif /* LWIP_MPL */
71 #if !LWIP_ICMP6_DATASIZE || (LWIP_ICMP6_DATASIZE > (IP6_MIN_MTU_LENGTH - IP6_HLEN - ICMP6_HLEN))
72 #undef LWIP_ICMP6_DATASIZE
73 #define LWIP_ICMP6_DATASIZE (IP6_MIN_MTU_LENGTH - IP6_HLEN - ICMP6_HLEN)
74 #endif
75
76 #if LWIP_ICMP6_ERR_RT_LMT
77 /* icmp6 rate limit queue */
78 struct icmp6_err_bkt icmp6_errbkt;
79 #endif
80
81 /* Forward declarations */
82 static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type);
83 static void icmp6_send_response_with_addrs(struct pbuf *p, u8_t code, u32_t data,
84 u8_t type, const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr);
85 static void icmp6_send_response_with_addrs_and_netif(struct pbuf *p, u8_t code, u32_t data,
86 u8_t type, const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr, struct netif *netif);
87
88
89 /**
90 * Process an input ICMPv6 message. Called by ip6_input.
91 *
92 * Will generate a reply for echo requests. Other messages are forwarded
93 * to nd6_input, or mld6_input.
94 *
95 * @param p the mld packet, p->payload pointing to the icmpv6 header
96 * @param inp the netif on which this packet was received
97 */
98 void
icmp6_input(struct pbuf * p,struct netif * inp)99 icmp6_input(struct pbuf *p, struct netif *inp)
100 {
101 struct icmpv6_hdr *icmp6hdr;
102 struct pbuf *r;
103 const ip6_addr_t *reply_src;
104
105 ICMP6_STATS_INC(icmp6.recv);
106
107 /* Check that ICMPv6 header fits in payload */
108 if (p->len < sizeof(struct icmpv6_hdr)) {
109 /* drop short packets */
110 pbuf_free(p);
111 ICMP6_STATS_INC(icmp6.lenerr);
112 ICMP6_STATS_INC(icmp6.drop);
113 return;
114 }
115
116 icmp6hdr = (struct icmpv6_hdr *)p->payload;
117 #if LWIP_RIPPLE && defined(LWIP_NA_PROXY) && LWIP_NA_PROXY
118 if ((p->na_proxy == lwIP_TRUE) && (icmp6hdr->type != ICMP6_TYPE_NS)) {
119 (void)pbuf_free(p);
120 return;
121 }
122 #endif
123
124 #if CHECKSUM_CHECK_ICMP6
125 IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_ICMP6) {
126 if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(),
127 ip6_current_dest_addr()) != 0) {
128 /* Checksum failed */
129 pbuf_free(p);
130 ICMP6_STATS_INC(icmp6.chkerr);
131 ICMP6_STATS_INC(icmp6.drop);
132 return;
133 }
134 }
135 #endif /* CHECKSUM_CHECK_ICMP6 */
136
137 switch (icmp6hdr->type) {
138 case ICMP6_TYPE_NA: /* Neighbor advertisement */
139 case ICMP6_TYPE_NS: /* Neighbor solicitation */
140 case ICMP6_TYPE_RA: /* Router advertisement */
141 case ICMP6_TYPE_RD: /* Redirect */
142 case ICMP6_TYPE_PTB: /* Packet too big */
143 nd6_input(p, inp);
144 return;
145 case ICMP6_TYPE_RS:
146 #if LWIP_IPV6_FORWARD
147 /* @todo implement router functionality */
148 #endif
149 #if LWIP_ND6_ROUTER
150 #if LWIP_RIPPLE && LWIP_RPL_RS_DAO
151 /*
152 * just send DAO message
153 * when the node is mesh gate
154 */
155 if (lwip_rpl_is_router()) {
156 nd6_rs_input(p, inp, ND6_RS_FLAG_DAO);
157 return;
158 }
159 #endif
160 nd6_rs_input(p, inp, ND6_RS_FLAG_RA);
161 #endif
162 return;
163 #if (LWIP_IPV6_MLD || LWIP_IPV6_MLD_QUERIER)
164 case ICMP6_TYPE_MLQ:
165 case ICMP6_TYPE_MLR:
166 case ICMP6_TYPE_MLD:
167 mld6_input(p, inp);
168 return;
169 #endif
170 case ICMP6_TYPE_EREQ:
171 #if !LWIP_MULTICAST_PING
172 /* multicast destination address? */
173 if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
174 /* drop */
175 pbuf_free(p);
176 ICMP6_STATS_INC(icmp6.drop);
177 return;
178 }
179 #endif /* LWIP_MULTICAST_PING */
180
181 /* Allocate reply. */
182 r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM);
183 if (r == NULL) {
184 /* drop */
185 pbuf_free(p);
186 ICMP6_STATS_INC(icmp6.memerr);
187 return;
188 }
189
190 /* Copy echo request. */
191 if (pbuf_copy(r, p) != ERR_OK) {
192 /* drop */
193 pbuf_free(p);
194 pbuf_free(r);
195 ICMP6_STATS_INC(icmp6.err);
196 return;
197 }
198
199 /* Determine reply source IPv6 address. */
200 #if LWIP_MULTICAST_PING
201 if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
202 reply_src = ip_2_ip6(ip6_select_source_address(inp, ip6_current_src_addr()));
203 if (reply_src == NULL) {
204 /* drop */
205 pbuf_free(p);
206 pbuf_free(r);
207 ICMP6_STATS_INC(icmp6.rterr);
208 return;
209 }
210 }
211 else
212 #endif /* LWIP_MULTICAST_PING */
213 {
214 reply_src = ip6_current_dest_addr();
215 }
216
217 /* Set fields in reply. */
218 ((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP;
219 ((struct icmp6_echo_hdr *)(r->payload))->chksum = 0;
220 #if CHECKSUM_GEN_ICMP6
221 IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP6) {
222 ((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r,
223 IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr());
224 }
225 #endif /* CHECKSUM_GEN_ICMP6 */
226
227 /* Send reply. */
228 ICMP6_STATS_INC(icmp6.xmit);
229 ip6_output_if(r, reply_src, ip6_current_src_addr(),
230 LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, inp);
231 pbuf_free(r);
232
233 break;
234 #if LWIP_RIPPLE
235 case ICMP6_TYPE_RPL:
236 lwip_handle_rpl_ctrl_msg(icmp6hdr, p, ip6_current_src_addr(), ip6_current_dest_addr(), inp);
237
238 break;
239 #endif /* LWIP_RIPPLE */
240 case ICMP6_TYPE_EREP:
241 ICMP6_STATS_INC(icmp6.drop);
242 LWIP_DEBUGF(ICMP_DEBUG, ("icmp6_input: ICMPv6 type %"S16_F" code %"S16_F" is dropped.\n",
243 (s16_t)icmp6hdr->type, (s16_t)icmp6hdr->code));
244 break;
245
246 #if LWIP_STATS
247 case ICMP6_TYPE_DUR:
248 MIB2_STATS_INC(mib2.icmpindestunreachs);
249 ICMP6_STATS_INC(icmp6.destunreachs);
250 LWIP_DEBUGF(ICMP_DEBUG, ("icmp6_input: ICMPv6 type %"S16_F" code %"S16_F" not supported.\n",
251 (s16_t)icmp6hdr->type, (s16_t)icmp6hdr->code));
252
253 ICMP6_STATS_INC(icmp6.drop);
254 break;
255
256 case ICMP6_TYPE_TE:
257 MIB2_STATS_INC(mib2.icmpintimeexcds);
258 ICMP6_STATS_INC(icmp6.timeexcds);
259 LWIP_DEBUGF(ICMP_DEBUG, ("icmp6_input: ICMPv6 type %"S16_F" code %"S16_F" not supported.\n",
260 (s16_t)icmp6hdr->type, (s16_t)icmp6hdr->code));
261
262 ICMP6_STATS_INC(icmp6.drop);
263 break;
264
265 case ICMP6_TYPE_PP:
266 MIB2_STATS_INC(mib2.icmpinparmprobs);
267 ICMP6_STATS_INC(icmp6.parmprobs);
268 LWIP_DEBUGF(ICMP_DEBUG, ("icmp6_input: ICMPv6 type %"S16_F" code %"S16_F" not supported.\n",
269 (s16_t)icmp6hdr->type, (s16_t)icmp6hdr->code));
270
271 ICMP6_STATS_INC(icmp6.drop);
272 break;
273 #endif
274
275 #if LWIP_MPL
276 case ICMP6_TYPE_ESMRF:
277 LWIP_DEBUGF(ICMP_DEBUG, ("icmp6_input: ICMP6_TYPE_ESMRF\n"));
278 MCAST6_MPL_IN(p, (u8_t *)(ip6_current_src_addr()));
279 break;
280 #endif /* LWIP_MPL */
281
282 default:
283 LWIP_DEBUGF(ICMP_DEBUG, ("icmp6_input: ICMPv6 type %"S16_F" code %"S16_F" not supported.\n",
284 (s16_t)icmp6hdr->type, (s16_t)icmp6hdr->code));
285
286 ICMP6_STATS_INC(icmp6.proterr);
287 ICMP6_STATS_INC(icmp6.drop);
288 break;
289 }
290
291 pbuf_free(p);
292 }
293
294
295 /**
296 * Send an icmpv6 'destination unreachable' packet.
297 *
298 * This function must be used only in direct response to a packet that is being
299 * received right now. Otherwise, address zones would be lost.
300 *
301 * @param p the input packet for which the 'unreachable' should be sent,
302 * p->payload pointing to the IPv6 header
303 * @param c ICMPv6 code for the unreachable type
304 */
305 void
icmp6_dest_unreach(struct pbuf * p,enum icmp6_dur_code c)306 icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c)
307 {
308 icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR);
309 }
310
311 /**
312 * Send an icmpv6 'packet too big' packet.
313 *
314 * This function must be used only in direct response to a packet that is being
315 * received right now. Otherwise, address zones would be lost.
316 *
317 * @param p the input packet for which the 'packet too big' should be sent,
318 * p->payload pointing to the IPv6 header
319 * @param mtu the maximum mtu that we can accept
320 */
321 void
icmp6_packet_too_big(struct pbuf * p,u32_t mtu)322 icmp6_packet_too_big(struct pbuf *p, u32_t mtu)
323 {
324 icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB);
325 }
326
327 /**
328 * Send an icmpv6 'time exceeded' packet.
329 *
330 * This function must be used only in direct response to a packet that is being
331 * received right now. Otherwise, address zones would be lost.
332 *
333 * @param p the input packet for which the 'time exceeded' should be sent,
334 * p->payload pointing to the IPv6 header
335 * @param c ICMPv6 code for the time exceeded type
336 */
337 void
icmp6_time_exceeded(struct pbuf * p,enum icmp6_te_code c)338 icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c)
339 {
340 icmp6_send_response(p, c, 0, ICMP6_TYPE_TE);
341 }
342
343 /**
344 * Send an icmpv6 'time exceeded' packet, with explicit source and destination
345 * addresses.
346 *
347 * This function may be used to send a response sometime after receiving the
348 * packet for which this response is meant. The provided source and destination
349 * addresses are used primarily to retain their zone information.
350 *
351 * @param p the input packet for which the 'time exceeded' should be sent,
352 * p->payload pointing to the IPv6 header
353 * @param c ICMPv6 code for the time exceeded type
354 * @param src_addr source address of the original packet, with zone information
355 * @param dest_addr destination address of the original packet, with zone
356 * information
357 */
358 void
icmp6_time_exceeded_with_addrs(struct pbuf * p,enum icmp6_te_code c,const ip6_addr_t * src_addr,const ip6_addr_t * dest_addr)359 icmp6_time_exceeded_with_addrs(struct pbuf *p, enum icmp6_te_code c,
360 const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr)
361 {
362 icmp6_send_response_with_addrs(p, c, 0, ICMP6_TYPE_TE, src_addr, dest_addr);
363 }
364
365 /**
366 * Send an icmpv6 'parameter problem' packet.
367 *
368 * This function must be used only in direct response to a packet that is being
369 * received right now. Otherwise, address zones would be lost and the calculated
370 * offset would be wrong (calculated against ip6_current_header()).
371 *
372 * @param p the input packet for which the 'param problem' should be sent,
373 * p->payload pointing to the IP header
374 * @param c ICMPv6 code for the param problem type
375 * @param pointer the pointer to the byte where the parameter is found
376 */
377 void
icmp6_param_problem(struct pbuf * p,enum icmp6_pp_code c,const void * pointer)378 icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, const void *pointer)
379 {
380 u32_t pointer_u32 = (u32_t)((const u8_t *)pointer - (const u8_t *)ip6_current_header());
381 icmp6_send_response(p, c, pointer_u32, ICMP6_TYPE_PP);
382 }
383
384 #if LWIP_ICMP6_ERR_RT_LMT
385
386 /* Invoked when the error message is receieved */
icmp6_err_update_rate_lmt()387 u8_t icmp6_err_update_rate_lmt()
388 {
389 struct icmp6_err_bkt *p = &icmp6_errbkt;
390 u32_t avg = 0;
391 u16_t i;
392
393 p->icmp_bkt[p->bkt_index]++;
394 for (i = 0; i < ICMP6_ERR_BKT_SIZE; i++) {
395 avg += p->icmp_bkt[i];
396 }
397
398 /* calculate the running average and compare with the threshold */
399 p->avg = avg / ICMP6_ERR_BKT_SIZE;
400
401 /* drop the packet */
402 return(p->avg >= (u32_t)ICMP6_ERR_THRESHOLD_LEVEL);
403 }
404
405
406 /* invoked through nd6 timer. No need to create one more timer for this */
icmp6_err_rate_calc(void)407 void icmp6_err_rate_calc(void)
408 {
409 struct icmp6_err_bkt *p = &icmp6_errbkt;
410
411 p->bkt_index++;
412 if (p->bkt_index >= (ICMP6_ERR_BKT_SIZE)) {
413 p->bkt_index = 0;
414 }
415
416 p->icmp_bkt[p->bkt_index] = 0;
417
418 return;
419 }
420 #endif
421
422 /**
423 * Send an ICMPv6 packet in response to an incoming packet.
424 * The packet is sent *to* ip_current_src_addr() on ip_current_netif().
425 *
426 * @param p the input packet for which the response should be sent,
427 * p->payload pointing to the IPv6 header
428 * @param code Code of the ICMPv6 header
429 * @param data Additional 32-bit parameter in the ICMPv6 header
430 * @param type Type of the ICMPv6 header
431 */
432 static void
icmp6_send_response(struct pbuf * p,u8_t code,u32_t data,u8_t type)433 icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type)
434 {
435 const struct ip6_addr *reply_src, *reply_dest;
436 ip6_addr_t reply_src_local, reply_dest_local;
437 struct ip6_hdr *ip6hdr = NULL;
438 struct netif *netif = NULL;
439 #if LWIP_ICMP6_ERR_RT_LMT
440 if (icmp6_err_update_rate_lmt()) {
441 LWIP_DEBUGF(ICMP_DEBUG,
442 ("icmp_time_exceeded: rate limit execceded for the packet so dropping the error icmp packet.\n"));
443 ICMP6_STATS_INC(icmp6.drop);
444 return;
445 }
446 #endif
447
448 /* Get the destination address and netif for this ICMP message. */
449 if ((ip_current_netif() == NULL) ||
450 ((code == ICMP6_TE_FRAG) && (type == ICMP6_TYPE_TE))) {
451 /* Special case, as ip6_current_xxx is either NULL, or points
452 * to a different packet than the one that expired.
453 * We must use the addresses that are stored in the expired packet.
454 */
455 ip6hdr = (struct ip6_hdr *)p->payload;
456 /* copy from packed address to aligned address */
457 ip6_addr_copy_from_packed(reply_dest_local, ip6hdr->src);
458 ip6_addr_copy_from_packed(reply_src_local, ip6hdr->dest);
459
460 reply_dest = &reply_dest_local;
461 reply_src = &reply_src_local;
462 netif = ip6_route(reply_src, reply_dest);
463 if (netif == NULL) {
464 ICMP6_STATS_INC(icmp6.rterr);
465 return;
466 }
467 ip6_addr_assign_zone(&reply_dest_local, IP6_UNICAST, netif);
468 ip6_addr_assign_zone(&reply_src_local, IP6_UNICAST, netif);
469 } else {
470 netif = ip_current_netif();
471 reply_dest = ip6_current_src_addr();
472
473 /* Select an address to use as source. */
474 reply_src = ip_2_ip6(ip6_select_source_address(netif, reply_dest));
475 if (reply_src == NULL) {
476 /* drop */
477 ICMP6_STATS_INC(icmp6.rterr);
478 return;
479 }
480 }
481
482 #if LWIP_IP6IN4
483 /* when the ip6in4 handle fail, it should not send the icmpv6. just drop it. */
484 if (p->ip6in4_ip4 == lwIP_TRUE) {
485 return;
486 }
487 #endif
488
489 icmp6_send_response_with_addrs_and_netif(p, code, data, type, reply_src, reply_dest, netif);
490 }
491
492 /**
493 * Send an ICMPv6 packet in response to an incoming packet.
494 *
495 * Call this function if the packet is NOT sent as a direct response to an
496 * incoming packet, but rather sometime later (e.g. for a fragment reassembly
497 * timeout). The caller must provide the zoned source and destination addresses
498 * from the original packet with the src_addr and dest_addr parameters. The
499 * reason for this approach is that while the addresses themselves are part of
500 * the original packet, their zone information is not, thus possibly resulting
501 * in a link-local response being sent over the wrong link.
502 *
503 * @param p the input packet for which the response should be sent,
504 * p->payload pointing to the IPv6 header
505 * @param code Code of the ICMPv6 header
506 * @param data Additional 32-bit parameter in the ICMPv6 header
507 * @param type Type of the ICMPv6 header
508 * @param src_addr original source address
509 * @param dest_addr original destination address
510 */
511 static void
icmp6_send_response_with_addrs(struct pbuf * p,u8_t code,u32_t data,u8_t type,const ip6_addr_t * src_addr,const ip6_addr_t * dest_addr)512 icmp6_send_response_with_addrs(struct pbuf *p, u8_t code, u32_t data, u8_t type,
513 const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr)
514 {
515 const struct ip6_addr *reply_src, *reply_dest;
516 struct netif *netif;
517
518 /* Get the destination address and netif for this ICMP message. */
519 LWIP_ASSERT("must provide both source and destination", src_addr != NULL);
520 LWIP_ASSERT("must provide both source and destination", dest_addr != NULL);
521
522 /* Special case, as ip6_current_xxx is either NULL, or points
523 to a different packet than the one that expired. */
524 IP6_ADDR_ZONECHECK(src_addr);
525 IP6_ADDR_ZONECHECK(dest_addr);
526 /* Swap source and destination for the reply. */
527 reply_dest = src_addr;
528 reply_src = dest_addr;
529 netif = ip6_route(reply_src, reply_dest);
530 if (netif == NULL) {
531 ICMP6_STATS_INC(icmp6.rterr);
532 return;
533 }
534 icmp6_send_response_with_addrs_and_netif(p, code, data, type, reply_src,
535 reply_dest, netif);
536 }
537
538 /**
539 * Send an ICMPv6 packet (with srd/dst address and netif given).
540 *
541 * @param p the input packet for which the response should be sent,
542 * p->payload pointing to the IPv6 header
543 * @param code Code of the ICMPv6 header
544 * @param data Additional 32-bit parameter in the ICMPv6 header
545 * @param type Type of the ICMPv6 header
546 * @param reply_src source address of the packet to send
547 * @param reply_dest destination address of the packet to send
548 * @param netif netif to send the packet
549 */
550 static void
icmp6_send_response_with_addrs_and_netif(struct pbuf * p,u8_t code,u32_t data,u8_t type,const ip6_addr_t * reply_src,const ip6_addr_t * reply_dest,struct netif * netif)551 icmp6_send_response_with_addrs_and_netif(struct pbuf *p, u8_t code, u32_t data, u8_t type,
552 const ip6_addr_t *reply_src, const ip6_addr_t *reply_dest, struct netif *netif)
553 {
554 struct pbuf *q;
555 struct icmpv6_hdr *icmp6hdr;
556 u16_t datalen = LWIP_MIN(p->tot_len, LWIP_ICMP6_DATASIZE);
557 u16_t offset;
558
559 /* ICMPv6 header + datalen (as much of the offending packet as possible) */
560 q = pbuf_alloc(PBUF_IP, sizeof(struct icmpv6_hdr) + datalen,
561 PBUF_RAM);
562 if (q == NULL) {
563 LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n"));
564 ICMP6_STATS_INC(icmp6.memerr);
565 return;
566 }
567 LWIP_ASSERT("check that first pbuf can hold icmp6 header",
568 (q->len >= (sizeof(struct icmpv6_hdr))));
569
570 icmp6hdr = (struct icmpv6_hdr *)q->payload;
571 icmp6hdr->type = type;
572 icmp6hdr->code = code;
573 icmp6hdr->data = lwip_htonl(data);
574
575 /* copy fields from original packet (which may be a chain of pbufs) */
576 offset = sizeof(struct icmpv6_hdr);
577 while (p && datalen) {
578 u16_t len = LWIP_MIN(datalen, p->len);
579 err_t res = pbuf_take_at(q, p->payload, len, offset);
580 if (res != ERR_OK) break;
581 datalen -= len;
582 offset += len;
583 p = p->next;
584 }
585
586 /* calculate checksum */
587 icmp6hdr->chksum = 0;
588 #if CHECKSUM_GEN_ICMP6
589 IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
590 icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len,
591 reply_src, reply_dest);
592 }
593 #endif /* CHECKSUM_GEN_ICMP6 */
594
595 ICMP6_STATS_INC(icmp6.xmit);
596 ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
597 pbuf_free(q);
598 }
599
600 #endif /* LWIP_ICMP6 && LWIP_IPV6 */
601