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_ICMP6_ERROR_BUF ((struct uip_icmp6_error *)UIP_ICMP_PAYLOAD)
57 
58 /** \brief temporary IP address */
59 static uip_ipaddr_t tmp_ipaddr;
60 
61 LIST(echo_reply_callback_list);
62 /*---------------------------------------------------------------------------*/
63 /* List of input handlers */
64 LIST(input_handler_list);
65 /*---------------------------------------------------------------------------*/
66 static uip_icmp6_input_handler_t *
67 input_handler_lookup(uint8_t type, uint8_t icode)
68 {
69  uip_icmp6_input_handler_t *handler = NULL;
70 
71  for(handler = list_head(input_handler_list);
72  handler != NULL;
73  handler = list_item_next(handler)) {
74  if(handler->type == type &&
75  (handler->icode == icode ||
76  handler->icode == UIP_ICMP6_HANDLER_CODE_ANY)) {
77  return handler;
78  }
79  }
80 
81  return NULL;
82 }
83 /*---------------------------------------------------------------------------*/
84 uint8_t
85 uip_icmp6_input(uint8_t type, uint8_t icode)
86 {
87  uip_icmp6_input_handler_t *handler = input_handler_lookup(type, icode);
88 
89  if(handler == NULL) {
90  return UIP_ICMP6_INPUT_ERROR;
91  }
92 
93  if(handler->handler == NULL) {
94  return UIP_ICMP6_INPUT_ERROR;
95  }
96 
97  handler->handler();
98  return UIP_ICMP6_INPUT_SUCCESS;
99 }
100 /*---------------------------------------------------------------------------*/
101 void
102 uip_icmp6_register_input_handler(uip_icmp6_input_handler_t *handler)
103 {
104  list_add(input_handler_list, handler);
105 }
106 /*---------------------------------------------------------------------------*/
107 static void
108 echo_request_input(void)
109 {
110  /*
111  * we send an echo reply. It is trivial if there was no extension
112  * headers in the request otherwise we need to remove the extension
113  * headers and change a few fields
114  */
115  LOG_INFO("Received Echo Request from ");
116  LOG_INFO_6ADDR(&UIP_IP_BUF->srcipaddr);
117  LOG_INFO_(" to ");
118  LOG_INFO_6ADDR(&UIP_IP_BUF->destipaddr);
119  LOG_INFO_("\n");
120 
121  /* IP header */
122  UIP_IP_BUF->ttl = uip_ds6_if.cur_hop_limit;
123 
124  if(uip_is_addr_mcast(&UIP_IP_BUF->destipaddr)){
125  uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &UIP_IP_BUF->srcipaddr);
126  uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr);
127  } else {
128  uip_ipaddr_copy(&tmp_ipaddr, &UIP_IP_BUF->srcipaddr);
129  uip_ipaddr_copy(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr);
130  uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &tmp_ipaddr);
131  }
132 
134 
135  /* Below is important for the correctness of UIP_ICMP_BUF and the
136  * checksum
137  */
138 
139  /* Note: now UIP_ICMP_BUF points to the beginning of the echo reply */
141  UIP_ICMP_BUF->icode = 0;
142  UIP_ICMP_BUF->icmpchksum = 0;
143  UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();
144 
145  LOG_INFO("Sending Echo Reply to ");
146  LOG_INFO_6ADDR(&UIP_IP_BUF->destipaddr);
147  LOG_INFO_(" from ");
148  LOG_INFO_6ADDR(&UIP_IP_BUF->srcipaddr);
149  LOG_INFO_("\n");
150  UIP_STAT(++uip_stat.icmp.sent);
151  return;
152 }
153 /*---------------------------------------------------------------------------*/
154 void
155 uip_icmp6_error_output(uint8_t type, uint8_t code, uint32_t param)
156 {
157  /* check if originating packet is not an ICMP error */
158  uint16_t shift;
159 
160  if(uip_last_proto == UIP_PROTO_ICMP6 && UIP_ICMP_BUF->type < 128) {
161  uipbuf_clear();
162  return;
163  }
164 
165  /* the source should not be unspecified nor multicast */
166  if(uip_is_addr_unspecified(&UIP_IP_BUF->srcipaddr) ||
167  uip_is_addr_mcast(&UIP_IP_BUF->srcipaddr)) {
168  uipbuf_clear();
169  return;
170  }
171 
172  /* Remove all extension headers related to the routing protocol in place.
173  * Keep all other extension headers, so as to match original packet. */
174  if(NETSTACK_ROUTING.ext_header_remove() == 0) {
175  LOG_WARN("Unable to remove ext header before sending ICMPv6 ERROR message\n");
176  }
177 
178  /* remember data of original packet before shifting */
179  uip_ipaddr_copy(&tmp_ipaddr, &UIP_IP_BUF->destipaddr);
180 
181  /* The ICMPv6 error message contains as much of possible of the invoking packet
182  * (see RFC 4443 section 3). Make space for the additional IPv6 and
183  * ICMPv6 headers here and move payload to the "right". What we move includes
184  * extension headers */
185  shift = UIP_IPH_LEN + UIP_ICMPH_LEN + UIP_ICMP6_ERROR_LEN;
186  uip_len += shift;
187  uip_len = MIN(uip_len, UIP_LINK_MTU);
188  uip_ext_len = 0;
189  memmove(uip_buf + shift, (void *)UIP_IP_BUF, uip_len - shift);
190 
191  UIP_IP_BUF->vtc = 0x60;
192  UIP_IP_BUF->tcflow = 0;
193  UIP_IP_BUF->flow = 0;
194  UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
195  UIP_IP_BUF->ttl = uip_ds6_if.cur_hop_limit;
196 
197  uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &UIP_IP_BUF->srcipaddr);
198 
200  if(type == ICMP6_PARAM_PROB && code == ICMP6_PARAMPROB_OPTION){
201  uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &tmp_ipaddr);
202  } else {
203  uipbuf_clear();
204  return;
205  }
206  } else {
207  /* need to pick a source that corresponds to this node */
208  uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &tmp_ipaddr);
209  }
210 
211  UIP_ICMP_BUF->type = type;
212  UIP_ICMP_BUF->icode = code;
213  UIP_ICMP6_ERROR_BUF->param = uip_htonl(param);
214  uipbuf_set_len_field(UIP_IP_BUF, uip_len - UIP_IPH_LEN);
215  UIP_ICMP_BUF->icmpchksum = 0;
216  UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();
217 
218  UIP_STAT(++uip_stat.icmp.sent);
219 
220  LOG_WARN("Sending ICMPv6 ERROR message type %d code %d to ", type, code);
221  LOG_WARN_6ADDR(&UIP_IP_BUF->destipaddr);
222  LOG_WARN_(" from ");
223  LOG_WARN_6ADDR(&UIP_IP_BUF->srcipaddr);
224  LOG_WARN_("\n");
225  return;
226 }
227 
228 /*---------------------------------------------------------------------------*/
229 void
230 uip_icmp6_send(const uip_ipaddr_t *dest, int type, int code, int payload_len)
231 {
232  UIP_IP_BUF->vtc = 0x60;
233  UIP_IP_BUF->tcflow = 0;
234  UIP_IP_BUF->flow = 0;
235  UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
236  UIP_IP_BUF->ttl = uip_ds6_if.cur_hop_limit;
237  uipbuf_set_len_field(UIP_IP_BUF, UIP_ICMPH_LEN + payload_len);
238 
239  if(dest == NULL) {
240  LOG_ERR("invalid argument; dest is NULL\n");
241  return;
242  }
243 
244  memcpy(&UIP_IP_BUF->destipaddr, dest, sizeof(*dest));
245  uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr);
246 
247  UIP_ICMP_BUF->type = type;
248  UIP_ICMP_BUF->icode = code;
249 
250  UIP_ICMP_BUF->icmpchksum = 0;
251  UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();
252 
253  uip_len = UIP_IPH_LEN + UIP_ICMPH_LEN + payload_len;
254 
255  UIP_STAT(++uip_stat.icmp.sent);
256  UIP_STAT(++uip_stat.ip.sent);
257 
258  LOG_INFO("Sending ICMPv6 packet to ");
259  LOG_INFO_6ADDR(&UIP_IP_BUF->destipaddr);
260  LOG_INFO_(", type %u, code %u, len %u\n", type, code, payload_len);
261 
263 }
264 /*---------------------------------------------------------------------------*/
265 static void
266 echo_reply_input(void)
267 {
268  int ttl;
269  uip_ipaddr_t sender;
270 
271  LOG_INFO("Received Echo Reply from ");
272  LOG_INFO_6ADDR(&UIP_IP_BUF->srcipaddr);
273  LOG_INFO_(" to ");
274  LOG_INFO_6ADDR(&UIP_IP_BUF->destipaddr);
275  LOG_INFO_("\n");
276 
277  uip_ipaddr_copy(&sender, &UIP_IP_BUF->srcipaddr);
278  ttl = UIP_IP_BUF->ttl;
279 
281 
282  /* Call all registered applications to let them know an echo reply
283  has been received. */
284  {
285  struct uip_icmp6_echo_reply_notification *n;
286  for(n = list_head(echo_reply_callback_list);
287  n != NULL;
288  n = list_item_next(n)) {
289  if(n->callback != NULL) {
290  n->callback(&sender, ttl,
291  (uint8_t *)UIP_ICMP_PAYLOAD,
292  uip_len - sizeof(struct uip_icmp_hdr) - UIP_IPH_LEN);
293  }
294  }
295  }
296 
297  uipbuf_clear();
298  return;
299 }
300 /*---------------------------------------------------------------------------*/
301 void
302 uip_icmp6_echo_reply_callback_add(struct uip_icmp6_echo_reply_notification *n,
303  uip_icmp6_echo_reply_callback_t c)
304 {
305  if(n != NULL && c != NULL) {
306  n->callback = c;
307  list_add(echo_reply_callback_list, n);
308  }
309 }
310 /*---------------------------------------------------------------------------*/
311 void
312 uip_icmp6_echo_reply_callback_rm(struct uip_icmp6_echo_reply_notification *n)
313 {
314  list_remove(echo_reply_callback_list, n);
315 }
316 /*---------------------------------------------------------------------------*/
317 UIP_ICMP6_HANDLER(echo_request_handler, ICMP6_ECHO_REQUEST,
318  UIP_ICMP6_HANDLER_CODE_ANY, echo_request_input);
319 UIP_ICMP6_HANDLER(echo_reply_handler, ICMP6_ECHO_REPLY,
320  UIP_ICMP6_HANDLER_CODE_ANY, echo_reply_input);
321 /*---------------------------------------------------------------------------*/
322 void
324 {
325  /* Register Echo Request and Reply handlers */
326  uip_icmp6_register_input_handler(&echo_request_handler);
327  uip_icmp6_register_input_handler(&echo_reply_handler);
328 }
329 /*---------------------------------------------------------------------------*/
330 /** @} */
#define UIP_ICMP6_ERROR_LEN
ICMPv6 Error message constant part length.
Definition: uip-icmp6.h:104
#define UIP_IP_BUF
Direct access to IPv6 header.
Definition: uip.h:71
uint8_t uip_last_proto
The final protocol after IPv6 extension headers: UIP_PROTO_TCP, UIP_PROTO_UDP or UIP_PROTO_ICMP6.
Definition: uip6.c:125
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:312
Header file for ICMPv6 message and error handing (RFC 4443)
uint16_t uip_len
The length of the packet in the uip_buf buffer.
Definition: uip6.c:159
void tcpip_ipv6_output(void)
This function does address resolution and then calls tcpip_output.
Definition: tcpip.c:631
#define UIP_ICMP_BUF
Direct access to ICMP, UDP, and TCP headers and payload, with implicit ext header offset (global uip_...
Definition: uip.h:77
#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:155
#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:228
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:85
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:302
uint16_t uip_ext_len
The length of the extension headers.
Definition: uip6.c:122
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:1836
Routing driver header file
#define uip_buf
Macro to access uip_aligned_buf as an array of bytes.
Definition: uip.h:510
#define uip_ipaddr_copy(dest, src)
Copy an IP address from one place to another.
Definition: uip.h:1015
#define UIP_STAT(s)
The uIP TCP/IP statistics.
Definition: uip.h:1417
#define uip_is_addr_mcast(a)
is address a multicast address, see RFC 4291 a is of type uip_ipaddr_t*
Definition: uip.h:1961
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:89
static uip_ipaddr_t tmp_ipaddr
temporary IP address
Definition: uip-icmp6.c:59
#define ICMP6_PARAMPROB_OPTION
unrecognized option
Definition: uip-icmp6.h:97
uip_ds6_netif_t uip_ds6_if
The single interface.
Definition: uip-ds6.c:75
bool uip_remove_ext_hdr(void)
Removes all IPv6 extension headers from uip_buf, updates length fields (uip_len and uip_ext_len) ...
Definition: uip6.c:493
#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:363
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:102
void uip_icmp6_init()
Initialise the uIP ICMPv6 core.
Definition: uip-icmp6.c:323
bool(* ext_header_remove)(void)
Removes all extension headers that pertain to the routing protocol.
Definition: routing.h:132
void uip_icmp6_send(const uip_ipaddr_t *dest, int type, int code, int payload_len)
Send an icmpv6 message.
Definition: uip-icmp6.c:230
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:538
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