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