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"
48 #include "lwip/ip6_addr.h"
49 #include "lwip/inet_chksum.h"
50 #include "lwip/pbuf.h"
51 #include "lwip/netif.h"
53 #include "lwip/mld6.h"
54 #include "lwip/stats.h"
58 #ifndef LWIP_ICMP6_DATASIZE
59 #define LWIP_ICMP6_DATASIZE 8
61 #if LWIP_ICMP6_DATASIZE == 0
62 #define LWIP_ICMP6_DATASIZE 8
65 /* Forward declarations */
66 static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type);
70 * Process an input ICMPv6 message. Called by ip6_input.
72 * Will generate a reply for echo requests. Other messages are forwarded
73 * to nd6_input, or mld6_input.
75 * @param p the mld packet, p->payload pointing to the icmpv6 header
76 * @param inp the netif on which this packet was received
79 icmp6_input(struct pbuf *p, struct netif *inp)
81 struct icmp6_hdr *icmp6hdr;
83 ip6_addr_t * reply_src;
85 ICMP6_STATS_INC(icmp6.recv);
87 /* Check that ICMPv6 header fits in payload */
88 if (p->len < sizeof(struct icmp6_hdr)) {
89 /* drop short packets */
91 ICMP6_STATS_INC(icmp6.lenerr);
92 ICMP6_STATS_INC(icmp6.drop);
96 icmp6hdr = (struct icmp6_hdr *)p->payload;
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 */
103 ICMP6_STATS_INC(icmp6.chkerr);
104 ICMP6_STATS_INC(icmp6.drop);
107 #endif /* LWIP_ICMP6_CHECKSUM_CHECK */
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 */
119 #if LWIP_IPV6_FORWARD
120 /* TODO implement router functionality */
131 case ICMP6_TYPE_EREQ:
132 #if !LWIP_MULTICAST_PING
133 /* multicast destination address? */
134 if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
137 ICMP6_STATS_INC(icmp6.drop);
140 #endif /* LWIP_MULTICAST_PING */
142 /* Allocate reply. */
143 r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM);
147 ICMP6_STATS_INC(icmp6.memerr);
151 /* Copy echo request. */
152 if (pbuf_copy(r, p) != ERR_OK) {
156 ICMP6_STATS_INC(icmp6.err);
160 /* Determine reply source IPv6 address. */
161 reply_src = ip6_select_source_address(inp, ip6_current_src_addr());
162 if (reply_src == NULL) {
166 ICMP6_STATS_INC(icmp6.rterr);
170 /* Set fields in reply. */
171 ((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP;
172 ((struct icmp6_echo_hdr *)(r->payload))->chksum = 0;
173 ((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r,
174 IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr());
177 ICMP6_STATS_INC(icmp6.xmit);
178 ip6_output_if(r, reply_src, ip6_current_src_addr(),
179 LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, inp);
184 ICMP6_STATS_INC(icmp6.proterr);
185 ICMP6_STATS_INC(icmp6.drop);
194 * Send an icmpv6 'destination unreachable' packet.
196 * @param p the input packet for which the 'unreachable' should be sent,
197 * p->payload pointing to the IPv6 header
198 * @param c ICMPv6 code for the unreachable type
201 icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c)
203 icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR);
207 * Send an icmpv6 'packet too big' packet.
209 * @param p the input packet for which the 'packet too big' should be sent,
210 * p->payload pointing to the IPv6 header
211 * @param mtu the maximum mtu that we can accept
214 icmp6_packet_too_big(struct pbuf *p, u32_t mtu)
216 icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB);
220 * Send an icmpv6 'time exceeded' packet.
222 * @param p the input packet for which the 'unreachable' should be sent,
223 * p->payload pointing to the IPv6 header
224 * @param c ICMPv6 code for the time exceeded type
227 icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c)
229 icmp6_send_response(p, c, 0, ICMP6_TYPE_TE);
233 * Send an icmpv6 'parameter problem' packet.
235 * @param p the input packet for which the 'param problem' should be sent,
236 * p->payload pointing to the IP header
237 * @param c ICMPv6 code for the param problem type
238 * @param pointer the pointer to the byte where the parameter is found
241 icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer)
243 icmp6_send_response(p, c, pointer, ICMP6_TYPE_PP);
247 * Send an ICMPv6 packet in response to an incoming packet.
249 * @param p the input packet for which the response should be sent,
250 * p->payload pointing to the IPv6 header
251 * @param code Code of the ICMPv6 header
252 * @param data Additional 32-bit parameter in the ICMPv6 header
253 * @param type Type of the ICMPv6 header
256 icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type)
259 struct icmp6_hdr *icmp6hdr;
260 ip6_addr_t * reply_src;
262 /* ICMPv6 header + IPv6 header + data */
263 q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE,
266 LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n"));
267 ICMP6_STATS_INC(icmp6.memerr);
270 LWIP_ASSERT("check that first pbuf can hold icmp 6message",
271 (q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE)));
273 icmp6hdr = (struct icmp6_hdr *)q->payload;
274 icmp6hdr->type = type;
275 icmp6hdr->code = code;
276 icmp6hdr->data = data;
278 /* copy fields from original packet */
279 SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload,
280 IP6_HLEN + LWIP_ICMP6_DATASIZE);
282 /* Select an address to use as source. */
283 reply_src = ip6_select_source_address(ip_current_netif(), ip6_current_src_addr());
284 if (reply_src == NULL) {
287 ICMP6_STATS_INC(icmp6.rterr);
291 /* calculate checksum */
292 icmp6hdr->chksum = 0;
293 icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len,
294 reply_src, ip6_current_src_addr());
296 ICMP6_STATS_INC(icmp6.xmit);
297 ip6_output(q, reply_src, ip6_current_src_addr(), LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6);
301 #endif /* LWIP_ICMP6 && LWIP_IPV6 */