Contiki-NG
uip-icmp6.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2001-2003, Adam Dunkels.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote
14  * products derived from this software without specific prior
15  * written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  * This file is part of the uIP TCP/IP stack.
30  *
31  */
32 
33 /**
34  * \addtogroup uip
35  * @{
36  */
37 
38 /**
39  * \file
40  * ICMPv6 (RFC 4443) implementation, with message and error handling
41  * \author Julien Abeille <jabeille@cisco.com>
42  * \author Mathilde Durvy <mdurvy@cisco.com>
43  */
44 
45 #include <string.h>
46 #include "net/ipv6/uip-ds6.h"
47 #include "net/ipv6/uip-icmp6.h"
48 #include "contiki-default-conf.h"
49 #include "net/routing/routing.h"
50 
51 /* Log configuration */
52 #include "sys/log.h"
53 #define LOG_MODULE "ICMPv6"
54 #define LOG_LEVEL LOG_LEVEL_IPV6
55 
56 #define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN])
57 #define UIP_ICMP_BUF ((struct uip_icmp_hdr *)&uip_buf[uip_l2_l3_hdr_len])
58 #define UIP_ICMP6_ERROR_BUF ((struct uip_icmp6_error *)&uip_buf[uip_l2_l3_icmp_hdr_len])
59 #define UIP_EXT_BUF ((struct uip_ext_hdr *)&uip_buf[uip_l2_l3_hdr_len])
60 #define UIP_FIRST_EXT_BUF ((struct uip_ext_hdr *)&uip_buf[UIP_LLIPH_LEN])
61 
62 /** \brief temporary IP address */
63 static uip_ipaddr_t tmp_ipaddr;
64 
65 LIST(echo_reply_callback_list);
66 /*---------------------------------------------------------------------------*/
67 /* List of input handlers */
68 LIST(input_handler_list);
69 /*---------------------------------------------------------------------------*/
70 static uip_icmp6_input_handler_t *
71 input_handler_lookup(uint8_t type, uint8_t icode)
72 {
73  uip_icmp6_input_handler_t *handler = NULL;
74 
75  for(handler = list_head(input_handler_list);
76  handler != NULL;
77  handler = list_item_next(handler)) {
78  if(handler->type == type &&
79  (handler->icode == icode ||
80  handler->icode == UIP_ICMP6_HANDLER_CODE_ANY)) {
81  return handler;
82  }
83  }
84 
85  return NULL;
86 }
87 /*---------------------------------------------------------------------------*/
88 uint8_t
89 uip_icmp6_input(uint8_t type, uint8_t icode)
90 {
91  uip_icmp6_input_handler_t *handler = input_handler_lookup(type, icode);
92 
93  if(handler == NULL) {
94  return UIP_ICMP6_INPUT_ERROR;
95  }
96 
97  if(handler->handler == NULL) {
98  return UIP_ICMP6_INPUT_ERROR;
99  }
100 
101  handler->handler();
102  return UIP_ICMP6_INPUT_SUCCESS;
103 }
104 /*---------------------------------------------------------------------------*/
105 void
106 uip_icmp6_register_input_handler(uip_icmp6_input_handler_t *handler)
107 {
108  list_add(input_handler_list, handler);
109 }
110 /*---------------------------------------------------------------------------*/
111 static void
112 echo_request_input(void)
113 {
114  /*
115  * we send an echo reply. It is trivial if there was no extension
116  * headers in the request otherwise we need to remove the extension
117  * headers and change a few fields
118  */
119  LOG_INFO("Received Echo Request from ");
120  LOG_INFO_6ADDR(&UIP_IP_BUF->srcipaddr);
121  LOG_INFO_(" to ");
122  LOG_INFO_6ADDR(&UIP_IP_BUF->destipaddr);
123  LOG_INFO_("\n");
124 
125  /* IP header */
126  UIP_IP_BUF->ttl = uip_ds6_if.cur_hop_limit;
127 
128  if(uip_is_addr_mcast(&UIP_IP_BUF->destipaddr)){
129  uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &UIP_IP_BUF->srcipaddr);
130  uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr);
131  } else {
132  uip_ipaddr_copy(&tmp_ipaddr, &UIP_IP_BUF->srcipaddr);
133  uip_ipaddr_copy(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr);
134  uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &tmp_ipaddr);
135  }
136 
137  if(uip_ext_len > 0) {
138  /* Remove extension headers if any */
139  UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
140  uip_len -= uip_ext_len;
141  UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
142  UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
143  /* move the echo request payload (starting after the icmp header)
144  * to the new location in the reply.
145  * The shift is equal to the length of the extension headers present
146  * Note: UIP_ICMP_BUF still points to the echo request at this stage
147  */
148  memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - uip_ext_len,
149  (uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
150  (uip_len - UIP_IPH_LEN - UIP_ICMPH_LEN));
151  uip_ext_len = 0;
152  }
153 
154  /* Below is important for the correctness of UIP_ICMP_BUF and the
155  * checksum
156  */
157 
158  /* Note: now UIP_ICMP_BUF points to the beginning of the echo reply */
160  UIP_ICMP_BUF->icode = 0;
161  UIP_ICMP_BUF->icmpchksum = 0;
162  UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();
163 
164  LOG_INFO("Sending Echo Reply to ");
165  LOG_INFO_6ADDR(&UIP_IP_BUF->destipaddr);
166  LOG_INFO_(" from ");
167  LOG_INFO_6ADDR(&UIP_IP_BUF->srcipaddr);
168  LOG_INFO_("\n");
169  UIP_STAT(++uip_stat.icmp.sent);
170  return;
171 }
172 /*---------------------------------------------------------------------------*/
173 void
174 uip_icmp6_error_output(uint8_t type, uint8_t code, uint32_t param) {
175  /* check if originating packet is not an ICMP error */
176  if(uip_ext_len) {
177  if(UIP_EXT_BUF->next == UIP_PROTO_ICMP6 && UIP_ICMP_BUF->type < 128) {
178  uip_clear_buf();
179  return;
180  }
181  } else {
182  if(UIP_IP_BUF->proto == UIP_PROTO_ICMP6 && UIP_ICMP_BUF->type < 128) {
183  uip_clear_buf();
184  return;
185  }
186  }
187 
188  /* Remove all extension headers related to the routing protocol in place */
189  NETSTACK_ROUTING.ext_header_remove();
190 
191  /* remember data of original packet before shifting */
192  uip_ipaddr_copy(&tmp_ipaddr, &UIP_IP_BUF->destipaddr);
193 
194  uip_len += UIP_IPICMPH_LEN + UIP_ICMP6_ERROR_LEN;
195 
196  if(uip_len > UIP_LINK_MTU) {
198  }
199 
200  memmove((uint8_t *)UIP_ICMP6_ERROR_BUF + uip_ext_len + UIP_ICMP6_ERROR_LEN,
201  (void *)UIP_IP_BUF, uip_len - UIP_IPICMPH_LEN - uip_ext_len - UIP_ICMP6_ERROR_LEN);
202 
203  UIP_IP_BUF->vtc = 0x60;
204  UIP_IP_BUF->tcflow = 0;
205  UIP_IP_BUF->flow = 0;
206  if (uip_ext_len) {
207  UIP_FIRST_EXT_BUF->next = UIP_PROTO_ICMP6;
208  } else {
209  UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
210  }
211  UIP_IP_BUF->ttl = uip_ds6_if.cur_hop_limit;
212 
213  /* the source should not be unspecified nor multicast, the check for
214  multicast is done in uip_process */
215  if(uip_is_addr_unspecified(&UIP_IP_BUF->srcipaddr)){
216  uip_clear_buf();
217  return;
218  }
219 
220  uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &UIP_IP_BUF->srcipaddr);
221 
223  if(type == ICMP6_PARAM_PROB && code == ICMP6_PARAMPROB_OPTION){
224  uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &tmp_ipaddr);
225  } else {
226  uip_clear_buf();
227  return;
228  }
229  } else {
230  /* need to pick a source that corresponds to this node */
231  uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &tmp_ipaddr);
232  }
233 
234  UIP_ICMP_BUF->type = type;
235  UIP_ICMP_BUF->icode = code;
236  UIP_ICMP6_ERROR_BUF->param = uip_htonl(param);
237  UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
238  UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
239  UIP_ICMP_BUF->icmpchksum = 0;
240  UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();
241 
242  UIP_STAT(++uip_stat.icmp.sent);
243 
244  LOG_WARN("Sending ICMPv6 ERROR message type %d code %d to ", type, code);
245  LOG_WARN_6ADDR(&UIP_IP_BUF->destipaddr);
246  LOG_WARN_(" from ");
247  LOG_WARN_6ADDR(&UIP_IP_BUF->srcipaddr);
248  LOG_WARN_("\n");
249  return;
250 }
251 
252 /*---------------------------------------------------------------------------*/
253 void
254 uip_icmp6_send(const uip_ipaddr_t *dest, int type, int code, int payload_len)
255 {
256  UIP_IP_BUF->vtc = 0x60;
257  UIP_IP_BUF->tcflow = 0;
258  UIP_IP_BUF->flow = 0;
259  UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
260  UIP_IP_BUF->ttl = uip_ds6_if.cur_hop_limit;
261  UIP_IP_BUF->len[0] = (UIP_ICMPH_LEN + payload_len) >> 8;
262  UIP_IP_BUF->len[1] = (UIP_ICMPH_LEN + payload_len) & 0xff;
263 
264  memcpy(&UIP_IP_BUF->destipaddr, dest, sizeof(*dest));
265  uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr);
266 
267  UIP_ICMP_BUF->type = type;
268  UIP_ICMP_BUF->icode = code;
269 
270  UIP_ICMP_BUF->icmpchksum = 0;
271  UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();
272 
273  uip_len = UIP_IPH_LEN + UIP_ICMPH_LEN + payload_len;
274 
275  UIP_STAT(++uip_stat.icmp.sent);
276  UIP_STAT(++uip_stat.ip.sent);
277 
278  LOG_INFO("Sending ICMPv6 packet to ");
279  LOG_INFO_6ADDR(&UIP_IP_BUF->destipaddr);
280  LOG_INFO_(", type %u, code %u, len %u\n", type, code, payload_len);
281 
283 }
284 /*---------------------------------------------------------------------------*/
285 static void
286 echo_reply_input(void)
287 {
288  int ttl;
289  uip_ipaddr_t sender;
290 
291  LOG_INFO("Received Echo Reply from ");
292  LOG_INFO_6ADDR(&UIP_IP_BUF->srcipaddr);
293  LOG_INFO_(" to ");
294  LOG_INFO_6ADDR(&UIP_IP_BUF->destipaddr);
295  LOG_INFO_("\n");
296 
297  uip_ipaddr_copy(&sender, &UIP_IP_BUF->srcipaddr);
298  ttl = UIP_IP_BUF->ttl;
299 
300  if(uip_ext_len > 0) {
301  /* Remove extension headers if any */
302  UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
303  uip_len -= uip_ext_len;
304  UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
305  UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
306  /* move the echo reply payload (starting after the icmp header)
307  * to the new location in the reply. The shift is equal to the
308  * length of the extension headers present Note: UIP_ICMP_BUF
309  * still points to the echo request at this stage
310  */
311  memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - uip_ext_len,
312  (uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
313  (uip_len - UIP_IPH_LEN - UIP_ICMPH_LEN));
314  uip_ext_len = 0;
315  }
316 
317  /* Call all registered applications to let them know an echo reply
318  has been received. */
319  {
320  struct uip_icmp6_echo_reply_notification *n;
321  for(n = list_head(echo_reply_callback_list);
322  n != NULL;
323  n = list_item_next(n)) {
324  if(n->callback != NULL) {
325  n->callback(&sender, ttl,
326  (uint8_t *)&UIP_ICMP_BUF[sizeof(struct uip_icmp_hdr)],
327  uip_len - sizeof(struct uip_icmp_hdr) - UIP_IPH_LEN);
328  }
329  }
330  }
331 
332  uip_clear_buf();
333  return;
334 }
335 /*---------------------------------------------------------------------------*/
336 void
337 uip_icmp6_echo_reply_callback_add(struct uip_icmp6_echo_reply_notification *n,
338  uip_icmp6_echo_reply_callback_t c)
339 {
340  if(n != NULL && c != NULL) {
341  n->callback = c;
342  list_add(echo_reply_callback_list, n);
343  }
344 }
345 /*---------------------------------------------------------------------------*/
346 void
347 uip_icmp6_echo_reply_callback_rm(struct uip_icmp6_echo_reply_notification *n)
348 {
349  list_remove(echo_reply_callback_list, n);
350 }
351 /*---------------------------------------------------------------------------*/
352 UIP_ICMP6_HANDLER(echo_request_handler, ICMP6_ECHO_REQUEST,
353  UIP_ICMP6_HANDLER_CODE_ANY, echo_request_input);
354 UIP_ICMP6_HANDLER(echo_reply_handler, ICMP6_ECHO_REPLY,
355  UIP_ICMP6_HANDLER_CODE_ANY, echo_reply_input);
356 /*---------------------------------------------------------------------------*/
357 void
359 {
360  /* Register Echo Request and Reply handlers */
361  uip_icmp6_register_input_handler(&echo_request_handler);
362  uip_icmp6_register_input_handler(&echo_reply_handler);
363 }
364 /*---------------------------------------------------------------------------*/
365 /** @} */
#define UIP_ICMP6_ERROR_LEN
ICMPv6 Error message constant part length.
Definition: uip-icmp6.h:103
#define UIP_IP_BUF
Pointer to IP header.
Definition: uip-nd6.c:97
void uip_icmp6_echo_reply_callback_rm(struct uip_icmp6_echo_reply_notification *n)
Remove a callback function for ping replies.
Definition: uip-icmp6.c:347
Header file for ICMPv6 message and error handing (RFC 4443)
void(* ext_header_remove)(void)
Removes all extension headers that pertain to the routing protocol.
Definition: routing.h:123
uint16_t uip_len
The length of the packet in the uip_buf buffer.
Definition: uip6.c:179
void tcpip_ipv6_output(void)
This function does address resolution and then calls tcpip_output.
Definition: tcpip.c:636
#define UIP_ICMP_BUF
Pointer to ICMP header.
Definition: uip-nd6.c:98
#define ICMP6_PARAM_PROB
ip6 header bad
Definition: uip-icmp6.h:56
void uip_icmp6_error_output(uint8_t type, uint8_t code, uint32_t param)
Send an icmpv6 error message.
Definition: uip-icmp6.c:174
#define ICMP6_ECHO_REQUEST
Echo request.
Definition: uip-icmp6.h:57
#define UIP_LINK_MTU
The maximum transmission unit at the IP Layer.
Definition: uipopt.h:249
Header file for IPv6-related data structures.
uint8_t uip_icmp6_input(uint8_t type, uint8_t icode)
Handle an incoming ICMPv6 message.
Definition: uip-icmp6.c:89
void uip_icmp6_echo_reply_callback_add(struct uip_icmp6_echo_reply_notification *n, uip_icmp6_echo_reply_callback_t c)
Add a callback function for ping replies.
Definition: uip-icmp6.c:337
void * list_head(list_t list)
Get a pointer to the first element of a list.
Definition: list.c:82
#define uip_is_addr_unspecified(a)
Is IPv6 address a the unspecified address a is of type uip_ipaddr_t.
Definition: uip.h:1982
Routing driver header file
#define uip_ipaddr_copy(dest, src)
Copy an IP address from one place to another.
Definition: uip.h:1018
#define UIP_STAT(s)
The uIP TCP/IP statistics.
Definition: uip.h:1439
#define uip_is_addr_mcast(a)
is address a multicast address, see RFC 4291 a is of type uip_ipaddr_t*
Definition: uip.h:2107
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:142
#define LIST(name)
Declare a linked list.
Definition: list.h:88
static uip_ipaddr_t tmp_ipaddr
temporary IP address
Definition: uip-icmp6.c:63
#define ICMP6_PARAMPROB_OPTION
unrecognized option
Definition: uip-icmp6.h:96
uint8_t uip_ext_len
The length of the extension headers.
Definition: uip6.c:132
uip_ds6_netif_t uip_ds6_if
The single interface.
Definition: uip-ds6.c:75
#define ICMP6_ECHO_REPLY
Echo reply.
Definition: uip-icmp6.h:58
uint16_t uip_icmp6chksum(void)
Calculate the ICMP checksum of the packet in uip_buf.
Definition: uip6.c:384
void uip_icmp6_register_input_handler(uip_icmp6_input_handler_t *handler)
Register a handler which can handle a specific ICMPv6 message type.
Definition: uip-icmp6.c:106
void uip_icmp6_init()
Initialise the uIP ICMPv6 core.
Definition: uip-icmp6.c:358
void uip_icmp6_send(const uip_ipaddr_t *dest, int type, int code, int payload_len)
Send an icmpv6 message.
Definition: uip-icmp6.c:254
Header file for the logging system
void uip_ds6_select_src(uip_ipaddr_t *src, uip_ipaddr_t *dst)
Source address selection, see RFC 3484.
Definition: uip-ds6.c:519
void list_remove(list_t list, void *item)
Remove a specific element from a list.
Definition: list.c:237
void * list_item_next(void *item)
Get the next item following this item.
Definition: list.c:322