]> rtime.felk.cvut.cz Git - pes-rpp/rpp-lwip.git/blob - src/core/ipv6/icmp6.c
ea82682efde7a99058fb28252e8e90aadc03597f
[pes-rpp/rpp-lwip.git] / src / core / ipv6 / icmp6.c
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/ip6.h"
48 #include "lwip/ip6_addr.h"
49 #include "lwip/inet_chksum.h"
50 #include "lwip/pbuf.h"
51 #include "lwip/netif.h"
52 #include "lwip/nd6.h"
53 #include "lwip/mld6.h"
54 #include "lwip/stats.h"
55
56 #include <string.h>
57
58 #ifndef LWIP_ICMP6_DATASIZE
59 #define LWIP_ICMP6_DATASIZE   8
60 #endif
61 #if LWIP_ICMP6_DATASIZE == 0
62 #define LWIP_ICMP6_DATASIZE   8
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
68
69 /**
70  * Process an input ICMPv6 message. Called by ip6_input.
71  *
72  * Will generate a reply for echo requests. Other messages are forwarded
73  * to nd6_input, or mld6_input.
74  *
75  * @param p the mld packet, p->payload pointing to the icmpv6 header
76  * @param inp the netif on which this packet was received
77  */
78 void
79 icmp6_input(struct pbuf *p, struct netif *inp)
80 {
81   struct icmp6_hdr *icmp6hdr;
82   struct pbuf * r;
83   ip6_addr_t * reply_src;
84
85   ICMP6_STATS_INC(icmp6.recv);
86
87   /* Check that ICMPv6 header fits in payload */
88   if (p->len < sizeof(struct icmp6_hdr)) {
89     /* drop short packets */
90     pbuf_free(p);
91     ICMP6_STATS_INC(icmp6.lenerr);
92     ICMP6_STATS_INC(icmp6.drop);
93     return;
94   }
95
96   icmp6hdr = (struct icmp6_hdr *)p->payload;
97
98 #if LWIP_ICMP6_CHECKSUM_CHECK
99   if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(),
100                         ip6_current_dest_addr()) != 0) {
101     /* Checksum failed */
102     pbuf_free(p);
103     ICMP6_STATS_INC(icmp6.chkerr);
104     ICMP6_STATS_INC(icmp6.drop);
105     return;
106   }
107 #endif /* LWIP_ICMP6_CHECKSUM_CHECK */
108
109   switch (icmp6hdr->type) {
110   case ICMP6_TYPE_NA: /* Neighbor advertisement */
111   case ICMP6_TYPE_NS: /* Neighbor solicitation */
112   case ICMP6_TYPE_RA: /* Router advertisement */
113   case ICMP6_TYPE_RD: /* Redirect */
114   case ICMP6_TYPE_PTB: /* Packet too big */
115     nd6_input(p, inp);
116     return;
117     break;
118   case ICMP6_TYPE_RS:
119 #if LWIP_IPV6_FORWARD
120     /* TODO implement router functionality */
121 #endif
122     break;
123 #if LWIP_IPV6_MLD
124   case ICMP6_TYPE_MLQ:
125   case ICMP6_TYPE_MLR:
126   case ICMP6_TYPE_MLD:
127     mld6_input(p, inp);
128     return;
129     break;
130 #endif
131   case ICMP6_TYPE_EREQ:
132 #if !LWIP_MULTICAST_PING
133     /* multicast destination address? */
134     if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
135       /* drop */
136       pbuf_free(p);
137       ICMP6_STATS_INC(icmp6.drop);
138       return;
139     }
140 #endif /* LWIP_MULTICAST_PING */
141
142     /* Allocate reply. */
143     r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM);
144     if (r == NULL) {
145       /* drop */
146       pbuf_free(p);
147       ICMP6_STATS_INC(icmp6.memerr);
148       return;
149     }
150
151     /* Copy echo request. */
152     if (pbuf_copy(r, p) != ERR_OK) {
153       /* drop */
154       pbuf_free(p);
155       pbuf_free(r);
156       ICMP6_STATS_INC(icmp6.err);
157       return;
158     }
159
160     /* Determine reply source IPv6 address. */
161 #if LWIP_MULTICAST_PING
162     if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
163       reply_src = ip6_select_source_address(inp, ip6_current_src_addr());
164       if (reply_src == NULL) {
165         /* drop */
166         pbuf_free(p);
167         pbuf_free(r);
168         ICMP6_STATS_INC(icmp6.rterr);
169         return;
170       }
171     }
172     else
173 #endif /* LWIP_MULTICAST_PING */
174     {
175       reply_src = ip6_current_dest_addr();
176     }
177
178     /* Set fields in reply. */
179     ((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP;
180     ((struct icmp6_echo_hdr *)(r->payload))->chksum = 0;
181     ((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r,
182         IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr());
183
184     /* Send reply. */
185     ICMP6_STATS_INC(icmp6.xmit);
186     ip6_output_if(r, reply_src, ip6_current_src_addr(),
187         LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, inp);
188     pbuf_free(r);
189
190     break;
191   default:
192     ICMP6_STATS_INC(icmp6.proterr);
193     ICMP6_STATS_INC(icmp6.drop);
194     break;
195   }
196
197   pbuf_free(p);
198 }
199
200
201 /**
202  * Send an icmpv6 'destination unreachable' packet.
203  *
204  * @param p the input packet for which the 'unreachable' should be sent,
205  *          p->payload pointing to the IPv6 header
206  * @param c ICMPv6 code for the unreachable type
207  */
208 void
209 icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c)
210 {
211   icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR);
212 }
213
214 /**
215  * Send an icmpv6 'packet too big' packet.
216  *
217  * @param p the input packet for which the 'packet too big' should be sent,
218  *          p->payload pointing to the IPv6 header
219  * @param mtu the maximum mtu that we can accept
220  */
221 void
222 icmp6_packet_too_big(struct pbuf *p, u32_t mtu)
223 {
224   icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB);
225 }
226
227 /**
228  * Send an icmpv6 'time exceeded' packet.
229  *
230  * @param p the input packet for which the 'unreachable' should be sent,
231  *          p->payload pointing to the IPv6 header
232  * @param c ICMPv6 code for the time exceeded type
233  */
234 void
235 icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c)
236 {
237   icmp6_send_response(p, c, 0, ICMP6_TYPE_TE);
238 }
239
240 /**
241  * Send an icmpv6 'parameter problem' packet.
242  *
243  * @param p the input packet for which the 'param problem' should be sent,
244  *          p->payload pointing to the IP header
245  * @param c ICMPv6 code for the param problem type
246  * @param pointer the pointer to the byte where the parameter is found
247  */
248 void
249 icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer)
250 {
251   icmp6_send_response(p, c, pointer, ICMP6_TYPE_PP);
252 }
253
254 /**
255  * Send an ICMPv6 packet in response to an incoming packet.
256  *
257  * @param p the input packet for which the response should be sent,
258  *          p->payload pointing to the IPv6 header
259  * @param code Code of the ICMPv6 header
260  * @param data Additional 32-bit parameter in the ICMPv6 header
261  * @param type Type of the ICMPv6 header
262  */
263 static void
264 icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type)
265 {
266   struct pbuf *q;
267   struct icmp6_hdr *icmp6hdr;
268   ip6_addr_t *reply_src, *reply_dest;
269   ip6_addr_t reply_src_local, reply_dest_local;
270   struct ip6_hdr *ip6hdr;
271   struct netif *netif;
272
273   /* ICMPv6 header + IPv6 header + data */
274   q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE,
275                  PBUF_RAM);
276   if (q == NULL) {
277     LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n"));
278     ICMP6_STATS_INC(icmp6.memerr);
279     return;
280   }
281   LWIP_ASSERT("check that first pbuf can hold icmp 6message",
282              (q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE)));
283
284   icmp6hdr = (struct icmp6_hdr *)q->payload;
285   icmp6hdr->type = type;
286   icmp6hdr->code = code;
287   icmp6hdr->data = data;
288
289   /* copy fields from original packet */
290   SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload,
291           IP6_HLEN + LWIP_ICMP6_DATASIZE);
292
293   /* Get the destination address and netif for this ICMP message. */
294   if ((ip_current_netif() == NULL) ||
295       ((code == ICMP6_TE_FRAG) && (type == ICMP6_TYPE_TE))) {
296     /* Special case, as ip6_current_xxx is either NULL, or points
297      * to a different packet than the one that expired.
298      * We must use the addresses that are stored in the expired packet. */
299     ip6hdr = (struct ip6_hdr *)p->payload;
300     /* copy from packed address to aligned address */
301     ip6_addr_copy(reply_dest_local, ip6hdr->src);
302     ip6_addr_copy(reply_src_local, ip6hdr->dest);
303     reply_dest = &reply_dest_local;
304     reply_src = &reply_src_local;
305     netif = ip6_route(reply_src, reply_dest);
306     if (netif == NULL) {
307       /* drop */
308       pbuf_free(q);
309       ICMP6_STATS_INC(icmp6.rterr);
310       return;
311     }
312   }
313   else {
314     netif = ip_current_netif();
315     reply_dest = ip6_current_src_addr();
316
317     /* Select an address to use as source. */
318     reply_src = ip6_select_source_address(netif, reply_dest);
319     if (reply_src == NULL) {
320       /* drop */
321       pbuf_free(q);
322       ICMP6_STATS_INC(icmp6.rterr);
323       return;
324     }
325   }
326
327   /* calculate checksum */
328   icmp6hdr->chksum = 0;
329   icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len,
330     reply_src, reply_dest);
331
332   ICMP6_STATS_INC(icmp6.xmit);
333   ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
334   pbuf_free(q);
335 }
336
337 #endif /* LWIP_ICMP6 && LWIP_IPV6 */