Contiki-Inga 3.x
uip-icmp6.c
Go to the documentation of this file.
1 /**
2  * \addtogroup uip6
3  * @{
4  */
5 
6 /**
7  * \file
8  * ICMPv6 echo request and error messages (RFC 4443)
9  * \author Julien Abeille <jabeille@cisco.com>
10  * \author Mathilde Durvy <mdurvy@cisco.com>
11  */
12 
13 /*
14  * Copyright (c) 2001-2003, Adam Dunkels.
15  * All rights reserved.
16  *
17  * Redistribution and use in source and binary forms, with or without
18  * modification, are permitted provided that the following conditions
19  * are met:
20  * 1. Redistributions of source code must retain the above copyright
21  * notice, this list of conditions and the following disclaimer.
22  * 2. Redistributions in binary form must reproduce the above copyright
23  * notice, this list of conditions and the following disclaimer in the
24  * documentation and/or other materials provided with the distribution.
25  * 3. The name of the author may not be used to endorse or promote
26  * products derived from this software without specific prior
27  * written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
30  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
31  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
33  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
35  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
36  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
37  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40  *
41  * This file is part of the uIP TCP/IP stack.
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 /*---------------------------------------------------------------------------*/
79 void
81 {
82 #if UIP_CONF_IPV6_RPL
83  uint8_t temp_ext_len;
84 #endif /* UIP_CONF_IPV6_RPL */
85  /*
86  * we send an echo reply. It is trivial if there was no extension
87  * headers in the request otherwise we need to remove the extension
88  * headers and change a few fields
89  */
90  PRINTF("Received Echo Request from");
91  PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
92  PRINTF("to");
93  PRINT6ADDR(&UIP_IP_BUF->destipaddr);
94  PRINTF("\n");
95 
96  /* IP header */
97  UIP_IP_BUF->ttl = uip_ds6_if.cur_hop_limit;
98 
99  if(uip_is_addr_mcast(&UIP_IP_BUF->destipaddr)){
100  uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &UIP_IP_BUF->srcipaddr);
101  uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr);
102  } else {
103  uip_ipaddr_copy(&tmp_ipaddr, &UIP_IP_BUF->srcipaddr);
104  uip_ipaddr_copy(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr);
105  uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &tmp_ipaddr);
106  }
107 
108  if(uip_ext_len > 0) {
109 #if UIP_CONF_IPV6_RPL
110  if((temp_ext_len = rpl_invert_header())) {
111  /* If there were other extension headers*/
112  UIP_FIRST_EXT_BUF->next = UIP_PROTO_ICMP6;
113  if (uip_ext_len != temp_ext_len) {
114  uip_len -= (uip_ext_len - temp_ext_len);
115  UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
116  UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
117  /* move the echo request payload (starting after the icmp header)
118  * to the new location in the reply.
119  * The shift is equal to the length of the remaining extension headers present
120  * Note: UIP_ICMP_BUF still points to the echo request at this stage
121  */
122  memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - (uip_ext_len - temp_ext_len),
123  (uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
124  (uip_len - UIP_IPH_LEN - temp_ext_len - UIP_ICMPH_LEN));
125  }
126  uip_ext_len = temp_ext_len;
127  } else {
128 #endif /* UIP_CONF_IPV6_RPL */
129  /* If there were extension headers*/
130  UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
131  uip_len -= uip_ext_len;
132  UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
133  UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
134  /* move the echo request payload (starting after the icmp header)
135  * to the new location in the reply.
136  * The shift is equal to the length of the extension headers present
137  * Note: UIP_ICMP_BUF still points to the echo request at this stage
138  */
139  memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - uip_ext_len,
140  (uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
141  (uip_len - UIP_IPH_LEN - UIP_ICMPH_LEN));
142  uip_ext_len = 0;
143 #if UIP_CONF_IPV6_RPL
144  }
145 #endif /* UIP_CONF_IPV6_RPL */
146  }
147  /* Below is important for the correctness of UIP_ICMP_BUF and the
148  * checksum
149  */
150 
151  /* Note: now UIP_ICMP_BUF points to the beginning of the echo reply */
153  UIP_ICMP_BUF->icode = 0;
154  UIP_ICMP_BUF->icmpchksum = 0;
155  UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();
156 
157  PRINTF("Sending Echo Reply to");
158  PRINT6ADDR(&UIP_IP_BUF->destipaddr);
159  PRINTF("from");
160  PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
161  PRINTF("\n");
162  UIP_STAT(++uip_stat.icmp.sent);
163  return;
164 }
165 /*---------------------------------------------------------------------------*/
166 void
167 uip_icmp6_error_output(uint8_t type, uint8_t code, uint32_t param) {
168 
169  /* check if originating packet is not an ICMP error*/
170  if (uip_ext_len) {
171  if(UIP_EXT_BUF->next == UIP_PROTO_ICMP6 && UIP_ICMP_BUF->type < 128){
172  uip_len = 0;
173  return;
174  }
175  } else {
176  if(UIP_IP_BUF->proto == UIP_PROTO_ICMP6 && UIP_ICMP_BUF->type < 128){
177  uip_len = 0;
178  return;
179  }
180  }
181 
182 #if UIP_CONF_IPV6_RPL
183  uip_ext_len = rpl_invert_header();
184 #else /* UIP_CONF_IPV6_RPL */
185  uip_ext_len = 0;
186 #endif /* UIP_CONF_IPV6_RPL */
187 
188  /* remember data of original packet before shifting */
189  uip_ipaddr_copy(&tmp_ipaddr, &UIP_IP_BUF->destipaddr);
190 
191  uip_len += UIP_IPICMPH_LEN + UIP_ICMP6_ERROR_LEN;
192 
193  if(uip_len > UIP_LINK_MTU)
195 
196  memmove((uint8_t *)UIP_ICMP6_ERROR_BUF + uip_ext_len + UIP_ICMP6_ERROR_LEN,
197  (void *)UIP_IP_BUF, uip_len - UIP_IPICMPH_LEN - uip_ext_len - UIP_ICMP6_ERROR_LEN);
198 
199  UIP_IP_BUF->vtc = 0x60;
200  UIP_IP_BUF->tcflow = 0;
201  UIP_IP_BUF->flow = 0;
202  if (uip_ext_len) {
203  UIP_FIRST_EXT_BUF->next = UIP_PROTO_ICMP6;
204  } else {
205  UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
206  }
207  UIP_IP_BUF->ttl = uip_ds6_if.cur_hop_limit;
208 
209  /* the source should not be unspecified nor multicast, the check for
210  multicast is done in uip_process */
211  if(uip_is_addr_unspecified(&UIP_IP_BUF->srcipaddr)){
212  uip_len = 0;
213  return;
214  }
215 
216  uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &UIP_IP_BUF->srcipaddr);
217 
218  if(uip_is_addr_mcast(&tmp_ipaddr)){
219  if(type == ICMP6_PARAM_PROB && code == ICMP6_PARAMPROB_OPTION){
220  uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &tmp_ipaddr);
221  } else {
222  uip_len = 0;
223  return;
224  }
225  } else {
226 #if UIP_CONF_ROUTER
227  /* need to pick a source that corresponds to this node */
228  uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &tmp_ipaddr);
229 #else
230  uip_ipaddr_copy(&UIP_IP_BUF->srcipaddr, &tmp_ipaddr);
231 #endif
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  PRINTF("Sending ICMPv6 ERROR message type %d code %d to", type, code);
245  PRINT6ADDR(&UIP_IP_BUF->destipaddr);
246  PRINTF("from");
247  PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
248  PRINTF("\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 
257  UIP_IP_BUF->vtc = 0x60;
258  UIP_IP_BUF->tcflow = 0;
259  UIP_IP_BUF->flow = 0;
260  UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
261  UIP_IP_BUF->ttl = uip_ds6_if.cur_hop_limit;
262  UIP_IP_BUF->len[0] = (UIP_ICMPH_LEN + payload_len) >> 8;
263  UIP_IP_BUF->len[1] = (UIP_ICMPH_LEN + payload_len) & 0xff;
264 
265  memcpy(&UIP_IP_BUF->destipaddr, dest, sizeof(*dest));
266  uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr);
267 
268  UIP_ICMP_BUF->type = type;
269  UIP_ICMP_BUF->icode = code;
270 
271  UIP_ICMP_BUF->icmpchksum = 0;
272  UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();
273 
274  uip_len = UIP_IPH_LEN + UIP_ICMPH_LEN + payload_len;
276 }
277 /*---------------------------------------------------------------------------*/
278 void
280 {
281  int ttl;
282  uip_ipaddr_t sender;
283 #if UIP_CONF_IPV6_RPL
284  uint8_t temp_ext_len;
285 #endif /* UIP_CONF_IPV6_RPL */
286 
287  uip_ipaddr_copy(&sender, &UIP_IP_BUF->srcipaddr);
288  ttl = UIP_IP_BUF->ttl;
289 
290  if(uip_ext_len > 0) {
291 #if UIP_CONF_IPV6_RPL
292  if((temp_ext_len = rpl_invert_header())) {
293  /* If there were other extension headers*/
294  UIP_FIRST_EXT_BUF->next = UIP_PROTO_ICMP6;
295  if (uip_ext_len != temp_ext_len) {
296  uip_len -= (uip_ext_len - temp_ext_len);
297  UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
298  UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
299  /* move the echo reply payload (starting after the icmp
300  * header) to the new location in the reply. The shift is
301  * equal to the length of the remaining extension headers
302  * present Note: UIP_ICMP_BUF still points to the echo reply
303  * at this stage
304  */
305  memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - (uip_ext_len - temp_ext_len),
306  (uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
307  (uip_len - UIP_IPH_LEN - temp_ext_len - UIP_ICMPH_LEN));
308  }
309  uip_ext_len = temp_ext_len;
310  uip_len -= uip_ext_len;
311  } else {
312 #endif /* UIP_CONF_IPV6_RPL */
313  /* If there were extension headers*/
314  UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
315  uip_len -= uip_ext_len;
316  UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
317  UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
318  /* move the echo reply payload (starting after the icmp header)
319  * to the new location in the reply. The shift is equal to the
320  * length of the extension headers present Note: UIP_ICMP_BUF
321  * still points to the echo request at this stage
322  */
323  memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - uip_ext_len,
324  (uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
325  (uip_len - UIP_IPH_LEN - UIP_ICMPH_LEN));
326  uip_ext_len = 0;
327 #if UIP_CONF_IPV6_RPL
328  }
329 #endif /* UIP_CONF_IPV6_RPL */
330  }
331 
332  /* Call all registered applications to let them know an echo reply
333  has been received. */
334  {
335  struct uip_icmp6_echo_reply_notification *n;
336  for(n = list_head(echo_reply_callback_list);
337  n != NULL;
338  n = list_item_next(n)) {
339  if(n->callback != NULL) {
340  n->callback(&sender, ttl,
341  (uint8_t *)&UIP_ICMP_BUF[sizeof(struct uip_icmp_hdr)],
342  uip_len - sizeof(struct uip_icmp_hdr) - UIP_IPH_LEN);
343  }
344  }
345  }
346  return;
347 }
348 /*---------------------------------------------------------------------------*/
349 void
350 uip_icmp6_echo_reply_callback_add(struct uip_icmp6_echo_reply_notification *n,
351  uip_icmp6_echo_reply_callback_t c)
352 {
353  if(n != NULL && c != NULL) {
354  n->callback = c;
355  list_add(echo_reply_callback_list, n);
356  }
357 }
358 /*---------------------------------------------------------------------------*/
359 void
360 uip_icmp6_echo_reply_callback_rm(struct uip_icmp6_echo_reply_notification *n)
361 {
362  list_remove(echo_reply_callback_list, n);
363 }
364 /*---------------------------------------------------------------------------*/
365 /** @} */
366 #endif /* UIP_CONF_IPV6 */