Contiki 3.x
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  * \file
35  * ICMPv6 echo request and error messages (RFC 4443)
36  * \author Julien Abeille <jabeille@cisco.com>
37  * \author Mathilde Durvy <mdurvy@cisco.com>
38  */
39 
40 /**
41  * \addtogroup uip6
42  * @{
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 
50 #define DEBUG 0
51 #if DEBUG
52 #include <stdio.h>
53 #define PRINTF(...) printf(__VA_ARGS__)
54 #define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
55 #define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",lladdr->addr[0], lladdr->addr[1], lladdr->addr[2], lladdr->addr[3],lladdr->addr[4], lladdr->addr[5])
56 #else
57 #define PRINTF(...)
58 #define PRINT6ADDR(addr)
59 #endif
60 
61 #define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN])
62 #define UIP_ICMP_BUF ((struct uip_icmp_hdr *)&uip_buf[uip_l2_l3_hdr_len])
63 #define UIP_ICMP6_ERROR_BUF ((struct uip_icmp6_error *)&uip_buf[uip_l2_l3_icmp_hdr_len])
64 #define UIP_EXT_BUF ((struct uip_ext_hdr *)&uip_buf[uip_l2_l3_hdr_len])
65 #define UIP_FIRST_EXT_BUF ((struct uip_ext_hdr *)&uip_buf[UIP_LLIPH_LEN])
66 
67 #if UIP_CONF_IPV6_RPL
68 #include "rpl/rpl.h"
69 #endif /* UIP_CONF_IPV6_RPL */
70 
71 #if UIP_CONF_IPV6
72 
73 /** \brief temporary IP address */
74 static uip_ipaddr_t tmp_ipaddr;
75 
76 LIST(echo_reply_callback_list);
77 /*---------------------------------------------------------------------------*/
78 /* List of input handlers */
79 LIST(input_handler_list);
80 /*---------------------------------------------------------------------------*/
81 static uip_icmp6_input_handler_t *
82 input_handler_lookup(uint8_t type, uint8_t icode)
83 {
84  uip_icmp6_input_handler_t *handler = NULL;
85 
86  for(handler = list_head(input_handler_list);
87  handler != NULL;
88  handler = list_item_next(handler)) {
89  if(handler->type == type &&
90  (handler->icode == icode ||
91  handler->icode == UIP_ICMP6_HANDLER_CODE_ANY)) {
92  return handler;
93  }
94  }
95 
96  return NULL;
97 }
98 /*---------------------------------------------------------------------------*/
99 uint8_t
100 uip_icmp6_input(uint8_t type, uint8_t icode)
101 {
102  uip_icmp6_input_handler_t *handler = input_handler_lookup(type, icode);
103 
104  if(handler == NULL) {
105  return UIP_ICMP6_INPUT_ERROR;
106  }
107 
108  if(handler->handler == NULL) {
109  return UIP_ICMP6_INPUT_ERROR;
110  }
111 
112  handler->handler();
113  return UIP_ICMP6_INPUT_SUCCESS;
114 }
115 /*---------------------------------------------------------------------------*/
116 void
117 uip_icmp6_register_input_handler(uip_icmp6_input_handler_t *handler)
118 {
119  list_add(input_handler_list, handler);
120 }
121 /*---------------------------------------------------------------------------*/
122 static void
123 echo_request_input(void)
124 {
125 #if UIP_CONF_IPV6_RPL
126  uint8_t temp_ext_len;
127 #endif /* UIP_CONF_IPV6_RPL */
128  /*
129  * we send an echo reply. It is trivial if there was no extension
130  * headers in the request otherwise we need to remove the extension
131  * headers and change a few fields
132  */
133  PRINTF("Received Echo Request from");
134  PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
135  PRINTF("to");
136  PRINT6ADDR(&UIP_IP_BUF->destipaddr);
137  PRINTF("\n");
138 
139  /* IP header */
140  UIP_IP_BUF->ttl = uip_ds6_if.cur_hop_limit;
141 
142  if(uip_is_addr_mcast(&UIP_IP_BUF->destipaddr)){
143  uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &UIP_IP_BUF->srcipaddr);
144  uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr);
145  } else {
146  uip_ipaddr_copy(&tmp_ipaddr, &UIP_IP_BUF->srcipaddr);
147  uip_ipaddr_copy(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr);
148  uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &tmp_ipaddr);
149  }
150 
151  if(uip_ext_len > 0) {
152 #if UIP_CONF_IPV6_RPL
153  if((temp_ext_len = rpl_invert_header())) {
154  /* If there were other extension headers*/
155  UIP_FIRST_EXT_BUF->next = UIP_PROTO_ICMP6;
156  if (uip_ext_len != temp_ext_len) {
157  uip_len -= (uip_ext_len - temp_ext_len);
158  UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
159  UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
160  /* move the echo request payload (starting after the icmp header)
161  * to the new location in the reply.
162  * The shift is equal to the length of the remaining extension headers present
163  * Note: UIP_ICMP_BUF still points to the echo request at this stage
164  */
165  memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - (uip_ext_len - temp_ext_len),
166  (uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
167  (uip_len - UIP_IPH_LEN - temp_ext_len - UIP_ICMPH_LEN));
168  }
169  uip_ext_len = temp_ext_len;
170  } else {
171 #endif /* UIP_CONF_IPV6_RPL */
172  /* If there were extension headers*/
173  UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
174  uip_len -= uip_ext_len;
175  UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
176  UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
177  /* move the echo request payload (starting after the icmp header)
178  * to the new location in the reply.
179  * The shift is equal to the length of the extension headers present
180  * Note: UIP_ICMP_BUF still points to the echo request at this stage
181  */
182  memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - uip_ext_len,
183  (uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
184  (uip_len - UIP_IPH_LEN - UIP_ICMPH_LEN));
185  uip_ext_len = 0;
186 #if UIP_CONF_IPV6_RPL
187  }
188 #endif /* UIP_CONF_IPV6_RPL */
189  }
190  /* Below is important for the correctness of UIP_ICMP_BUF and the
191  * checksum
192  */
193 
194  /* Note: now UIP_ICMP_BUF points to the beginning of the echo reply */
196  UIP_ICMP_BUF->icode = 0;
197  UIP_ICMP_BUF->icmpchksum = 0;
198  UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();
199 
200  PRINTF("Sending Echo Reply to");
201  PRINT6ADDR(&UIP_IP_BUF->destipaddr);
202  PRINTF("from");
203  PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
204  PRINTF("\n");
205  UIP_STAT(++uip_stat.icmp.sent);
206  return;
207 }
208 /*---------------------------------------------------------------------------*/
209 void
210 uip_icmp6_error_output(uint8_t type, uint8_t code, uint32_t param) {
211 
212  /* check if originating packet is not an ICMP error*/
213  if (uip_ext_len) {
214  if(UIP_EXT_BUF->next == UIP_PROTO_ICMP6 && UIP_ICMP_BUF->type < 128){
215  uip_len = 0;
216  return;
217  }
218  } else {
219  if(UIP_IP_BUF->proto == UIP_PROTO_ICMP6 && UIP_ICMP_BUF->type < 128){
220  uip_len = 0;
221  return;
222  }
223  }
224 
225 #if UIP_CONF_IPV6_RPL
226  uip_ext_len = rpl_invert_header();
227 #else /* UIP_CONF_IPV6_RPL */
228  uip_ext_len = 0;
229 #endif /* UIP_CONF_IPV6_RPL */
230 
231  /* remember data of original packet before shifting */
232  uip_ipaddr_copy(&tmp_ipaddr, &UIP_IP_BUF->destipaddr);
233 
234  uip_len += UIP_IPICMPH_LEN + UIP_ICMP6_ERROR_LEN;
235 
236  if(uip_len > UIP_LINK_MTU)
238 
239  memmove((uint8_t *)UIP_ICMP6_ERROR_BUF + uip_ext_len + UIP_ICMP6_ERROR_LEN,
240  (void *)UIP_IP_BUF, uip_len - UIP_IPICMPH_LEN - uip_ext_len - UIP_ICMP6_ERROR_LEN);
241 
242  UIP_IP_BUF->vtc = 0x60;
243  UIP_IP_BUF->tcflow = 0;
244  UIP_IP_BUF->flow = 0;
245  if (uip_ext_len) {
246  UIP_FIRST_EXT_BUF->next = UIP_PROTO_ICMP6;
247  } else {
248  UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
249  }
250  UIP_IP_BUF->ttl = uip_ds6_if.cur_hop_limit;
251 
252  /* the source should not be unspecified nor multicast, the check for
253  multicast is done in uip_process */
254  if(uip_is_addr_unspecified(&UIP_IP_BUF->srcipaddr)){
255  uip_len = 0;
256  return;
257  }
258 
259  uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &UIP_IP_BUF->srcipaddr);
260 
261  if(uip_is_addr_mcast(&tmp_ipaddr)){
262  if(type == ICMP6_PARAM_PROB && code == ICMP6_PARAMPROB_OPTION){
263  uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &tmp_ipaddr);
264  } else {
265  uip_len = 0;
266  return;
267  }
268  } else {
269 #if UIP_CONF_ROUTER
270  /* need to pick a source that corresponds to this node */
271  uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &tmp_ipaddr);
272 #else
273  uip_ipaddr_copy(&UIP_IP_BUF->srcipaddr, &tmp_ipaddr);
274 #endif
275  }
276 
277  UIP_ICMP_BUF->type = type;
278  UIP_ICMP_BUF->icode = code;
279  UIP_ICMP6_ERROR_BUF->param = uip_htonl(param);
280  UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
281  UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
282  UIP_ICMP_BUF->icmpchksum = 0;
283  UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();
284 
285  UIP_STAT(++uip_stat.icmp.sent);
286 
287  PRINTF("Sending ICMPv6 ERROR message type %d code %d to", type, code);
288  PRINT6ADDR(&UIP_IP_BUF->destipaddr);
289  PRINTF("from");
290  PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
291  PRINTF("\n");
292  return;
293 }
294 
295 /*---------------------------------------------------------------------------*/
296 void
297 uip_icmp6_send(const uip_ipaddr_t *dest, int type, int code, int payload_len)
298 {
299 
300  UIP_IP_BUF->vtc = 0x60;
301  UIP_IP_BUF->tcflow = 0;
302  UIP_IP_BUF->flow = 0;
303  UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
304  UIP_IP_BUF->ttl = uip_ds6_if.cur_hop_limit;
305  UIP_IP_BUF->len[0] = (UIP_ICMPH_LEN + payload_len) >> 8;
306  UIP_IP_BUF->len[1] = (UIP_ICMPH_LEN + payload_len) & 0xff;
307 
308  memcpy(&UIP_IP_BUF->destipaddr, dest, sizeof(*dest));
309  uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr);
310 
311  UIP_ICMP_BUF->type = type;
312  UIP_ICMP_BUF->icode = code;
313 
314  UIP_ICMP_BUF->icmpchksum = 0;
315  UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();
316 
317  uip_len = UIP_IPH_LEN + UIP_ICMPH_LEN + payload_len;
319 }
320 /*---------------------------------------------------------------------------*/
321 static void
322 echo_reply_input(void)
323 {
324  int ttl;
325  uip_ipaddr_t sender;
326 #if UIP_CONF_IPV6_RPL
327  uint8_t temp_ext_len;
328 #endif /* UIP_CONF_IPV6_RPL */
329 
330  uip_ipaddr_copy(&sender, &UIP_IP_BUF->srcipaddr);
331  ttl = UIP_IP_BUF->ttl;
332 
333  if(uip_ext_len > 0) {
334 #if UIP_CONF_IPV6_RPL
335  if((temp_ext_len = rpl_invert_header())) {
336  /* If there were other extension headers*/
337  UIP_FIRST_EXT_BUF->next = UIP_PROTO_ICMP6;
338  if (uip_ext_len != temp_ext_len) {
339  uip_len -= (uip_ext_len - temp_ext_len);
340  UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
341  UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
342  /* move the echo reply payload (starting after the icmp
343  * header) to the new location in the reply. The shift is
344  * equal to the length of the remaining extension headers
345  * present Note: UIP_ICMP_BUF still points to the echo reply
346  * at this stage
347  */
348  memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - (uip_ext_len - temp_ext_len),
349  (uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
350  (uip_len - UIP_IPH_LEN - temp_ext_len - UIP_ICMPH_LEN));
351  }
352  uip_ext_len = temp_ext_len;
353  uip_len -= uip_ext_len;
354  } else {
355 #endif /* UIP_CONF_IPV6_RPL */
356  /* If there were extension headers*/
357  UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
358  uip_len -= uip_ext_len;
359  UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
360  UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
361  /* move the echo reply payload (starting after the icmp header)
362  * to the new location in the reply. The shift is equal to the
363  * length of the extension headers present Note: UIP_ICMP_BUF
364  * still points to the echo request at this stage
365  */
366  memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - uip_ext_len,
367  (uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
368  (uip_len - UIP_IPH_LEN - UIP_ICMPH_LEN));
369  uip_ext_len = 0;
370 #if UIP_CONF_IPV6_RPL
371  }
372 #endif /* UIP_CONF_IPV6_RPL */
373  }
374 
375  /* Call all registered applications to let them know an echo reply
376  has been received. */
377  {
378  struct uip_icmp6_echo_reply_notification *n;
379  for(n = list_head(echo_reply_callback_list);
380  n != NULL;
381  n = list_item_next(n)) {
382  if(n->callback != NULL) {
383  n->callback(&sender, ttl,
384  (uint8_t *)&UIP_ICMP_BUF[sizeof(struct uip_icmp_hdr)],
385  uip_len - sizeof(struct uip_icmp_hdr) - UIP_IPH_LEN);
386  }
387  }
388  }
389 
390  uip_len = 0;
391  return;
392 }
393 /*---------------------------------------------------------------------------*/
394 void
395 uip_icmp6_echo_reply_callback_add(struct uip_icmp6_echo_reply_notification *n,
396  uip_icmp6_echo_reply_callback_t c)
397 {
398  if(n != NULL && c != NULL) {
399  n->callback = c;
400  list_add(echo_reply_callback_list, n);
401  }
402 }
403 /*---------------------------------------------------------------------------*/
404 void
405 uip_icmp6_echo_reply_callback_rm(struct uip_icmp6_echo_reply_notification *n)
406 {
407  list_remove(echo_reply_callback_list, n);
408 }
409 /*---------------------------------------------------------------------------*/
410 UIP_ICMP6_HANDLER(echo_request_handler, ICMP6_ECHO_REQUEST,
411  UIP_ICMP6_HANDLER_CODE_ANY, echo_request_input);
412 UIP_ICMP6_HANDLER(echo_reply_handler, ICMP6_ECHO_REPLY,
413  UIP_ICMP6_HANDLER_CODE_ANY, echo_reply_input);
414 /*---------------------------------------------------------------------------*/
415 void
417 {
418  /* Register Echo Request and Reply handlers */
419  uip_icmp6_register_input_handler(&echo_request_handler);
420  uip_icmp6_register_input_handler(&echo_reply_handler);
421 }
422 /*---------------------------------------------------------------------------*/
423 /** @} */
424 #endif /* UIP_CONF_IPV6 */
#define UIP_LINK_MTU
The maximum transmission unit at the IP Layer.
Definition: uipopt.h:283
uip_len
The length of the packet in the uip_buf buffer.
Definition: tcp_loader.c:75
void tcpip_ipv6_output(void)
This function does address resolution and then calls tcpip_output.
Definition: tcpip.c:540
#define uip_is_addr_mcast(a)
is address a multicast address, see RFC 3513 a is of type uip_ipaddr_t*
Definition: uip.h:2095
void uip_icmp6_error_output(uint8_t type, uint8_t code, uint32_t param)
Send an icmpv6 error message.
Definition: uip-icmp6.c:210
Network interface and stateless autoconfiguration (RFC 4862)
void * list_item_next(void *item)
Get the next item following this item.
Definition: list.c:325
#define ICMP6_ECHO_REPLY
Echo reply.
Definition: uip-icmp6.h:58
#define NULL
The null pointer.
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:117
void list_remove(list_t list, void *item)
Remove a specific element from a list.
Definition: list.c:240
#define ICMP6_PARAM_PROB
ip6 header bad
Definition: uip-icmp6.h:56
#define UIP_STAT(s)
The uIP TCP/IP statistics.
Definition: uip.h:1431
#define ICMP6_PARAMPROB_OPTION
unrecognized option
Definition: uip-icmp6.h:95
#define UIP_IP_BUF
Pointer to IP header.
Definition: uip-nd6.c:104
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:405
#define uip_is_addr_unspecified(a)
Is IPv6 address a the unspecified address a is of type uip_ipaddr_t.
Definition: uip.h:1962
uint16_t uip_icmp6chksum(void)
Calculate the ICMP checksum of the packet in uip_buf.
Definition: uip6.c:387
#define uip_ipaddr_copy(dest, src)
Copy an IP address from one place to another.
Definition: uip.h:1026
void * list_head(list_t list)
Get a pointer to the first element of a list.
Definition: list.c:83
uint8_t uip_ext_len
The length of the extension headers.
Definition: uip6.c:137
void uip_icmp6_send(const uip_ipaddr_t *dest, int type, int code, int payload_len)
Send an icmpv6 message.
Definition: uip-icmp6.c:297
ICMPv6 echo request and error messages (RFC 4443)
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:143
#define ICMP6_ECHO_REQUEST
Echo request.
Definition: uip-icmp6.h:57
void uip_ds6_select_src(uip_ipaddr_t *src, uip_ipaddr_t *dst)
Source address selection, see RFC 3484.
Definition: uip-ds6.c:497
#define LIST(name)
Declare a linked list.
Definition: list.h:86
#define UIP_ICMP_BUF
Pointer to ICMP header.
Definition: uip-nd6.c:105
uint8_t uip_icmp6_input(uint8_t type, uint8_t icode)
Handle an incoming ICMPv6 message.
Definition: uip-icmp6.c:100
void uip_icmp6_init()
Initialise the uIP ICMPv6 core.
Definition: uip-icmp6.c:416
#define UIP_ICMP6_ERROR_LEN
ICMPv6 Error message constant part length.
Definition: uip-icmp6.h:102
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:395