xref: /minix/minix/lib/liblwip/dist/src/core/ipv6/icmp6.c (revision ef8d499e)
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