• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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