/* * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This file is part of the Contiki operating system. * */ /** * \file * border-router * \author * Niclas Finne * Joakim Eriksson * Nicolas Tsiftes */ #include "contiki.h" #include "contiki-lib.h" #include "contiki-net.h" #include "net/ipv6/uip.h" #include "net/ipv6/uip-ds6.h" #include "rpl.h" #if UIP_CONF_IPV6_RPL_LITE == 0 #include "rpl-private.h" #endif /* UIP_CONF_IPV6_RPL_LITE == 0 */ #include "rpl-dag-root.h" #if RPL_WITH_NON_STORING #include "rpl-ns.h" #endif /* RPL_WITH_NON_STORING */ #include "net/netstack.h" #include "dev/button-sensor.h" #include "dev/slip.h" #include #include #include #include #define DEBUG DEBUG_NONE #include "net/ipv6/uip-debug.h" static uip_ipaddr_t prefix; static uint8_t prefix_set; PROCESS(border_router_process, "Border router process"); #if WEBSERVER==0 /* No webserver */ AUTOSTART_PROCESSES(&border_router_process); #elif WEBSERVER>1 /* Use an external webserver application */ #include "webserver-nogui.h" AUTOSTART_PROCESSES(&border_router_process,&webserver_nogui_process); #else /* Use simple webserver with only one page for minimum footprint. * Multiple connections can result in interleaved tcp segments since * a single static buffer is used for all segments. */ #include "httpd-simple.h" /* The internal webserver can provide additional information if * enough program flash is available. */ #define WEBSERVER_CONF_LOADTIME 0 #define WEBSERVER_CONF_FILESTATS 0 #define WEBSERVER_CONF_NEIGHBOR_STATUS 0 /* Adding links requires a larger RAM buffer. To avoid static allocation * the stack can be used for formatting; however tcp retransmissions * and multiple connections can result in garbled segments. * TODO:use PSOCk_GENERATOR_SEND and tcp state storage to fix this. */ #define WEBSERVER_CONF_ROUTE_LINKS 0 #if WEBSERVER_CONF_ROUTE_LINKS #define BUF_USES_STACK 1 #endif PROCESS(webserver_nogui_process, "Web server"); PROCESS_THREAD(webserver_nogui_process, ev, data) { PROCESS_BEGIN(); httpd_init(); while(1) { PROCESS_WAIT_EVENT_UNTIL(ev == tcpip_event); httpd_appcall(data); } PROCESS_END(); } AUTOSTART_PROCESSES(&border_router_process,&webserver_nogui_process); static const char *TOP = "ContikiRPL\n"; static const char *BOTTOM = "\n"; #if BUF_USES_STACK static char *bufptr, *bufend; #define ADD(...) do { \ bufptr += snprintf(bufptr, bufend - bufptr, __VA_ARGS__); \ } while(0) #else static char buf[256]; static int blen; #define ADD(...) do { \ blen += snprintf(&buf[blen], sizeof(buf) - blen, __VA_ARGS__); \ } while(0) #endif /*---------------------------------------------------------------------------*/ static void ipaddr_add(const uip_ipaddr_t *addr) { uint16_t a; int i, f; for(i = 0, f = 0; i < sizeof(uip_ipaddr_t); i += 2) { a = (addr->u8[i] << 8) + addr->u8[i + 1]; if(a == 0 && f >= 0) { if(f++ == 0) ADD("::"); } else { if(f > 0) { f = -1; } else if(i > 0) { ADD(":"); } ADD("%x", a); } } } /*---------------------------------------------------------------------------*/ static PT_THREAD(generate_routes(struct httpd_state *s)) { static uip_ds6_route_t *r; #if RPL_WITH_NON_STORING static rpl_ns_node_t *link; #endif /* RPL_WITH_NON_STORING */ static uip_ds6_nbr_t *nbr; #if BUF_USES_STACK char buf[256]; #endif #if WEBSERVER_CONF_LOADTIME static clock_time_t numticks; numticks = clock_time(); #endif PSOCK_BEGIN(&s->sout); SEND_STRING(&s->sout, TOP); #if BUF_USES_STACK bufptr = buf;bufend=bufptr+sizeof(buf); #else blen = 0; #endif ADD("Neighbors
");

  for(nbr = nbr_table_head(ds6_neighbors);
      nbr != NULL;
      nbr = nbr_table_next(ds6_neighbors, nbr)) {

#if WEBSERVER_CONF_NEIGHBOR_STATUS
#if BUF_USES_STACK
{char* j=bufptr+25;
      ipaddr_add(&nbr->ipaddr);
      while (bufptr < j) ADD(" ");
      switch (nbr->state) {
      case NBR_INCOMPLETE: ADD(" INCOMPLETE");break;
      case NBR_REACHABLE: ADD(" REACHABLE");break;
      case NBR_STALE: ADD(" STALE");break;
      case NBR_DELAY: ADD(" DELAY");break;
      case NBR_PROBE: ADD(" NBR_PROBE");break;
      }
}
#else
{uint8_t j=blen+25;
      ipaddr_add(&nbr->ipaddr);
      while (blen < j) ADD(" ");
      switch (nbr->state) {
      case NBR_INCOMPLETE: ADD(" INCOMPLETE");break;
      case NBR_REACHABLE: ADD(" REACHABLE");break;
      case NBR_STALE: ADD(" STALE");break;
      case NBR_DELAY: ADD(" DELAY");break;
      case NBR_PROBE: ADD(" NBR_PROBE");break;
      }
}
#endif
#else
      ipaddr_add(&nbr->ipaddr);
#endif

      ADD("\n");
#if BUF_USES_STACK
      if(bufptr > bufend - 45) {
        SEND_STRING(&s->sout, buf);
        bufptr = buf; bufend = bufptr + sizeof(buf);
      }
#else
      if(blen > sizeof(buf) - 45) {
        SEND_STRING(&s->sout, buf);
        blen = 0;
      }
#endif
  }
  ADD("
Routes
\n");
  SEND_STRING(&s->sout, buf);
#if BUF_USES_STACK
  bufptr = buf; bufend = bufptr + sizeof(buf);
#else
  blen = 0;
#endif

  for(r = uip_ds6_route_head(); r != NULL; r = uip_ds6_route_next(r)) {

#if BUF_USES_STACK
#if WEBSERVER_CONF_ROUTE_LINKS
    ADD("ipaddr);
    ADD("]/status.shtml>");
    ipaddr_add(&r->ipaddr);
    ADD("");
#else
    ipaddr_add(&r->ipaddr);
#endif
#else
#if WEBSERVER_CONF_ROUTE_LINKS
    ADD("ipaddr);
    ADD("]/status.shtml>");
    SEND_STRING(&s->sout, buf); //TODO: why tunslip6 needs an output here, wpcapslip does not
    blen = 0;
    ipaddr_add(&r->ipaddr);
    ADD("");
#else
    ipaddr_add(&r->ipaddr);
#endif
#endif
    ADD("/%u (via ", r->length);
    ipaddr_add(uip_ds6_route_nexthop(r));
    if(1 || (r->state.lifetime < 600)) {
      ADD(") %lus\n", (unsigned long)r->state.lifetime);
    } else {
      ADD(")\n");
    }
    SEND_STRING(&s->sout, buf);
#if BUF_USES_STACK
    bufptr = buf; bufend = bufptr + sizeof(buf);
#else
    blen = 0;
#endif
  }
  ADD("
"); #if RPL_WITH_NON_STORING ADD("Links
\n");
  SEND_STRING(&s->sout, buf);
#if BUF_USES_STACK
  bufptr = buf; bufend = bufptr + sizeof(buf);
#else
  blen = 0;
#endif
  for(link = rpl_ns_node_head(); link != NULL; link = rpl_ns_node_next(link)) {
    if(link->parent != NULL) {
      uip_ipaddr_t child_ipaddr;
      uip_ipaddr_t parent_ipaddr;

      rpl_ns_get_node_global_addr(&child_ipaddr, link);
      rpl_ns_get_node_global_addr(&parent_ipaddr, link->parent);

#if BUF_USES_STACK
#if WEBSERVER_CONF_ROUTE_LINKS
      ADD("");
      ipaddr_add(&child_ipaddr);
      ADD("");
#else
      ipaddr_add(&child_ipaddr);
#endif
#else
#if WEBSERVER_CONF_ROUTE_LINKS
      ADD("");
      SEND_STRING(&s->sout, buf); //TODO: why tunslip6 needs an output here, wpcapslip does not
      blen = 0;
      ipaddr_add(&child_ipaddr);
      ADD("");
#else
      ipaddr_add(&child_ipaddr);
#endif
#endif

      ADD(" (parent: ");
      ipaddr_add(&parent_ipaddr);
      if(1 || (link->lifetime < 600)) {
        ADD(") %us\n", (unsigned int)link->lifetime); // iotlab printf does not have %lu
        //ADD(") %lus\n", (unsigned long)r->state.lifetime);
      } else {
        ADD(")\n");
      }
      SEND_STRING(&s->sout, buf);
#if BUF_USES_STACK
      bufptr = buf; bufend = bufptr + sizeof(buf);
#else
      blen = 0;
#endif
    }
  }
  ADD("
"); #endif /* RPL_WITH_NON_STORING */ #if WEBSERVER_CONF_FILESTATS static uint16_t numtimes; ADD("
This page sent %u times",++numtimes); #endif #if WEBSERVER_CONF_LOADTIME numticks = clock_time() - numticks + 1; ADD(" (%u.%02u sec)",numticks/CLOCK_SECOND,(100*(numticks%CLOCK_SECOND))/CLOCK_SECOND)); #endif SEND_STRING(&s->sout, buf); SEND_STRING(&s->sout, BOTTOM); PSOCK_END(&s->sout); } /*---------------------------------------------------------------------------*/ httpd_simple_script_t httpd_simple_get_script(const char *name) { return generate_routes; } #endif /* WEBSERVER */ /*---------------------------------------------------------------------------*/ static void print_local_addresses(void) { int i; uint8_t state; PRINTA("Server IPv6 addresses:\n"); for(i = 0; i < UIP_DS6_ADDR_NB; i++) { state = uip_ds6_if.addr_list[i].state; if(uip_ds6_if.addr_list[i].isused && (state == ADDR_TENTATIVE || state == ADDR_PREFERRED)) { PRINTA(" "); uip_debug_ipaddr_print(&uip_ds6_if.addr_list[i].ipaddr); PRINTA("\n"); } } } /*---------------------------------------------------------------------------*/ void request_prefix(void) { /* mess up uip_buf with a dirty request... */ uip_buf[0] = '?'; uip_buf[1] = 'P'; uip_len = 2; slip_send(); uip_clear_buf(); } /*---------------------------------------------------------------------------*/ void set_prefix_64(uip_ipaddr_t *prefix_64) { memcpy(&prefix, prefix_64, 16); prefix_set = 1; rpl_dag_root_init(prefix_64, NULL); rpl_dag_root_init_dag_immediately(); } /*---------------------------------------------------------------------------*/ PROCESS_THREAD(border_router_process, ev, data) { static struct etimer et; PROCESS_BEGIN(); /* While waiting for the prefix to be sent through the SLIP connection, the future * border router can join an existing DAG as a parent or child, or acquire a default * router that will later take precedence over the SLIP fallback interface. * Prevent that by turning the radio off until we are initialized as a DAG root. */ prefix_set = 0; NETSTACK_MAC.off(); PROCESS_PAUSE(); SENSORS_ACTIVATE(button_sensor); PRINTF("RPL-Border router started\n"); /* Request prefix until it has been received */ while(!prefix_set) { etimer_set(&et, CLOCK_SECOND); request_prefix(); PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); } #if DEBUG || 1 print_local_addresses(); #endif while(1) { PROCESS_YIELD(); if (ev == sensors_event && data == &button_sensor) { PRINTF("Initiating global repair\n"); #if UIP_CONF_IPV6_RPL_LITE rpl_global_repair(); #else rpl_repair_root(RPL_DEFAULT_INSTANCE); #endif } } PROCESS_END(); } /*---------------------------------------------------------------------------*/