Contiki-NG
shell-commands.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017, Inria.
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. Neither the name of the Institute nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * This file is part of the Contiki operating system.
30  *
31  */
32 
33 /**
34  * \file
35  * The Contiki shell commands
36  * \author
37  * Simon Duquennoy <simon.duquennoy@inria.fr>
38  */
39 
40 /**
41  * \addtogroup shell
42  * @{
43  */
44 
45 #include "contiki.h"
46 #include "shell.h"
47 #include "shell-commands.h"
48 #include "lib/list.h"
49 #include "sys/log.h"
50 #include "dev/watchdog.h"
51 #include "net/ipv6/uip.h"
52 #include "net/ipv6/uiplib.h"
53 #include "net/ipv6/uip-icmp6.h"
54 #include "net/ipv6/uip-ds6.h"
55 #if MAC_CONF_WITH_TSCH
56 #include "net/mac/tsch/tsch.h"
57 #endif /* MAC_CONF_WITH_TSCH */
58 #if MAC_CONF_WITH_CSMA
59 #include "net/mac/csma/csma.h"
60 #endif
61 #include "net/routing/routing.h"
62 #include "net/mac/llsec802154.h"
63 
64 /* For RPL-specific commands */
65 #if ROUTING_CONF_RPL_LITE
66 #include "net/routing/rpl-lite/rpl.h"
67 #elif ROUTING_CONF_RPL_CLASSIC
68 #include "net/routing/rpl-classic/rpl.h"
69 #endif
70 
71 #include <stdlib.h>
72 
73 #define PING_TIMEOUT (5 * CLOCK_SECOND)
74 
75 #if NETSTACK_CONF_WITH_IPV6
76 static struct uip_icmp6_echo_reply_notification echo_reply_notification;
77 static shell_output_func *curr_ping_output_func = NULL;
78 static struct process *curr_ping_process;
79 static uint8_t curr_ping_ttl;
80 static uint16_t curr_ping_datalen;
81 #endif /* NETSTACK_CONF_WITH_IPV6 */
82 #if TSCH_WITH_SIXTOP
83 static shell_command_6top_sub_cmd_t sixtop_sub_cmd = NULL;
84 #endif /* TSCH_WITH_SIXTOP */
85 static struct shell_command_set_t builtin_shell_command_set;
86 LIST(shell_command_sets);
87 #if NETSTACK_CONF_WITH_IPV6
88 /*---------------------------------------------------------------------------*/
89 static const char *
90 ds6_nbr_state_to_str(uint8_t state)
91 {
92  switch(state) {
93  case NBR_INCOMPLETE:
94  return "Incomplete";
95  case NBR_REACHABLE:
96  return "Reachable";
97  case NBR_STALE:
98  return "Stale";
99  case NBR_DELAY:
100  return "Delay";
101  case NBR_PROBE:
102  return "Probe";
103  default:
104  return "Unknown";
105  }
106 }
107 /*---------------------------------------------------------------------------*/
108 static void
109 echo_reply_handler(uip_ipaddr_t *source, uint8_t ttl, uint8_t *data, uint16_t datalen)
110 {
111  if(curr_ping_output_func != NULL) {
112  curr_ping_output_func = NULL;
113  curr_ping_ttl = ttl;
114  curr_ping_datalen = datalen;
115  process_poll(curr_ping_process);
116  }
117 }
118 /*---------------------------------------------------------------------------*/
119 static
120 PT_THREAD(cmd_ping(struct pt *pt, shell_output_func output, char *args))
121 {
122  static uip_ipaddr_t remote_addr;
123  static struct etimer timeout_timer;
124  char *next_args;
125 
126  PT_BEGIN(pt);
127 
128  SHELL_ARGS_INIT(args, next_args);
129 
130  /* Get argument (remote IPv6) */
131  SHELL_ARGS_NEXT(args, next_args);
132  if(args == NULL) {
133  SHELL_OUTPUT(output, "Destination IPv6 address is not specified\n");
134  PT_EXIT(pt);
135  } else if(uiplib_ipaddrconv(args, &remote_addr) == 0) {
136  SHELL_OUTPUT(output, "Invalid IPv6 address: %s\n", args);
137  PT_EXIT(pt);
138  }
139 
140  SHELL_OUTPUT(output, "Pinging ");
141  shell_output_6addr(output, &remote_addr);
142  SHELL_OUTPUT(output, "\n");
143 
144  /* Send ping request */
145  curr_ping_process = PROCESS_CURRENT();
146  curr_ping_output_func = output;
147  etimer_set(&timeout_timer, PING_TIMEOUT);
148  uip_icmp6_send(&remote_addr, ICMP6_ECHO_REQUEST, 0, 4);
149  PT_WAIT_UNTIL(pt, curr_ping_output_func == NULL || etimer_expired(&timeout_timer));
150 
151  if(curr_ping_output_func != NULL) {
152  SHELL_OUTPUT(output, "Timeout\n");
153  curr_ping_output_func = NULL;
154  } else {
155  SHELL_OUTPUT(output, "Received ping reply from ");
156  shell_output_6addr(output, &remote_addr);
157  SHELL_OUTPUT(output, ", len %u, ttl %u, delay %lu ms\n",
158  curr_ping_datalen, curr_ping_ttl, (1000*(clock_time() - timeout_timer.timer.start))/CLOCK_SECOND);
159  }
160 
161  PT_END(pt);
162 }
163 #endif /* NETSTACK_CONF_WITH_IPV6 */
164 
165 #if ROUTING_CONF_RPL_LITE
166 /*---------------------------------------------------------------------------*/
167 static const char *
168 rpl_state_to_str(enum rpl_dag_state state)
169 {
170  switch(state) {
171  case DAG_INITIALIZED:
172  return "Initialized";
173  case DAG_JOINED:
174  return "Joined";
175  case DAG_REACHABLE:
176  return "Reachable";
177  case DAG_POISONING:
178  return "Poisoning";
179  default:
180  return "Unknown";
181  }
182 }
183 /*---------------------------------------------------------------------------*/
184 static const char *
185 rpl_mop_to_str(int mop)
186 {
187  switch(mop) {
188  case RPL_MOP_NO_DOWNWARD_ROUTES:
189  return "No downward routes";
190  case RPL_MOP_NON_STORING:
191  return "Non-storing";
192  case RPL_MOP_STORING_NO_MULTICAST:
193  return "Storing";
194  case RPL_MOP_STORING_MULTICAST:
195  return "Storing+multicast";
196  default:
197  return "Unknown";
198  }
199 }
200 /*---------------------------------------------------------------------------*/
201 static const char *
202 rpl_ocp_to_str(int ocp)
203 {
204  switch(ocp) {
205  case RPL_OCP_OF0:
206  return "OF0";
207  case RPL_OCP_MRHOF:
208  return "MRHOF";
209  default:
210  return "Unknown";
211  }
212 }
213 /*---------------------------------------------------------------------------*/
214 static
215 PT_THREAD(cmd_rpl_nbr(struct pt *pt, shell_output_func output, char *args))
216 {
217  PT_BEGIN(pt);
218 
219  if(!curr_instance.used || rpl_neighbor_count() == 0) {
220  SHELL_OUTPUT(output, "RPL neighbors: none\n");
221  } else {
222  rpl_nbr_t *nbr = nbr_table_head(rpl_neighbors);
223  SHELL_OUTPUT(output, "RPL neighbors:\n");
224  while(nbr != NULL) {
225  char buf[120];
226  rpl_neighbor_snprint(buf, sizeof(buf), nbr);
227  SHELL_OUTPUT(output, "%s\n", buf);
228  nbr = nbr_table_next(rpl_neighbors, nbr);
229  }
230  }
231 
232  PT_END(pt);
233 }
234 /*---------------------------------------------------------------------------*/
235 static
236 PT_THREAD(cmd_rpl_status(struct pt *pt, shell_output_func output, char *args))
237 {
238  PT_BEGIN(pt);
239 
240  SHELL_OUTPUT(output, "RPL status:\n");
241  if(!curr_instance.used) {
242  SHELL_OUTPUT(output, "-- Instance: None\n");
243  } else {
244  SHELL_OUTPUT(output, "-- Instance: %u\n", curr_instance.instance_id);
245  if(NETSTACK_ROUTING.node_is_root()) {
246  SHELL_OUTPUT(output, "-- DAG root\n");
247  } else {
248  SHELL_OUTPUT(output, "-- DAG node\n");
249  }
250  SHELL_OUTPUT(output, "-- DAG: ");
251  shell_output_6addr(output, &curr_instance.dag.dag_id);
252  SHELL_OUTPUT(output, ", version %u\n", curr_instance.dag.version);
253  SHELL_OUTPUT(output, "-- Prefix: ");
254  shell_output_6addr(output, &curr_instance.dag.prefix_info.prefix);
255  SHELL_OUTPUT(output, "/%u\n", curr_instance.dag.prefix_info.length);
256  SHELL_OUTPUT(output, "-- MOP: %s\n", rpl_mop_to_str(curr_instance.mop));
257  SHELL_OUTPUT(output, "-- OF: %s\n", rpl_ocp_to_str(curr_instance.of->ocp));
258  SHELL_OUTPUT(output, "-- Hop rank increment: %u\n", curr_instance.min_hoprankinc);
259  SHELL_OUTPUT(output, "-- Default lifetime: %lu seconds\n", RPL_LIFETIME(curr_instance.default_lifetime));
260 
261  SHELL_OUTPUT(output, "-- State: %s\n", rpl_state_to_str(curr_instance.dag.state));
262  SHELL_OUTPUT(output, "-- Preferred parent: ");
263  if(curr_instance.dag.preferred_parent) {
264  shell_output_6addr(output, rpl_neighbor_get_ipaddr(curr_instance.dag.preferred_parent));
265  SHELL_OUTPUT(output, " (last DTSN: %u)\n", curr_instance.dag.preferred_parent->dtsn);
266  } else {
267  SHELL_OUTPUT(output, "None\n");
268  }
269  SHELL_OUTPUT(output, "-- Rank: %u\n", curr_instance.dag.rank);
270  SHELL_OUTPUT(output, "-- Lowest rank: %u (%u)\n", curr_instance.dag.lowest_rank, curr_instance.max_rankinc);
271  SHELL_OUTPUT(output, "-- DTSN out: %u\n", curr_instance.dtsn_out);
272  SHELL_OUTPUT(output, "-- DAO sequence: last sent %u, last acked %u\n",
273  curr_instance.dag.dao_last_seqno, curr_instance.dag.dao_last_acked_seqno);
274  SHELL_OUTPUT(output, "-- Trickle timer: current %u, min %u, max %u, redundancy %u\n",
275  curr_instance.dag.dio_intcurrent, curr_instance.dio_intmin,
276  curr_instance.dio_intmin + curr_instance.dio_intdoubl, curr_instance.dio_redundancy);
277 
278  }
279 
280  PT_END(pt);
281 }
282 /*---------------------------------------------------------------------------*/
283 static
284 PT_THREAD(cmd_rpl_refresh_routes(struct pt *pt, shell_output_func output, char *args))
285 {
286  PT_BEGIN(pt);
287 
288  SHELL_OUTPUT(output, "Triggering routes refresh\n");
289  rpl_refresh_routes("Shell");
290 
291  PT_END(pt);
292 }
293 #endif /* ROUTING_CONF_RPL_LITE */
294 /*---------------------------------------------------------------------------*/
295 static void
296 shell_output_log_levels(shell_output_func output)
297 {
298  int i = 0;
299  SHELL_OUTPUT(output, "Log levels:\n");
300  while(all_modules[i].name != NULL) {
301  SHELL_OUTPUT(output, "-- %-10s: %u (%s)\n",
302  all_modules[i].name,
303  *all_modules[i].curr_log_level,
304  log_level_to_str(*all_modules[i].curr_log_level));
305  i++;
306  }
307 }
308 /*---------------------------------------------------------------------------*/
309 static
310 PT_THREAD(cmd_log(struct pt *pt, shell_output_func output, char *args))
311 {
312  static int prev_level;
313  static int level;
314  char *next_args;
315  char *ptr;
316  char *module;
317 
318  PT_BEGIN(pt);
319 
320  SHELL_ARGS_INIT(args, next_args);
321 
322  /* Get and parse argument: module name */
323  SHELL_ARGS_NEXT(args, next_args);
324  module = args;
325  prev_level = log_get_level(module);
326  if(module == NULL || (strcmp("all", module) && prev_level == -1)) {
327  SHELL_OUTPUT(output, "Invalid first argument: %s\n", module)
328  shell_output_log_levels(output);
329  PT_EXIT(pt);
330  }
331 
332  /* Get and parse argument: log level */
333  SHELL_ARGS_NEXT(args, next_args);
334  if(args == NULL) {
335  level = -1;
336  } else {
337  level = (int)strtol(args, &ptr, 10);
338  }
339  if((level == 0 && args == ptr)
340  || level < LOG_LEVEL_NONE || level > LOG_LEVEL_DBG) {
341  SHELL_OUTPUT(output, "Invalid second argument: %s\n", args);
342  PT_EXIT(pt);
343  }
344 
345  /* Set log level */
346  if(level != prev_level) {
347  log_set_level(module, level);
348 #if MAC_CONF_WITH_TSCH && TSCH_LOG_PER_SLOT
349  if(!strcmp(module, "mac") || !strcmp(module, "all")) {
350  if(level >= LOG_LEVEL_DBG) {
351  tsch_log_init();
352  SHELL_OUTPUT(output, "TSCH logging started\n");
353  } else {
354  tsch_log_stop();
355  SHELL_OUTPUT(output, "TSCH logging stopped\n");
356  }
357  }
358 #endif /* MAC_CONF_WITH_TSCH && TSCH_LOG_PER_SLOT */
359  }
360 
361  shell_output_log_levels(output);
362 
363  PT_END(pt);
364 }
365 /*---------------------------------------------------------------------------*/
366 static
367 PT_THREAD(cmd_help(struct pt *pt, shell_output_func output, char *args))
368 {
369  struct shell_command_set_t *set;
370  const struct shell_command_t *cmd;
371  PT_BEGIN(pt);
372 
373  SHELL_OUTPUT(output, "Available commands:\n");
374  /* Note: we explicitly don't expend any code space to deal with shadowing */
375  for(set = list_head(shell_command_sets); set != NULL; set = list_item_next(set)) {
376  for(cmd = set->commands; cmd->name != NULL; ++cmd) {
377  SHELL_OUTPUT(output, "%s\n", cmd->help);
378  }
379  }
380 
381  PT_END(pt);
382 }
383 #if UIP_CONF_IPV6_RPL
384 /*---------------------------------------------------------------------------*/
385 static
386 PT_THREAD(cmd_rpl_set_root(struct pt *pt, shell_output_func output, char *args))
387 {
388  static int is_on;
389  static uip_ipaddr_t prefix;
390  char *next_args;
391 
392  PT_BEGIN(pt);
393 
394  SHELL_ARGS_INIT(args, next_args);
395 
396  /* Get first arg (0/1) */
397  SHELL_ARGS_NEXT(args, next_args);
398 
399  if(!strcmp(args, "1")) {
400  is_on = 1;
401  } else if(!strcmp(args, "0")) {
402  is_on = 0;
403  } else {
404  SHELL_OUTPUT(output, "Invalid argument: %s\n", args);
405  PT_EXIT(pt);
406  }
407 
408  /* Get first second arg (prefix) */
409  SHELL_ARGS_NEXT(args, next_args);
410  if(args != NULL) {
411  if(uiplib_ipaddrconv(args, &prefix) == 0) {
412  SHELL_OUTPUT(output, "Invalid Prefix: %s\n", args);
413  PT_EXIT(pt);
414  }
415  } else {
416  const uip_ipaddr_t *default_prefix = uip_ds6_default_prefix();
417  uip_ip6addr_copy(&prefix, default_prefix);
418  }
419 
420  if(is_on) {
421  if(!NETSTACK_ROUTING.node_is_root()) {
422  SHELL_OUTPUT(output, "Setting as DAG root with prefix ");
423  shell_output_6addr(output, &prefix);
424  SHELL_OUTPUT(output, "/64\n");
425  NETSTACK_ROUTING.root_set_prefix(&prefix, NULL);
426  NETSTACK_ROUTING.root_start();
427  } else {
428  SHELL_OUTPUT(output, "Node is already a DAG root\n");
429  }
430  } else {
431  if(NETSTACK_ROUTING.node_is_root()) {
432  SHELL_OUTPUT(output, "Setting as non-root node: leaving DAG\n");
433  NETSTACK_ROUTING.leave_network();
434  } else {
435  SHELL_OUTPUT(output, "Node is not a DAG root\n");
436  }
437  }
438 
439  PT_END(pt);
440 }
441 /*---------------------------------------------------------------------------*/
442 static
443 PT_THREAD(cmd_rpl_global_repair(struct pt *pt, shell_output_func output, char *args))
444 {
445  PT_BEGIN(pt);
446 
447  SHELL_OUTPUT(output, "Triggering routing global repair\n");
448  NETSTACK_ROUTING.global_repair("Shell");
449 
450  PT_END(pt);
451 }
452 /*---------------------------------------------------------------------------*/
453 static
454 PT_THREAD(cmd_rpl_local_repair(struct pt *pt, shell_output_func output, char *args))
455 {
456  PT_BEGIN(pt);
457 
458  SHELL_OUTPUT(output, "Triggering routing local repair\n");
459  NETSTACK_ROUTING.local_repair("Shell");
460 
461  PT_END(pt);
462 }
463 #endif /* UIP_CONF_IPV6_RPL */
464 /*---------------------------------------------------------------------------*/
465 static
466 PT_THREAD(cmd_macaddr(struct pt *pt, shell_output_func output, char *args))
467 {
468  PT_BEGIN(pt);
469 
470  SHELL_OUTPUT(output, "Node MAC address: ");
472  SHELL_OUTPUT(output, "\n");
473 
474  PT_END(pt);
475 }
476 #if NETSTACK_CONF_WITH_IPV6
477 /*---------------------------------------------------------------------------*/
478 static
479 PT_THREAD(cmd_ipaddr(struct pt *pt, shell_output_func output, char *args))
480 {
481  int i;
482  uint8_t state;
483 
484  PT_BEGIN(pt);
485 
486  SHELL_OUTPUT(output, "Node IPv6 addresses:\n");
487  for(i = 0; i < UIP_DS6_ADDR_NB; i++) {
488  state = uip_ds6_if.addr_list[i].state;
489  if(uip_ds6_if.addr_list[i].isused &&
490  (state == ADDR_TENTATIVE || state == ADDR_PREFERRED)) {
491  SHELL_OUTPUT(output, "-- ");
492  shell_output_6addr(output, &uip_ds6_if.addr_list[i].ipaddr);
493  SHELL_OUTPUT(output, "\n");
494  }
495  }
496 
497  PT_END(pt);
498 }
499 /*---------------------------------------------------------------------------*/
500 static
501 PT_THREAD(cmd_ip_neighbors(struct pt *pt, shell_output_func output, char *args))
502 {
504 
505  PT_BEGIN(pt);
506 
507  nbr = uip_ds6_nbr_head();
508  if(nbr == NULL) {
509  SHELL_OUTPUT(output, "Node IPv6 neighbors: none\n");
510  PT_EXIT(pt);
511  }
512 
513  SHELL_OUTPUT(output, "Node IPv6 neighbors:\n");
514  while(nbr != NULL) {
515  SHELL_OUTPUT(output, "-- ");
517  SHELL_OUTPUT(output, " <-> ");
518  shell_output_lladdr(output, (linkaddr_t *)uip_ds6_nbr_get_ll(nbr));
519  SHELL_OUTPUT(output, ", router %u, state %s ",
520  nbr->isrouter, ds6_nbr_state_to_str(nbr->state));
521  SHELL_OUTPUT(output, "\n");
522  nbr = uip_ds6_nbr_next(nbr);
523  }
524 
525  PT_END(pt);
526 
527 }
528 #endif /* NETSTACK_CONF_WITH_IPV6 */
529 #if MAC_CONF_WITH_TSCH
530 /*---------------------------------------------------------------------------*/
531 static
532 PT_THREAD(cmd_tsch_set_coordinator(struct pt *pt, shell_output_func output, char *args))
533 {
534  static int is_on;
535  static int is_secured;
536  char *next_args;
537 
538  PT_BEGIN(pt);
539 
540  SHELL_ARGS_INIT(args, next_args);
541 
542  /* Get first arg (0/1) */
543  SHELL_ARGS_NEXT(args, next_args);
544 
545  if(!strcmp(args, "1")) {
546  is_on = 1;
547  } else if(!strcmp(args, "0")) {
548  is_on = 0;
549  } else {
550  SHELL_OUTPUT(output, "Invalid first argument: %s\n", args);
551  PT_EXIT(pt);
552  }
553 
554  /* Get first second arg (prefix) */
555  SHELL_ARGS_NEXT(args, next_args);
556  if(args != NULL) {
557  if(!strcmp(args, "1")) {
558 #if LLSEC802154_ENABLED
559  is_secured = 1;
560 #else /* LLSEC802154_ENABLED */
561  SHELL_OUTPUT(output, "Security is not compiled in.\n");
562  is_secured = 0;
563 #endif /* LLSEC802154_ENABLED */
564  } else if(!strcmp(args, "0")) {
565  is_secured = 0;
566  } else {
567  SHELL_OUTPUT(output, "Invalid second argument: %s\n", args);
568  PT_EXIT(pt);
569  }
570  } else {
571  is_secured = 0;
572  }
573 
574  SHELL_OUTPUT(output, "Setting as TSCH %s (%s)\n",
575  is_on ? "coordinator" : "non-coordinator", is_secured ? "secured" : "non-secured");
576 
577  tsch_set_pan_secured(is_secured);
578  tsch_set_coordinator(is_on);
579 
580  PT_END(pt);
581 }
582 /*---------------------------------------------------------------------------*/
583 static
584 PT_THREAD(cmd_tsch_status(struct pt *pt, shell_output_func output, char *args))
585 {
586  PT_BEGIN(pt);
587 
588  SHELL_OUTPUT(output, "TSCH status:\n");
589 
590  SHELL_OUTPUT(output, "-- Is coordinator: %u\n", tsch_is_coordinator);
591  SHELL_OUTPUT(output, "-- Is associated: %u\n", tsch_is_associated);
592  if(tsch_is_associated) {
594  SHELL_OUTPUT(output, "-- PAN ID: 0x%x\n", frame802154_get_pan_id());
595  SHELL_OUTPUT(output, "-- Is PAN secured: %u\n", tsch_is_pan_secured);
596  SHELL_OUTPUT(output, "-- Join priority: %u\n", tsch_join_priority);
597  SHELL_OUTPUT(output, "-- Time source: ");
598  if(n != NULL) {
599  shell_output_lladdr(output, &n->addr);
600  SHELL_OUTPUT(output, "\n");
601  } else {
602  SHELL_OUTPUT(output, "none\n");
603  }
604  SHELL_OUTPUT(output, "-- Last synchronized: %lu seconds ago\n",
605  (clock_time() - tsch_last_sync_time) / CLOCK_SECOND);
606  SHELL_OUTPUT(output, "-- Drift w.r.t. coordinator: %ld ppm\n",
608  SHELL_OUTPUT(output, "-- Network uptime: %lu seconds\n",
609  (unsigned long)(tsch_get_network_uptime_ticks() / CLOCK_SECOND));
610  }
611 
612  PT_END(pt);
613 }
614 #endif /* MAC_CONF_WITH_TSCH */
615 #if NETSTACK_CONF_WITH_IPV6
616 /*---------------------------------------------------------------------------*/
617 static
618 PT_THREAD(cmd_routes(struct pt *pt, shell_output_func output, char *args))
619 {
620  uip_ds6_defrt_t *default_route;
621 
622  PT_BEGIN(pt);
623 
624  /* Our default route */
625  SHELL_OUTPUT(output, "Default route:\n");
626  default_route = uip_ds6_defrt_lookup(uip_ds6_defrt_choose());
627  if(default_route != NULL) {
628  SHELL_OUTPUT(output, "-- ");
629  shell_output_6addr(output, &default_route->ipaddr);
630  if(default_route->lifetime.interval != 0) {
631  SHELL_OUTPUT(output, " (lifetime: %lu seconds)\n", (unsigned long)default_route->lifetime.interval);
632  } else {
633  SHELL_OUTPUT(output, " (lifetime: infinite)\n");
634  }
635  } else {
636  SHELL_OUTPUT(output, "-- None\n");
637  }
638 
639 #if UIP_CONF_IPV6_RPL
640  if(uip_sr_num_nodes() > 0) {
641  uip_sr_node_t *link;
642  /* Our routing links */
643  SHELL_OUTPUT(output, "Routing links (%u in total):\n", uip_sr_num_nodes());
644  link = uip_sr_node_head();
645  while(link != NULL) {
646  char buf[100];
647  uip_sr_link_snprint(buf, sizeof(buf), link);
648  SHELL_OUTPUT(output, "-- %s\n", buf);
649  link = uip_sr_node_next(link);
650  }
651  } else {
652  SHELL_OUTPUT(output, "No routing links\n");
653  }
654 #endif /* UIP_CONF_IPV6_RPL */
655 
656 #if (UIP_MAX_ROUTES != 0)
657  if(uip_ds6_route_num_routes() > 0) {
658  uip_ds6_route_t *route;
659  /* Our routing entries */
660  SHELL_OUTPUT(output, "Routing entries (%u in total):\n", uip_ds6_route_num_routes());
661  route = uip_ds6_route_head();
662  while(route != NULL) {
663  SHELL_OUTPUT(output, "-- ");
664  shell_output_6addr(output, &route->ipaddr);
665  SHELL_OUTPUT(output, " via ");
666  shell_output_6addr(output, uip_ds6_route_nexthop(route));
667  if((unsigned long)route->state.lifetime != 0xFFFFFFFF) {
668  SHELL_OUTPUT(output, " (lifetime: %lu seconds)\n", (unsigned long)route->state.lifetime);
669  } else {
670  SHELL_OUTPUT(output, " (lifetime: infinite)\n");
671  }
672  route = uip_ds6_route_next(route);
673  }
674  } else {
675  SHELL_OUTPUT(output, "No routing entries\n");
676  }
677 #endif /* (UIP_MAX_ROUTES != 0) */
678 
679  PT_END(pt);
680 }
681 #endif /* NETSTACK_CONF_WITH_IPV6 */
682 /*---------------------------------------------------------------------------*/
683 static
684 PT_THREAD(cmd_reboot(struct pt *pt, shell_output_func output, char *args))
685 {
686  PT_BEGIN(pt);
687  SHELL_OUTPUT(output, "rebooting\n");
688  watchdog_reboot();
689  PT_END(pt);
690 }
691 #if MAC_CONF_WITH_TSCH
692 /*---------------------------------------------------------------------------*/
693 static
694 PT_THREAD(cmd_tsch_schedule(struct pt *pt, shell_output_func output, char *args))
695 {
696  struct tsch_slotframe *sf;
697 
698  PT_BEGIN(pt);
699 
700  if(tsch_is_locked()) {
701  PT_EXIT(pt);
702  }
703 
705 
706  if(sf == NULL) {
707  SHELL_OUTPUT(output, "TSCH schedule: no slotframe\n");
708  } else {
709  SHELL_OUTPUT(output, "TSCH schedule:\n");
710  while(sf != NULL) {
711  struct tsch_link *l = list_head(sf->links_list);
712 
713  SHELL_OUTPUT(output, "-- Slotframe: handle %u, size %u, links:\n", sf->handle, sf->size.val);
714 
715  while(l != NULL) {
716  SHELL_OUTPUT(output, "---- Options %02x, type %u, timeslot %u, channel offset %u, address ",
717  l->link_options, l->link_type, l->timeslot, l->channel_offset);
718  shell_output_lladdr(output, &l->addr);
719  SHELL_OUTPUT(output, "\n");
720  l = list_item_next(l);
721  }
722 
724  }
725  }
726  PT_END(pt);
727 }
728 #endif /* MAC_CONF_WITH_TSCH */
729 /*---------------------------------------------------------------------------*/
730 #if TSCH_WITH_SIXTOP
731 void
732 shell_commands_set_6top_sub_cmd(shell_command_6top_sub_cmd_t sub_cmd)
733 {
734  sixtop_sub_cmd = sub_cmd;
735 }
736 /*---------------------------------------------------------------------------*/
737 static
738 PT_THREAD(cmd_6top(struct pt *pt, shell_output_func output, char *args))
739 {
740  char *next_args;
741 
742  PT_BEGIN(pt);
743 
744  SHELL_ARGS_INIT(args, next_args);
745 
746  if(sixtop_sub_cmd == NULL) {
747  SHELL_OUTPUT(output, "6top command is unavailable:\n");
748  } else {
749  SHELL_OUTPUT(output, "6top: ");
750  sixtop_sub_cmd(output, args);
751  }
752  SHELL_ARGS_NEXT(args, next_args);
753 
754  PT_END(pt);
755 }
756 #endif /* TSCH_WITH_SIXTOP */
757 /*---------------------------------------------------------------------------*/
758 #if LLSEC802154_ENABLED
759 static
760 PT_THREAD(cmd_llsec_setlv(struct pt *pt, shell_output_func output, char *args))
761 {
762 
763  PT_BEGIN(pt);
764 
765  if(args == NULL) {
766  SHELL_OUTPUT(output, "Default LLSEC level is %d\n",
767  uipbuf_get_attr(UIPBUF_ATTR_LLSEC_LEVEL));
768  PT_EXIT(pt);
769  } else {
770  int lv = atoi(args);
771  if(lv < 0 || lv > 7) {
772  SHELL_OUTPUT(output, "Illegal LLSEC Level %d\n", lv);
773  PT_EXIT(pt);
774  } else {
775  uipbuf_set_default_attr(UIPBUF_ATTR_LLSEC_LEVEL, lv);
776  uipbuf_clear_attr();
777  SHELL_OUTPUT(output, "LLSEC default level set %d\n", lv);
778  }
779  }
780 
781  PT_END(pt);
782 }
783 /*---------------------------------------------------------------------------*/
784 static
785 PT_THREAD(cmd_llsec_setkey(struct pt *pt, shell_output_func output, char *args))
786 {
787  char *next_args;
788 
789  PT_BEGIN(pt);
790 
791  SHELL_ARGS_INIT(args, next_args);
792 
793  if(args == NULL) {
794  SHELL_OUTPUT(output, "Provide an index and a 16-char string for the key\n");
795  PT_EXIT(pt);
796  } else {
797  int key;
798  SHELL_ARGS_NEXT(args, next_args);
799  key = atoi(args);
800  if(key < 0) {
801  SHELL_OUTPUT(output, "Illegal LLSEC Key index %d\n", key);
802  PT_EXIT(pt);
803  } else {
804 #if MAC_CONF_WITH_CSMA
805  /* Get next arg (key-string) */
806  SHELL_ARGS_NEXT(args, next_args);
807  if(args == NULL) {
808  SHELL_OUTPUT(output, "Provide both an index and a key\n");
809  } else if(strlen(args) == 16) {
810  csma_security_set_key(key, (const uint8_t *) args);
811  SHELL_OUTPUT(output, "Set key for index %d\n", key);
812  } else {
813  SHELL_OUTPUT(output, "Wrong length of key: '%s' (%d)\n", args, strlen(args));
814  }
815 #else
816  SHELL_OUTPUT(output, "Set key not supported.\n");
817  PT_EXIT(pt);
818 #endif
819  }
820  }
821  PT_END(pt);
822 }
823 #endif /* LLSEC802154_ENABLED */
824 /*---------------------------------------------------------------------------*/
825 void
827 {
828  list_init(shell_command_sets);
829  list_add(shell_command_sets, &builtin_shell_command_set);
830 #if NETSTACK_CONF_WITH_IPV6
831  /* Set up Ping Reply callback */
832  uip_icmp6_echo_reply_callback_add(&echo_reply_notification,
833  echo_reply_handler);
834 #endif /* NETSTACK_CONF_WITH_IPV6 */
835 }
836 /*---------------------------------------------------------------------------*/
837 void
838 shell_command_set_register(struct shell_command_set_t *set)
839 {
840  list_push(shell_command_sets, set);
841 }
842 /*---------------------------------------------------------------------------*/
843 int
844 shell_command_set_deregister(struct shell_command_set_t *set)
845 {
846  if(!list_contains(shell_command_sets, set)) {
847  return !0;
848  }
849  list_remove(shell_command_sets, set);
850  return 0;
851 }
852 /*---------------------------------------------------------------------------*/
853 const struct shell_command_t *
854 shell_command_lookup(const char *name)
855 {
856  struct shell_command_set_t *set;
857  const struct shell_command_t *cmd;
858 
859  for(set = list_head(shell_command_sets);
860  set != NULL;
861  set = list_item_next(set)) {
862  for(cmd = set->commands; cmd->name != NULL; ++cmd) {
863  if(!strcmp(cmd->name, name)) {
864  return cmd;
865  }
866  }
867  }
868  return NULL;
869 }
870 /*---------------------------------------------------------------------------*/
871 const struct shell_command_t builtin_shell_commands[] = {
872  { "help", cmd_help, "'> help': Shows this help" },
873  { "reboot", cmd_reboot, "'> reboot': Reboot the board by watchdog_reboot()" },
874  { "log", cmd_log, "'> log module level': Sets log level (0--4) for a given module (or \"all\"). For module \"mac\", level 4 also enables per-slot logging." },
875  { "mac-addr", cmd_macaddr, "'> mac-addr': Shows the node's MAC address" },
876 #if NETSTACK_CONF_WITH_IPV6
877  { "ip-addr", cmd_ipaddr, "'> ip-addr': Shows all IPv6 addresses" },
878  { "ip-nbr", cmd_ip_neighbors, "'> ip-nbr': Shows all IPv6 neighbors" },
879  { "ping", cmd_ping, "'> ping addr': Pings the IPv6 address 'addr'" },
880  { "routes", cmd_routes, "'> routes': Shows the route entries" },
881 #endif /* NETSTACK_CONF_WITH_IPV6 */
882 #if UIP_CONF_IPV6_RPL
883  { "rpl-set-root", cmd_rpl_set_root, "'> rpl-set-root 0/1 [prefix]': Sets node as root (1) or not (0). A /64 prefix can be optionally specified." },
884  { "rpl-local-repair", cmd_rpl_local_repair, "'> rpl-local-repair': Triggers a RPL local repair" },
885 #if ROUTING_CONF_RPL_LITE
886  { "rpl-refresh-routes", cmd_rpl_refresh_routes, "'> rpl-refresh-routes': Refreshes all routes through a DTSN increment" },
887  { "rpl-status", cmd_rpl_status, "'> rpl-status': Shows a summary of the current RPL state" },
888  { "rpl-nbr", cmd_rpl_nbr, "'> rpl-nbr': Shows the RPL neighbor table" },
889 #endif /* ROUTING_CONF_RPL_LITE */
890  { "rpl-global-repair", cmd_rpl_global_repair, "'> rpl-global-repair': Triggers a RPL global repair" },
891 #endif /* UIP_CONF_IPV6_RPL */
892 #if MAC_CONF_WITH_TSCH
893  { "tsch-set-coordinator", cmd_tsch_set_coordinator, "'> tsch-set-coordinator 0/1 [0/1]': Sets node as coordinator (1) or not (0). Second, optional parameter: enable (1) or disable (0) security." },
894  { "tsch-schedule", cmd_tsch_schedule, "'> tsch-schedule': Shows the current TSCH schedule" },
895  { "tsch-status", cmd_tsch_status, "'> tsch-status': Shows a summary of the current TSCH state" },
896 #endif /* MAC_CONF_WITH_TSCH */
897 #if TSCH_WITH_SIXTOP
898  { "6top", cmd_6top, "'> 6top help': Shows 6top command usage" },
899 #endif /* TSCH_WITH_SIXTOP */
900 #if LLSEC802154_ENABLED
901  { "llsec-set-level", cmd_llsec_setlv, "'> llsec-set-level <lv>': Set the level of link layer security (show if no lv argument)"},
902  { "llsec-set-key", cmd_llsec_setkey, "'> llsec-set-key <id> <key>': Set the key of link layer security"},
903 #endif /* LLSEC802154_ENABLED */
904  { NULL, NULL, NULL },
905 };
906 
907 static struct shell_command_set_t builtin_shell_command_set = {
908  .next = NULL,
909  .commands = builtin_shell_commands,
910 };
911 /** @} */
An entry in the default router list.
Header file for ICMPv6 message and error handing (RFC 4443)
Main header file for the Contiki shell
uip_ipaddr_t * rpl_neighbor_get_ipaddr(rpl_nbr_t *nbr)
Returns a neighbor&#39;s (link-local) IPv6 address.
Definition: rpl-neighbor.c:252
void shell_commands_init(void)
Initializes Shell-commands module.
static uip_ds6_nbr_t * nbr
Pointer to llao option in uip_buf.
Definition: uip-nd6.c:106
struct tsch_slotframe * tsch_schedule_slotframe_next(struct tsch_slotframe *sf)
Access the next item in the list of slotframes.
int uip_sr_num_nodes(void)
Tells how many nodes are currently stored in the graph.
Definition: uip-sr.c:63
void list_push(list_t list, void *item)
Add an item to the start of the list.
Definition: list.c:164
void shell_output_lladdr(shell_output_func output, const linkaddr_t *lladdr)
Prints a link-layer address.
Definition: shell.c:64
The 802.15.4 standard CSMA protocol (nonbeacon-enabled)
uip_ds6_nbr_t * uip_ds6_nbr_head(void)
Get the first neighbor cache in nbr_table.
Definition: uip-ds6-nbr.c:429
void tsch_log_init(void)
Initialize log module.
watchdog_reboot()
Keeps control until the WDT throws a reset signal.
Definition: watchdog.c:94
bool list_contains(list_t list, void *item)
Check if the list contains an item.
Definition: list.c:338
TSCH neighbor information.
Definition: tsch-types.h:109
802.15.4e slotframe (contains links)
Definition: tsch-types.h:84
Main header file for the Contiki shell
#define PT_BEGIN(pt)
Declare the start of a protothread inside the C function implementing the protothread.
Definition: pt.h:114
struct tsch_neighbor * tsch_queue_get_time_source(void)
Get the TSCH time source (we currently assume there is only one)
Definition: tsch-queue.c:124
Common functionality of 802.15.4-compliant llsec_drivers.
#define PT_WAIT_UNTIL(pt, condition)
Block and wait until condition is true.
Definition: pt.h:147
#define RPL_LIFETIME(lifetime)
Compute lifetime, accounting for the lifetime unit.
Definition: rpl-types.h:72
#define ICMP6_ECHO_REQUEST
Echo request.
Definition: uip-icmp6.h:57
int(* root_start)(void)
Set the node as root and start a network.
Definition: routing.h:76
int rpl_neighbor_count(void)
Returns the number of nodes in the RPL neighbor table.
Definition: rpl-neighbor.c:168
static uint8_t output(const linkaddr_t *localdest)
Take an IP packet and format it to be sent on an 802.15.4 network using 6lowpan.
Definition: sicslowpan.c:1527
void(* global_repair)(const char *str)
Triggers a global topology repair.
Definition: routing.h:120
Header file for IPv6-related data structures.
void tsch_log_stop(void)
Stop logging module.
void(* root_set_prefix)(uip_ipaddr_t *prefix, uip_ipaddr_t *iid)
Set the prefix, for nodes that will operate as root.
Definition: routing.h:70
linkaddr_t linkaddr_node_addr
The link-layer address of the node.
Definition: linkaddr.c:48
uip_sr_node_t * uip_sr_node_next(uip_sr_node_t *item)
Returns the next element of the non-storing node list.
Definition: uip-sr.c:200
#define PROCESS_CURRENT()
Get a pointer to the currently running process.
Definition: process.h:402
An entry in the routing table.
void log_set_level(const char *module, int level)
Sets a log level at run-time.
Definition: log.c:161
#define CLOCK_SECOND
A second, measured in system clock time.
Definition: clock.h:82
Header file for the IP address manipulation library.
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:297
const uip_ip6addr_t * uip_ds6_default_prefix()
Retrieve the Default IPv6 prefix.
Definition: uip-ds6.c:104
#define PT_END(pt)
Declare the end of a protothread.
Definition: pt.h:126
Linked list manipulation routines.
int(* node_is_root)(void)
Tells whether the node is a network root or not.
Definition: routing.h:82
void process_poll(struct process *p)
Request a process to be polled.
Definition: process.c:371
const uip_ipaddr_t * uip_ds6_nbr_get_ipaddr(const uip_ds6_nbr_t *nbr)
Get an IPv6 address of a neighbor cache.
Definition: uip-ds6-nbr.c:385
void * list_head(list_t list)
Get a pointer to the first element of a list.
Definition: list.c:82
uip_sr_node_t * uip_sr_node_head(void)
Returns the head of the non-storing node list.
Definition: uip-sr.c:194
rpl_dag_state
RPL DAG states.
Definition: rpl-types.h:177
Routing driver header file
All information related to a RPL neighbor.
Definition: rpl-types.h:136
Main API declarations for TSCH.
clock_time_t clock_time(void)
Get the current clock time.
Definition: clock.c:118
int etimer_expired(struct etimer *et)
Check if an event timer has expired.
Definition: etimer.c:213
int tsch_is_locked(void)
Checks if the TSCH lock is set.
struct tsch_slotframe * tsch_schedule_slotframe_head(void)
Access the first item in the list of slotframes.
long int tsch_adaptive_timesync_get_drift_ppm(void)
Gives the estimated clock drift w.r.t.
#define PT_EXIT(pt)
Exit the protothread.
Definition: pt.h:245
#define ADDR_TENTATIVE
Possible states for the an address (RFC 4862)
Definition: uip-ds6.h:155
#define PT_THREAD(name_args)
Declaration of a protothread.
Definition: pt.h:99
#define NBR_INCOMPLETE
Possible states for the nbr cache entries.
Definition: uip-ds6-nbr.h:64
void(* leave_network)(void)
Leave the network the node is part of.
Definition: routing.h:102
A timer.
Definition: etimer.h:76
uint64_t tsch_get_network_uptime_ticks(void)
Get the time, in clock ticks, since the TSCH network was started.
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:142
void list_init(list_t list)
Initialize a list.
Definition: list.c:65
int uip_sr_link_snprint(char *buf, int buflen, uip_sr_node_t *link)
Print a textual description of a source routing link.
Definition: uip-sr.c:252
Header file for the uIP TCP/IP stack.
const char * log_level_to_str(int level)
Returns a textual description of a log level.
Definition: log.c:192
int log_get_level(const char *module)
Returns the current log level.
Definition: log.c:176
#define LIST(name)
Declare a linked list.
Definition: list.h:89
uip_ds6_netif_t uip_ds6_if
The single interface.
Definition: uip-ds6.c:75
void rpl_refresh_routes(const char *str)
Triggers a route fresh via DTSN increment.
Definition: rpl-dag.c:189
uip_ds6_nbr_t * uip_ds6_nbr_next(uip_ds6_nbr_t *nbr)
Get the next neighbor cache of a specified one.
Definition: uip-ds6-nbr.c:444
void(* local_repair)(const char *str)
Triggers a RPL local topology repair.
Definition: routing.h:126
void tsch_set_pan_secured(int enable)
Enable/disable security.
Definition: tsch.c:178
const uip_lladdr_t * uip_ds6_nbr_get_ll(const uip_ds6_nbr_t *nbr)
Get the link-layer address associated with a specified nbr cache.
Definition: uip-ds6-nbr.c:392
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
#define uiplib_ipaddrconv
Convert a textual representation of an IP address to a numerical representation.
Definition: uiplib.h:71
Header file for the logging system
void etimer_set(struct etimer *et, clock_time_t interval)
Set an event timer.
Definition: etimer.c:177
void list_remove(list_t list, void *item)
Remove a specific element from a list.
Definition: list.c:237
void shell_output_6addr(shell_output_func output, const uip_ipaddr_t *ipaddr)
Prints an IPv6 address.
Definition: shell.c:55
void * list_item_next(void *item)
Get the next item following this item.
Definition: list.c:322
int rpl_neighbor_snprint(char *buf, int buflen, rpl_nbr_t *nbr)
Print a textual description of RPL neighbor into a string.
Definition: rpl-neighbor.c:90
A node in a source routing graph, stored at the root and representing all child-parent relationship...
Definition: uip-sr.h:92
void tsch_set_coordinator(int enable)
Set the node as PAN coordinator.
Definition: tsch.c:168
The default nbr_table entry (when UIP_DS6_NBR_MULTI_IPV6_ADDRS is disabled), that implements nbr cach...
Definition: uip-ds6-nbr.h:105