• 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_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 */
66 static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type);
67 static 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);
69 static 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  */
82 void
icmp6_input(struct pbuf * p,struct netif * inp)83 icmp6_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  */
219 void
icmp6_dest_unreach(struct pbuf * p,enum icmp6_dur_code c)220 icmp6_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  */
235 void
icmp6_packet_too_big(struct pbuf * p,u32_t mtu)236 icmp6_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  */
251 void
icmp6_time_exceeded(struct pbuf * p,enum icmp6_te_code c)252 icmp6_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  */
272 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)273 icmp6_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  */
291 void
icmp6_param_problem(struct pbuf * p,enum icmp6_pp_code c,const void * pointer)292 icmp6_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  */
308 static void
icmp6_send_response(struct pbuf * p,u8_t code,u32_t data,u8_t type)309 icmp6_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  */
345 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)346 icmp6_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   netif = ip6_route(reply_src, reply_dest);
364   if (netif == NULL) {
365     ICMP6_STATS_INC(icmp6.rterr);
366     return;
367   }
368   icmp6_send_response_with_addrs_and_netif(p, code, data, type, reply_src,
369     reply_dest, netif);
370 }
371 
372 /**
373  * Send an ICMPv6 packet (with srd/dst address and netif given).
374  *
375  * @param p the input packet for which the response should be sent,
376  *          p->payload pointing to the IPv6 header
377  * @param code Code of the ICMPv6 header
378  * @param data Additional 32-bit parameter in the ICMPv6 header
379  * @param type Type of the ICMPv6 header
380  * @param reply_src source address of the packet to send
381  * @param reply_dest destination address of the packet to send
382  * @param netif netif to send the packet
383  */
384 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)385 icmp6_send_response_with_addrs_and_netif(struct pbuf *p, u8_t code, u32_t data, u8_t type,
386     const ip6_addr_t *reply_src, const ip6_addr_t *reply_dest, struct netif *netif)
387 {
388   struct pbuf *q;
389   struct icmp6_hdr *icmp6hdr;
390   u16_t datalen = LWIP_MIN(p->tot_len, LWIP_ICMP6_DATASIZE);
391   u16_t offset;
392 
393   /* ICMPv6 header + datalen (as much of the offending packet as possible) */
394   q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + datalen,
395                  PBUF_RAM);
396   if (q == NULL) {
397     LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n"));
398     ICMP6_STATS_INC(icmp6.memerr);
399     return;
400   }
401   LWIP_ASSERT("check that first pbuf can hold icmp6 header",
402              (q->len >= (sizeof(struct icmp6_hdr))));
403 
404   icmp6hdr = (struct icmp6_hdr *)q->payload;
405   icmp6hdr->type = type;
406   icmp6hdr->code = code;
407   icmp6hdr->data = lwip_htonl(data);
408 
409   /* copy fields from original packet (which may be a chain of pbufs) */
410   offset = sizeof(struct icmp6_hdr);
411   while (p && datalen) {
412     u16_t len = LWIP_MIN(datalen, p->len);
413     err_t res = pbuf_take_at(q, p->payload, len, offset);
414     if (res != ERR_OK) break;
415     datalen -= len;
416     offset += len;
417     p = p->next;
418   }
419 
420   /* calculate checksum */
421   icmp6hdr->chksum = 0;
422 #if CHECKSUM_GEN_ICMP6
423   IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
424     icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len,
425       reply_src, reply_dest);
426   }
427 #endif /* CHECKSUM_GEN_ICMP6 */
428 
429   ICMP6_STATS_INC(icmp6.xmit);
430   ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
431   pbuf_free(q);
432 }
433 
434 #endif /* LWIP_ICMP6 && LWIP_IPV6 */
435