Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / third_party / lwip / repo / lwip / 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/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
81 icmp6_input(struct pbuf *p, struct netif *inp)
82 {
83   struct icmp6_hdr *icmp6hdr;
84   struct pbuf *r;
85   const ip6_addr_t *reply_src;
86
87   ICMP6_STATS_INC(icmp6.recv);
88
89   /* Check that ICMPv6 header fits in payload */
90   if (p->len < sizeof(struct icmp6_hdr)) {
91     /* drop short packets */
92     pbuf_free(p);
93     ICMP6_STATS_INC(icmp6.lenerr);
94     ICMP6_STATS_INC(icmp6.drop);
95     return;
96   }
97
98   icmp6hdr = (struct icmp6_hdr *)p->payload;
99
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 */
105       pbuf_free(p);
106       ICMP6_STATS_INC(icmp6.chkerr);
107       ICMP6_STATS_INC(icmp6.drop);
108       return;
109     }
110   }
111 #endif /* CHECKSUM_CHECK_ICMP6 */
112
113   switch (icmp6hdr->type) {
114 #if LWIP_IPV6_ND
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     break;
123   case ICMP6_TYPE_RS:
124 #if LWIP_IPV6_FORWARD
125     /* @todo implement router functionality */
126 #endif
127     break;
128 #if LWIP_IPV6_MLD
129   case ICMP6_TYPE_MLQ:
130   case ICMP6_TYPE_MLR:
131   case ICMP6_TYPE_MLD:
132     mld6_input(p, inp);
133     return;
134     break;
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())) {
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  * @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
217  */
218 void
219 icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c)
220 {
221   icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR);
222 }
223
224 /**
225  * Send an icmpv6 'packet too big' packet.
226  *
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
230  */
231 void
232 icmp6_packet_too_big(struct pbuf *p, u32_t mtu)
233 {
234   icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB);
235 }
236
237 /**
238  * Send an icmpv6 'time exceeded' packet.
239  *
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
243  */
244 void
245 icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c)
246 {
247   icmp6_send_response(p, c, 0, ICMP6_TYPE_TE);
248 }
249
250 /**
251  * Send an icmpv6 'parameter problem' packet.
252  *
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
257  */
258 void
259 icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer)
260 {
261   icmp6_send_response(p, c, pointer, ICMP6_TYPE_PP);
262 }
263
264 /**
265  * Send an ICMPv6 packet in response to an incoming packet.
266  *
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
272  */
273 static void
274 icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type)
275 {
276   struct pbuf *q;
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;
282   struct netif *netif;
283
284   /* ICMPv6 header + IPv6 header + data */
285   q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE,
286                  PBUF_RAM);
287   if (q == NULL) {
288     LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n"));
289     ICMP6_STATS_INC(icmp6.memerr);
290     return;
291   }
292   LWIP_ASSERT("check that first pbuf can hold icmp 6message",
293              (q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE)));
294
295   icmp6hdr = (struct icmp6_hdr *)q->payload;
296   icmp6hdr->type = type;
297   icmp6hdr->code = code;
298   icmp6hdr->data = data;
299
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);
303
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);
317     if (netif == NULL) {
318       /* drop */
319       pbuf_free(q);
320       ICMP6_STATS_INC(icmp6.rterr);
321       return;
322     }
323   }
324   else {
325     netif = ip_current_netif();
326     reply_dest = ip6_current_src_addr();
327
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) {
331       /* drop */
332       pbuf_free(q);
333       ICMP6_STATS_INC(icmp6.rterr);
334       return;
335     }
336   }
337
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);
344   }
345 #endif /* CHECKSUM_GEN_ICMP6 */
346
347   ICMP6_STATS_INC(icmp6.xmit);
348   ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
349   pbuf_free(q);
350 }
351
352 #endif /* LWIP_ICMP6 && LWIP_IPV6 */