BadVPN – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 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 | case ICMP6_TYPE_RA: /* Router advertisement */ |
||
121 | case ICMP6_TYPE_RD: /* Redirect */ |
||
122 | case ICMP6_TYPE_PTB: /* Packet too big */ |
||
123 | nd6_input(p, inp); |
||
124 | return; |
||
125 | case ICMP6_TYPE_RS: |
||
126 | #if LWIP_IPV6_FORWARD |
||
127 | /* @todo implement router functionality */ |
||
128 | #endif |
||
129 | break; |
||
130 | #if LWIP_IPV6_MLD |
||
131 | case ICMP6_TYPE_MLQ: |
||
132 | case ICMP6_TYPE_MLR: |
||
133 | case ICMP6_TYPE_MLD: |
||
134 | mld6_input(p, inp); |
||
135 | return; |
||
136 | #endif |
||
137 | case ICMP6_TYPE_EREQ: |
||
138 | #if !LWIP_MULTICAST_PING |
||
139 | /* multicast destination address? */ |
||
140 | if (ip6_addr_ismulticast(ip6_current_dest_addr())) { |
||
141 | /* drop */ |
||
142 | pbuf_free(p); |
||
143 | ICMP6_STATS_INC(icmp6.drop); |
||
144 | return; |
||
145 | } |
||
146 | #endif /* LWIP_MULTICAST_PING */ |
||
147 | |||
148 | /* Allocate reply. */ |
||
149 | r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM); |
||
150 | if (r == NULL) { |
||
151 | /* drop */ |
||
152 | pbuf_free(p); |
||
153 | ICMP6_STATS_INC(icmp6.memerr); |
||
154 | return; |
||
155 | } |
||
156 | |||
157 | /* Copy echo request. */ |
||
158 | if (pbuf_copy(r, p) != ERR_OK) { |
||
159 | /* drop */ |
||
160 | pbuf_free(p); |
||
161 | pbuf_free(r); |
||
162 | ICMP6_STATS_INC(icmp6.err); |
||
163 | return; |
||
164 | } |
||
165 | |||
166 | /* Determine reply source IPv6 address. */ |
||
167 | #if LWIP_MULTICAST_PING |
||
168 | if (ip6_addr_ismulticast(ip6_current_dest_addr())) { |
||
169 | reply_src = ip_2_ip6(ip6_select_source_address(inp, ip6_current_src_addr())); |
||
170 | if (reply_src == NULL) { |
||
171 | /* drop */ |
||
172 | pbuf_free(p); |
||
173 | pbuf_free(r); |
||
174 | ICMP6_STATS_INC(icmp6.rterr); |
||
175 | return; |
||
176 | } |
||
177 | } |
||
178 | else |
||
179 | #endif /* LWIP_MULTICAST_PING */ |
||
180 | { |
||
181 | reply_src = ip6_current_dest_addr(); |
||
182 | } |
||
183 | |||
184 | /* Set fields in reply. */ |
||
185 | ((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP; |
||
186 | ((struct icmp6_echo_hdr *)(r->payload))->chksum = 0; |
||
187 | #if CHECKSUM_GEN_ICMP6 |
||
188 | IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP6) { |
||
189 | ((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r, |
||
190 | IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr()); |
||
191 | } |
||
192 | #endif /* CHECKSUM_GEN_ICMP6 */ |
||
193 | |||
194 | /* Send reply. */ |
||
195 | ICMP6_STATS_INC(icmp6.xmit); |
||
196 | ip6_output_if(r, reply_src, ip6_current_src_addr(), |
||
197 | LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, inp); |
||
198 | pbuf_free(r); |
||
199 | |||
200 | break; |
||
201 | default: |
||
202 | ICMP6_STATS_INC(icmp6.proterr); |
||
203 | ICMP6_STATS_INC(icmp6.drop); |
||
204 | break; |
||
205 | } |
||
206 | |||
207 | pbuf_free(p); |
||
208 | } |
||
209 | |||
210 | |||
211 | /** |
||
212 | * Send an icmpv6 'destination unreachable' packet. |
||
213 | * |
||
214 | * This function must be used only in direct response to a packet that is being |
||
215 | * received right now. Otherwise, address zones would be lost. |
||
216 | * |
||
217 | * @param p the input packet for which the 'unreachable' should be sent, |
||
218 | * p->payload pointing to the IPv6 header |
||
219 | * @param c ICMPv6 code for the unreachable type |
||
220 | */ |
||
221 | void |
||
222 | icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c) |
||
223 | { |
||
224 | icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR); |
||
225 | } |
||
226 | |||
227 | /** |
||
228 | * Send an icmpv6 'packet too big' packet. |
||
229 | * |
||
230 | * This function must be used only in direct response to a packet that is being |
||
231 | * received right now. Otherwise, address zones would be lost. |
||
232 | * |
||
233 | * @param p the input packet for which the 'packet too big' should be sent, |
||
234 | * p->payload pointing to the IPv6 header |
||
235 | * @param mtu the maximum mtu that we can accept |
||
236 | */ |
||
237 | void |
||
238 | icmp6_packet_too_big(struct pbuf *p, u32_t mtu) |
||
239 | { |
||
240 | icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB); |
||
241 | } |
||
242 | |||
243 | /** |
||
244 | * Send an icmpv6 'time exceeded' packet. |
||
245 | * |
||
246 | * This function must be used only in direct response to a packet that is being |
||
247 | * received right now. Otherwise, address zones would be lost. |
||
248 | * |
||
249 | * @param p the input packet for which the 'time exceeded' should be sent, |
||
250 | * p->payload pointing to the IPv6 header |
||
251 | * @param c ICMPv6 code for the time exceeded type |
||
252 | */ |
||
253 | void |
||
254 | icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c) |
||
255 | { |
||
256 | icmp6_send_response(p, c, 0, ICMP6_TYPE_TE); |
||
257 | } |
||
258 | |||
259 | /** |
||
260 | * Send an icmpv6 'time exceeded' packet, with explicit source and destination |
||
261 | * addresses. |
||
262 | * |
||
263 | * This function may be used to send a response sometime after receiving the |
||
264 | * packet for which this response is meant. The provided source and destination |
||
265 | * addresses are used primarily to retain their zone information. |
||
266 | * |
||
267 | * @param p the input packet for which the 'time exceeded' should be sent, |
||
268 | * p->payload pointing to the IPv6 header |
||
269 | * @param c ICMPv6 code for the time exceeded type |
||
270 | * @param src_addr source address of the original packet, with zone information |
||
271 | * @param dest_addr destination address of the original packet, with zone |
||
272 | * information |
||
273 | */ |
||
274 | void |
||
275 | icmp6_time_exceeded_with_addrs(struct pbuf *p, enum icmp6_te_code c, |
||
276 | const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr) |
||
277 | { |
||
278 | icmp6_send_response_with_addrs(p, c, 0, ICMP6_TYPE_TE, src_addr, dest_addr); |
||
279 | } |
||
280 | |||
281 | /** |
||
282 | * Send an icmpv6 'parameter problem' packet. |
||
283 | * |
||
284 | * This function must be used only in direct response to a packet that is being |
||
285 | * received right now. Otherwise, address zones would be lost and the calculated |
||
286 | * offset would be wrong (calculated against ip6_current_header()). |
||
287 | * |
||
288 | * @param p the input packet for which the 'param problem' should be sent, |
||
289 | * p->payload pointing to the IP header |
||
290 | * @param c ICMPv6 code for the param problem type |
||
291 | * @param pointer the pointer to the byte where the parameter is found |
||
292 | */ |
||
293 | void |
||
294 | icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, const void *pointer) |
||
295 | { |
||
296 | u32_t pointer_u32 = (u32_t)((const u8_t *)pointer - (const u8_t *)ip6_current_header()); |
||
297 | icmp6_send_response(p, c, pointer_u32, ICMP6_TYPE_PP); |
||
298 | } |
||
299 | |||
300 | /** |
||
301 | * Send an ICMPv6 packet in response to an incoming packet. |
||
302 | * The packet is sent *to* ip_current_src_addr() on ip_current_netif(). |
||
303 | * |
||
304 | * @param p the input packet for which the response should be sent, |
||
305 | * p->payload pointing to the IPv6 header |
||
306 | * @param code Code of the ICMPv6 header |
||
307 | * @param data Additional 32-bit parameter in the ICMPv6 header |
||
308 | * @param type Type of the ICMPv6 header |
||
309 | */ |
||
310 | static void |
||
311 | icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type) |
||
312 | { |
||
313 | const struct ip6_addr *reply_src, *reply_dest; |
||
314 | struct netif *netif = ip_current_netif(); |
||
315 | |||
316 | LWIP_ASSERT("icmpv6 packet not a direct response", netif != NULL); |
||
317 | reply_dest = ip6_current_src_addr(); |
||
318 | |||
319 | /* Select an address to use as source. */ |
||
320 | reply_src = ip_2_ip6(ip6_select_source_address(netif, reply_dest)); |
||
321 | if (reply_src == NULL) { |
||
322 | ICMP6_STATS_INC(icmp6.rterr); |
||
323 | return; |
||
324 | } |
||
325 | icmp6_send_response_with_addrs_and_netif(p, code, data, type, reply_src, reply_dest, netif); |
||
326 | } |
||
327 | |||
328 | /** |
||
329 | * Send an ICMPv6 packet in response to an incoming packet. |
||
330 | * |
||
331 | * Call this function if the packet is NOT sent as a direct response to an |
||
332 | * incoming packet, but rather sometime later (e.g. for a fragment reassembly |
||
333 | * timeout). The caller must provide the zoned source and destination addresses |
||
334 | * from the original packet with the src_addr and dest_addr parameters. The |
||
335 | * reason for this approach is that while the addresses themselves are part of |
||
336 | * the original packet, their zone information is not, thus possibly resulting |
||
337 | * in a link-local response being sent over the wrong link. |
||
338 | * |
||
339 | * @param p the input packet for which the response should be sent, |
||
340 | * p->payload pointing to the IPv6 header |
||
341 | * @param code Code of the ICMPv6 header |
||
342 | * @param data Additional 32-bit parameter in the ICMPv6 header |
||
343 | * @param type Type of the ICMPv6 header |
||
344 | * @param src_addr original source address |
||
345 | * @param dest_addr original destination address |
||
346 | */ |
||
347 | static void |
||
348 | icmp6_send_response_with_addrs(struct pbuf *p, u8_t code, u32_t data, u8_t type, |
||
349 | const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr) |
||
350 | { |
||
351 | const struct ip6_addr *reply_src, *reply_dest; |
||
352 | struct netif *netif; |
||
353 | |||
354 | /* Get the destination address and netif for this ICMP message. */ |
||
355 | LWIP_ASSERT("must provide both source and destination", src_addr != NULL); |
||
356 | LWIP_ASSERT("must provide both source and destination", dest_addr != NULL); |
||
357 | |||
358 | /* Special case, as ip6_current_xxx is either NULL, or points |
||
359 | to a different packet than the one that expired. */ |
||
360 | IP6_ADDR_ZONECHECK(src_addr); |
||
361 | IP6_ADDR_ZONECHECK(dest_addr); |
||
362 | /* Swap source and destination for the reply. */ |
||
363 | reply_dest = src_addr; |
||
364 | reply_src = dest_addr; |
||
365 | netif = ip6_route(reply_src, reply_dest); |
||
366 | if (netif == NULL) { |
||
367 | ICMP6_STATS_INC(icmp6.rterr); |
||
368 | return; |
||
369 | } |
||
370 | icmp6_send_response_with_addrs_and_netif(p, code, data, type, reply_src, |
||
371 | reply_dest, netif); |
||
372 | } |
||
373 | |||
374 | /** |
||
375 | * Send an ICMPv6 packet (with srd/dst address and netif given). |
||
376 | * |
||
377 | * @param p the input packet for which the response should be sent, |
||
378 | * p->payload pointing to the IPv6 header |
||
379 | * @param code Code of the ICMPv6 header |
||
380 | * @param data Additional 32-bit parameter in the ICMPv6 header |
||
381 | * @param type Type of the ICMPv6 header |
||
382 | * @param reply_src source address of the packet to send |
||
383 | * @param reply_dest destination address of the packet to send |
||
384 | * @param netif netif to send the packet |
||
385 | */ |
||
386 | static void |
||
387 | icmp6_send_response_with_addrs_and_netif(struct pbuf *p, u8_t code, u32_t data, u8_t type, |
||
388 | const ip6_addr_t *reply_src, const ip6_addr_t *reply_dest, struct netif *netif) |
||
389 | { |
||
390 | struct pbuf *q; |
||
391 | struct icmp6_hdr *icmp6hdr; |
||
392 | |||
393 | /* ICMPv6 header + IPv6 header + data */ |
||
394 | q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE, |
||
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 icmp 6message", |
||
402 | (q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE))); |
||
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 */ |
||
410 | SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload, |
||
411 | IP6_HLEN + LWIP_ICMP6_DATASIZE); |
||
412 | |||
413 | /* calculate checksum */ |
||
414 | icmp6hdr->chksum = 0; |
||
415 | #if CHECKSUM_GEN_ICMP6 |
||
416 | IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) { |
||
417 | icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len, |
||
418 | reply_src, reply_dest); |
||
419 | } |
||
420 | #endif /* CHECKSUM_GEN_ICMP6 */ |
||
421 | |||
422 | ICMP6_STATS_INC(icmp6.xmit); |
||
423 | ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); |
||
424 | pbuf_free(q); |
||
425 | } |
||
426 | |||
427 | #endif /* LWIP_ICMP6 && LWIP_IPV6 */ |