4 * IPv6 version of ICMP, as per RFC 4443.
8 * Copyright (c) 2010 Inico Technologies Ltd.
11 * Redistribution and use in source and binary forms, with or without modification,
12 * are permitted provided that the following conditions are met:
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.
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
33 * This file is part of the lwIP TCP/IP stack.
35 * Author: Ivan Delamer <delamer@inicotech.com>
38 * Please coordinate changes and requests with Ivan Delamer
39 * <delamer@inicotech.com>
44 #if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
46 #include "lwip/icmp6.h"
47 #include "lwip/prot/icmp6.h"
49 #include "lwip/ip6_addr.h"
50 #include "lwip/inet_chksum.h"
51 #include "lwip/pbuf.h"
52 #include "lwip/netif.h"
54 #include "lwip/mld6.h"
56 #include "lwip/stats.h"
60 #ifndef LWIP_ICMP6_DATASIZE
61 #define LWIP_ICMP6_DATASIZE 8
63 #if LWIP_ICMP6_DATASIZE == 0
64 #define LWIP_ICMP6_DATASIZE 8
67 /* Forward declarations */
68 static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type);
72 * Process an input ICMPv6 message. Called by ip6_input.
74 * Will generate a reply for echo requests. Other messages are forwarded
75 * to nd6_input, or mld6_input.
77 * @param p the mld packet, p->payload pointing to the icmpv6 header
78 * @param inp the netif on which this packet was received
81 icmp6_input(struct pbuf *p, struct netif *inp)
83 struct icmp6_hdr *icmp6hdr;
85 const ip6_addr_t *reply_src;
87 ICMP6_STATS_INC(icmp6.recv);
89 /* Check that ICMPv6 header fits in payload */
90 if (p->len < sizeof(struct icmp6_hdr)) {
91 /* drop short packets */
93 ICMP6_STATS_INC(icmp6.lenerr);
94 ICMP6_STATS_INC(icmp6.drop);
98 icmp6hdr = (struct icmp6_hdr *)p->payload;
100 #if CHECKSUM_CHECK_ICMP6
101 IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_ICMP6) {
102 if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(),
103 ip6_current_dest_addr()) != 0) {
104 /* Checksum failed */
106 ICMP6_STATS_INC(icmp6.chkerr);
107 ICMP6_STATS_INC(icmp6.drop);
111 #endif /* CHECKSUM_CHECK_ICMP6 */
113 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 */
124 #if LWIP_IPV6_FORWARD
125 /* @todo implement router functionality */
135 #endif /* LWIP_IPV6_MLD */
136 #endif /* LWIP_IPV6_ND */
137 case ICMP6_TYPE_EREQ:
138 #if !LWIP_MULTICAST_PING
139 /* multicast destination address? */
140 if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
143 ICMP6_STATS_INC(icmp6.drop);
146 #endif /* LWIP_MULTICAST_PING */
148 /* Allocate reply. */
149 r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM);
153 ICMP6_STATS_INC(icmp6.memerr);
157 /* Copy echo request. */
158 if (pbuf_copy(r, p) != ERR_OK) {
162 ICMP6_STATS_INC(icmp6.err);
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) {
174 ICMP6_STATS_INC(icmp6.rterr);
179 #endif /* LWIP_MULTICAST_PING */
181 reply_src = ip6_current_dest_addr();
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());
192 #endif /* CHECKSUM_GEN_ICMP6 */
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);
202 ICMP6_STATS_INC(icmp6.proterr);
203 ICMP6_STATS_INC(icmp6.drop);
212 * Send an icmpv6 'destination unreachable' packet.
214 * @param p the input packet for which the 'unreachable' should be sent,
215 * p->payload pointing to the IPv6 header
216 * @param c ICMPv6 code for the unreachable type
219 icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c)
221 icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR);
225 * Send an icmpv6 'packet too big' packet.
227 * @param p the input packet for which the 'packet too big' should be sent,
228 * p->payload pointing to the IPv6 header
229 * @param mtu the maximum mtu that we can accept
232 icmp6_packet_too_big(struct pbuf *p, u32_t mtu)
234 icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB);
238 * Send an icmpv6 'time exceeded' packet.
240 * @param p the input packet for which the 'unreachable' should be sent,
241 * p->payload pointing to the IPv6 header
242 * @param c ICMPv6 code for the time exceeded type
245 icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c)
247 icmp6_send_response(p, c, 0, ICMP6_TYPE_TE);
251 * Send an icmpv6 'parameter problem' packet.
253 * @param p the input packet for which the 'param problem' should be sent,
254 * p->payload pointing to the IP header
255 * @param c ICMPv6 code for the param problem type
256 * @param pointer the pointer to the byte where the parameter is found
259 icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer)
261 icmp6_send_response(p, c, pointer, ICMP6_TYPE_PP);
265 * Send an ICMPv6 packet in response to an incoming packet.
267 * @param p the input packet for which the response should be sent,
268 * p->payload pointing to the IPv6 header
269 * @param code Code of the ICMPv6 header
270 * @param data Additional 32-bit parameter in the ICMPv6 header
271 * @param type Type of the ICMPv6 header
274 icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type)
277 struct icmp6_hdr *icmp6hdr;
278 const ip6_addr_t *reply_src;
279 ip6_addr_t *reply_dest;
280 ip6_addr_t reply_src_local, reply_dest_local;
281 struct ip6_hdr *ip6hdr;
284 /* ICMPv6 header + IPv6 header + data */
285 q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE,
288 LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n"));
289 ICMP6_STATS_INC(icmp6.memerr);
292 LWIP_ASSERT("check that first pbuf can hold icmp 6message",
293 (q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE)));
295 icmp6hdr = (struct icmp6_hdr *)q->payload;
296 icmp6hdr->type = type;
297 icmp6hdr->code = code;
298 icmp6hdr->data = data;
300 /* copy fields from original packet */
301 SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload,
302 IP6_HLEN + LWIP_ICMP6_DATASIZE);
304 /* Get the destination address and netif for this ICMP message. */
305 if ((ip_current_netif() == NULL) ||
306 ((code == ICMP6_TE_FRAG) && (type == ICMP6_TYPE_TE))) {
307 /* Special case, as ip6_current_xxx is either NULL, or points
308 * to a different packet than the one that expired.
309 * We must use the addresses that are stored in the expired packet. */
310 ip6hdr = (struct ip6_hdr *)p->payload;
311 /* copy from packed address to aligned address */
312 ip6_addr_copy(reply_dest_local, ip6hdr->src);
313 ip6_addr_copy(reply_src_local, ip6hdr->dest);
314 reply_dest = &reply_dest_local;
315 reply_src = &reply_src_local;
316 netif = ip6_route(reply_src, reply_dest);
320 ICMP6_STATS_INC(icmp6.rterr);
325 netif = ip_current_netif();
326 reply_dest = ip6_current_src_addr();
328 /* Select an address to use as source. */
329 reply_src = ip_2_ip6(ip6_select_source_address(netif, reply_dest));
330 if (reply_src == NULL) {
333 ICMP6_STATS_INC(icmp6.rterr);
338 /* calculate checksum */
339 icmp6hdr->chksum = 0;
340 #if CHECKSUM_GEN_ICMP6
341 IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
342 icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len,
343 reply_src, reply_dest);
345 #endif /* CHECKSUM_GEN_ICMP6 */
347 ICMP6_STATS_INC(icmp6.xmit);
348 ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
352 #endif /* LWIP_ICMP6 && LWIP_IPV6 */