dao_input_storing(void)
{
#if RPL_WITH_STORING
  uip_ipaddr_t dao_sender_addr;
  rpl_dag_t *dag;
  rpl_instance_t *instance;
  unsigned char *buffer;
  uint16_t sequence;
  uint8_t instance_id;
  uint8_t lifetime;
  uint8_t prefixlen;
  uint8_t flags;
  uint8_t subopt_type;
  /*
    uint8_t pathcontrol;
    uint8_t pathsequence;
  */
  uip_ipaddr_t prefix;
  uip_ds6_route_t *rep;
  uint8_t buffer_length;
  int pos;
  int len;
  int i;
  int learned_from;
  rpl_parent_t *parent;
  uip_ds6_nbr_t *nbr;
  int is_root;

  prefixlen = 0;
  parent = NULL;
  memset(&prefix, 0, sizeof(prefix));

  uip_ipaddr_copy(&dao_sender_addr, &UIP_IP_BUF->srcipaddr);

  buffer = UIP_ICMP_PAYLOAD;
  buffer_length = uip_len - uip_l3_icmp_hdr_len;

  pos = 0;
  instance_id = buffer[pos++];

  instance = rpl_get_instance(instance_id);

  lifetime = instance->default_lifetime;

  flags = buffer[pos++];
  /* reserved */
  pos++;
  sequence = buffer[pos++];

  dag = instance->current_dag;
  is_root = (dag->rank == ROOT_RANK(instance));

  /* Is the DAG ID present? */
  if(flags & RPL_DAO_D_FLAG) {
    if(memcmp(&dag->dag_id, &buffer[pos], sizeof(dag->dag_id))) {
      LOG_INFO("Ignoring a DAO for a DAG different from ours\n");
      return;
    }
    pos += 16;
  }

  learned_from = uip_is_addr_mcast(&dao_sender_addr) ?
    RPL_ROUTE_FROM_MULTICAST_DAO : RPL_ROUTE_FROM_UNICAST_DAO;

  /* Destination Advertisement Object */
  LOG_DBG("Received a (%s) DAO with sequence number %u from ",
         learned_from == RPL_ROUTE_FROM_UNICAST_DAO? "unicast": "multicast", sequence);
  LOG_DBG_6ADDR(&dao_sender_addr);
  LOG_DBG_("\n");

  if(learned_from == RPL_ROUTE_FROM_UNICAST_DAO) {
    /* Check whether this is a DAO forwarding loop. */
    parent = rpl_find_parent(dag, &dao_sender_addr);
    /* check if this is a new DAO registration with an "illegal" rank */
    /* if we already route to this node it is likely */
    if(parent != NULL &&
       DAG_RANK(parent->rank, instance) < DAG_RANK(dag->rank, instance)) {
      LOG_WARN("Loop detected when receiving a unicast DAO from a node with a lower rank! (%u < %u)\n",
             DAG_RANK(parent->rank, instance), DAG_RANK(dag->rank, instance));
      parent->rank = RPL_INFINITE_RANK;
      parent->flags |= RPL_PARENT_FLAG_UPDATED;
      return;
    }

    /* If we get the DAO from our parent, we also have a loop. */
    if(parent != NULL && parent == dag->preferred_parent) {
      LOG_WARN("Loop detected when receiving a unicast DAO from our parent\n");
      parent->rank = RPL_INFINITE_RANK;
      parent->flags |= RPL_PARENT_FLAG_UPDATED;
      return;
    }
  }

  /* Check if there are any RPL options present. */
  for(i = pos; i < buffer_length; i += len) {
    subopt_type = buffer[i];
    if(subopt_type == RPL_OPTION_PAD1) {
      len = 1;
    } else {
      /* The option consists of a two-byte header and a payload. */
      len = 2 + buffer[i + 1];
    }

    switch(subopt_type) {
      case RPL_OPTION_TARGET:
        /* Handle the target option. */
        prefixlen = buffer[i + 3];
        if(prefixlen == 0) {
          /* Ignore option targets with a prefix length of 0. */
          break;
        }
        if(prefixlen > 128) {
          LOG_ERR("Too large target prefix length %d\n", prefixlen);
          return;
        }
        if(i + 4 + ((prefixlen + 7) / CHAR_BIT) > buffer_length) {
          LOG_ERR("Insufficient space to copy RPL Target of %d bits\n",
                  prefixlen);
          return;
        }
        memset(&prefix, 0, sizeof(prefix));
        memcpy(&prefix, buffer + i + 4, (prefixlen + 7) / CHAR_BIT);
        break;
      case RPL_OPTION_TRANSIT:
        /* The path sequence and control are ignored. */
        /*      pathcontrol = buffer[i + 3];
                pathsequence = buffer[i + 4];*/
        lifetime = buffer[i + 5];
        /* The parent address is also ignored. */
        break;
    }
  }

  LOG_INFO("DAO lifetime: %u, prefix length: %u prefix: ",
         (unsigned)lifetime, (unsigned)prefixlen);
  LOG_INFO_6ADDR(&prefix);
  LOG_INFO_("\n");

#if RPL_WITH_MULTICAST
  if(uip_is_addr_mcast_global(&prefix)) {
    /*
     * "rep" is used for a unicast route which we don't need now; so set NULL so
     * that operations on "rep" will be skipped.
     */
    rep = NULL;
    mcast_group = uip_mcast6_route_add(&prefix);
    if(mcast_group) {
      mcast_group->dag = dag;
      mcast_group->lifetime = RPL_LIFETIME(instance, lifetime);
    }
    goto fwd_dao;
  }
#endif

  rep = uip_ds6_route_lookup(&prefix);

  if(lifetime == RPL_ZERO_LIFETIME) {
    LOG_INFO("No-Path DAO received\n");
    /* No-Path DAO received; invoke the route purging routine. */
    if(rep != NULL &&
       !RPL_ROUTE_IS_NOPATH_RECEIVED(rep) &&
       rep->length == prefixlen &&
       uip_ds6_route_nexthop(rep) != NULL &&
       uip_ipaddr_cmp(uip_ds6_route_nexthop(rep), &dao_sender_addr)) {
      LOG_DBG("Setting expiration timer for prefix ");
      LOG_DBG_6ADDR(&prefix);
      LOG_DBG_("\n");
      RPL_ROUTE_SET_NOPATH_RECEIVED(rep);
      rep->state.lifetime = RPL_NOPATH_REMOVAL_DELAY;

      /* We forward the incoming No-Path DAO to our parent, if we have
         one. */
      if(dag->preferred_parent != NULL &&
         rpl_parent_get_ipaddr(dag->preferred_parent) != NULL) {
        uint8_t out_seq;
        out_seq = prepare_for_dao_fwd(sequence, rep);

        LOG_DBG("Forwarding No-path DAO to parent - out_seq:%d",
               out_seq);
        LOG_DBG_6ADDR(rpl_parent_get_ipaddr(dag->preferred_parent));
        LOG_DBG_("\n");

        buffer = UIP_ICMP_PAYLOAD;
        buffer[3] = out_seq; /* add an outgoing seq no before fwd */
        uip_icmp6_send(rpl_parent_get_ipaddr(dag->preferred_parent),
                       ICMP6_RPL, RPL_CODE_DAO, buffer_length);
      }
    }
    /* independent if we remove or not - ACK the request */
    if(flags & RPL_DAO_K_FLAG) {
      /* indicate that we accepted the no-path DAO */
      uipbuf_clear();
      dao_ack_output(instance, &dao_sender_addr, sequence,
                     RPL_DAO_ACK_UNCONDITIONAL_ACCEPT);
    }
    return;
  }

  LOG_INFO("Adding DAO route\n");

  /* Update and add neighbor - if no room - fail. */
  if((nbr = rpl_icmp6_update_nbr_table(&dao_sender_addr, NBR_TABLE_REASON_RPL_DAO, instance)) == NULL) {
    LOG_ERR("Out of Memory, dropping DAO from ");
    LOG_ERR_6ADDR(&dao_sender_addr);
    LOG_ERR_(", ");
    LOG_ERR_LLADDR(packetbuf_addr(PACKETBUF_ADDR_SENDER));
    LOG_ERR_("\n");
    if(flags & RPL_DAO_K_FLAG) {
      /* signal the failure to add the node */
      dao_ack_output(instance, &dao_sender_addr, sequence,
                     is_root ? RPL_DAO_ACK_UNABLE_TO_ADD_ROUTE_AT_ROOT :
                     RPL_DAO_ACK_UNABLE_TO_ACCEPT);
    }
    return;
  }

  rep = rpl_add_route(dag, &prefix, prefixlen, &dao_sender_addr);
  if(rep == NULL) {
    RPL_STAT(rpl_stats.mem_overflows++);
    LOG_ERR("Could not add a route after receiving a DAO\n");
    if(flags & RPL_DAO_K_FLAG) {
      /* signal the failure to add the node */
      dao_ack_output(instance, &dao_sender_addr, sequence,
                     is_root ? RPL_DAO_ACK_UNABLE_TO_ADD_ROUTE_AT_ROOT :
                     RPL_DAO_ACK_UNABLE_TO_ACCEPT);
    }
    return;
  }

  /* set lifetime and clear NOPATH bit */
  rep->state.lifetime = RPL_LIFETIME(instance, lifetime);
  RPL_ROUTE_CLEAR_NOPATH_RECEIVED(rep);

#if RPL_WITH_MULTICAST
fwd_dao:
#endif

  if(learned_from == RPL_ROUTE_FROM_UNICAST_DAO) {
    int should_ack = 0;

    if(flags & RPL_DAO_K_FLAG) {
      if(rep != NULL) {
        /*
         * check if this route is already installed and we can ack now!
         * not pending - and same seq-no means that we can ack.
         * (e.g. the route is installed already so it will not take any
         * more room that it already takes - so should be ok!)
         */
        if((!RPL_ROUTE_IS_DAO_PENDING(rep) &&
            rep->state.dao_seqno_in == sequence) ||
           dag->rank == ROOT_RANK(instance)) {
          should_ack = 1;
        }
      }
    }

    if(dag->preferred_parent != NULL &&
       rpl_parent_get_ipaddr(dag->preferred_parent) != NULL) {
      uint8_t out_seq = 0;
      if(rep != NULL) {
        /* if this is pending and we get the same seq no it is a retrans */
        if(RPL_ROUTE_IS_DAO_PENDING(rep) &&
           rep->state.dao_seqno_in == sequence) {
          /* keep the same seq-no as before for parent also */
          out_seq = rep->state.dao_seqno_out;
        } else {
          out_seq = prepare_for_dao_fwd(sequence, rep);
        }
      }

      LOG_DBG("Forwarding DAO to parent ");
      LOG_DBG_6ADDR(rpl_parent_get_ipaddr(dag->preferred_parent));
      LOG_DBG_(" in seq: %d out seq: %d\n", sequence, out_seq);

      buffer = UIP_ICMP_PAYLOAD;
      buffer[3] = out_seq; /* add an outgoing seq no before fwd */
      uip_icmp6_send(rpl_parent_get_ipaddr(dag->preferred_parent),
                     ICMP6_RPL, RPL_CODE_DAO, buffer_length);
    }
    if(should_ack) {
      LOG_DBG("Sending DAO ACK\n");
      uipbuf_clear();
      dao_ack_output(instance, &dao_sender_addr, sequence,
                     RPL_DAO_ACK_UNCONDITIONAL_ACCEPT);
    }
  }
#endif /* RPL_WITH_STORING */
}
dao_input_storing(void)
{
#if RPL_WITH_STORING
  uip_ipaddr_t dao_sender_addr;
  rpl_dag_t *dag;
  rpl_instance_t *instance;
  unsigned char *buffer;
  uint16_t sequence;
  uint8_t instance_id;
  uint8_t lifetime;
  uint8_t prefixlen;
  uint8_t flags;
  uint8_t subopt_type;
   
  uip_ipaddr_t prefix;
  uip_ds6_route_t *rep;
  uint8_t buffer_length;
  int pos;
  int len;
  int i;
  int learned_from;
  rpl_parent_t *parent;
  uip_ds6_nbr_t *nbr;
  int is_root;

  prefixlen = 0;
  parent = NULL;
  memset(&prefix, 0, sizeof(prefix));

  uip_ipaddr_copy(&dao_sender_addr, &UIP_IP_BUF->srcipaddr);

  buffer = UIP_ICMP_PAYLOAD;
  buffer_length = uip_len - uip_l3_icmp_hdr_len;

  pos = 0;
  instance_id = buffer[pos++];

  instance = rpl_get_instance(instance_id);

  lifetime = instance->default_lifetime;

  flags = buffer[pos++];
   
  pos++;
  sequence = buffer[pos++];

  dag = instance->current_dag;
  is_root = (dag->rank == ROOT_RANK(instance));

   
  if(flags & RPL_DAO_D_FLAG) {
    if(memcmp(&dag->dag_id, &buffer[pos], sizeof(dag->dag_id))) {
      LOG_INFO("Ignoring a DAO for a DAG different from ours\n");
      return;
    }
    pos += 16;
  }

  learned_from = uip_is_addr_mcast(&dao_sender_addr) ?
    RPL_ROUTE_FROM_MULTICAST_DAO : RPL_ROUTE_FROM_UNICAST_DAO;

   
  LOG_DBG("Received a (%s) DAO with sequence number %u from ",
         learned_from == RPL_ROUTE_FROM_UNICAST_DAO? "unicast": "multicast", sequence);
  LOG_DBG_6ADDR(&dao_sender_addr);
  LOG_DBG_("\n");

  if(learned_from == RPL_ROUTE_FROM_UNICAST_DAO) {
     
    parent = rpl_find_parent(dag, &dao_sender_addr);
     
     
    if(parent != NULL &&
       DAG_RANK(parent->rank, instance) < DAG_RANK(dag->rank, instance)) {
      LOG_WARN("Loop detected when receiving a unicast DAO from a node with a lower rank! (%u < %u)\n",
             DAG_RANK(parent->rank, instance), DAG_RANK(dag->rank, instance));
      parent->rank = RPL_INFINITE_RANK;
      parent->flags |= RPL_PARENT_FLAG_UPDATED;
      return;
    }

     
    if(parent != NULL && parent == dag->preferred_parent) {
      LOG_WARN("Loop detected when receiving a unicast DAO from our parent\n");
      parent->rank = RPL_INFINITE_RANK;
      parent->flags |= RPL_PARENT_FLAG_UPDATED;
      return;
    }
  }

   
  for(i = pos; i < buffer_length; i += len) {
    subopt_type = buffer[i];
    if(subopt_type == RPL_OPTION_PAD1) {
      len = 1;
    } else {
       
      len = 2 + buffer[i + 1];
    }

    switch(subopt_type) {
      case RPL_OPTION_TARGET:
         
        prefixlen = buffer[i + 3];
        if(prefixlen == 0) {
           
          break;
        }
        if(prefixlen > 128) {
          LOG_ERR("Too large target prefix length %d\n", prefixlen);
          return;
        }
        if(i + 4 + ((prefixlen + 7) / CHAR_BIT) > buffer_length) {
          LOG_ERR("Insufficient space to copy RPL Target of %d bits\n",
                  prefixlen);
          return;
        }
        memset(&prefix, 0, sizeof(prefix));
        memcpy(&prefix, buffer + i + 4, (prefixlen + 7) / CHAR_BIT);
        break;
      case RPL_OPTION_TRANSIT:
         
         
        lifetime = buffer[i + 5];
         
        break;
    }
  }

  LOG_INFO("DAO lifetime: %u, prefix length: %u prefix: ",
         (unsigned)lifetime, (unsigned)prefixlen);
  LOG_INFO_6ADDR(&prefix);
  LOG_INFO_("\n");

#if RPL_WITH_MULTICAST
  if(uip_is_addr_mcast_global(&prefix)) {
     
    rep = NULL;
    mcast_group = uip_mcast6_route_add(&prefix);
    if(mcast_group) {
      mcast_group->dag = dag;
      mcast_group->lifetime = RPL_LIFETIME(instance, lifetime);
    }
    goto fwd_dao;
  }
#endif

  rep = uip_ds6_route_lookup(&prefix);

  if(lifetime == RPL_ZERO_LIFETIME) {
    LOG_INFO("No-Path DAO received\n");
     
    if(rep != NULL &&
       !RPL_ROUTE_IS_NOPATH_RECEIVED(rep) &&
       rep->length == prefixlen &&
       uip_ds6_route_nexthop(rep) != NULL &&
       uip_ipaddr_cmp(uip_ds6_route_nexthop(rep), &dao_sender_addr)) {
      LOG_DBG("Setting expiration timer for prefix ");
      LOG_DBG_6ADDR(&prefix);
      LOG_DBG_("\n");
      RPL_ROUTE_SET_NOPATH_RECEIVED(rep);
      rep->state.lifetime = RPL_NOPATH_REMOVAL_DELAY;

       
      if(dag->preferred_parent != NULL &&
         rpl_parent_get_ipaddr(dag->preferred_parent) != NULL) {
        uint8_t out_seq;
        out_seq = prepare_for_dao_fwd(sequence, rep);

        LOG_DBG("Forwarding No-path DAO to parent - out_seq:%d",
               out_seq);
        LOG_DBG_6ADDR(rpl_parent_get_ipaddr(dag->preferred_parent));
        LOG_DBG_("\n");

        buffer = UIP_ICMP_PAYLOAD;
        buffer[3] = out_seq;  
        uip_icmp6_send(rpl_parent_get_ipaddr(dag->preferred_parent),
                       ICMP6_RPL, RPL_CODE_DAO, buffer_length);
      }
    }
     
    if(flags & RPL_DAO_K_FLAG) {
       
      uipbuf_clear();
      dao_ack_output(instance, &dao_sender_addr, sequence,
                     RPL_DAO_ACK_UNCONDITIONAL_ACCEPT);
    }
    return;
  }

  LOG_INFO("Adding DAO route\n");

   
  if((nbr = rpl_icmp6_update_nbr_table(&dao_sender_addr, NBR_TABLE_REASON_RPL_DAO, instance)) == NULL) {
    LOG_ERR("Out of Memory, dropping DAO from ");
    LOG_ERR_6ADDR(&dao_sender_addr);
    LOG_ERR_(", ");
    LOG_ERR_LLADDR(packetbuf_addr(PACKETBUF_ADDR_SENDER));
    LOG_ERR_("\n");
    if(flags & RPL_DAO_K_FLAG) {
       
      dao_ack_output(instance, &dao_sender_addr, sequence,
                     is_root ? RPL_DAO_ACK_UNABLE_TO_ADD_ROUTE_AT_ROOT :
                     RPL_DAO_ACK_UNABLE_TO_ACCEPT);
    }
    return;
  }

  rep = rpl_add_route(dag, &prefix, prefixlen, &dao_sender_addr);
  if(rep == NULL) {
    RPL_STAT(rpl_stats.mem_overflows++);
    LOG_ERR("Could not add a route after receiving a DAO\n");
    if(flags & RPL_DAO_K_FLAG) {
       
      dao_ack_output(instance, &dao_sender_addr, sequence,
                     is_root ? RPL_DAO_ACK_UNABLE_TO_ADD_ROUTE_AT_ROOT :
                     RPL_DAO_ACK_UNABLE_TO_ACCEPT);
    }
    return;
  }

   
  rep->state.lifetime = RPL_LIFETIME(instance, lifetime);
  RPL_ROUTE_CLEAR_NOPATH_RECEIVED(rep);

#if RPL_WITH_MULTICAST
fwd_dao:
#endif

  if(learned_from == RPL_ROUTE_FROM_UNICAST_DAO) {
    int should_ack = 0;

    if(flags & RPL_DAO_K_FLAG) {
      if(rep != NULL) {
         
        if((!RPL_ROUTE_IS_DAO_PENDING(rep) &&
            rep->state.dao_seqno_in == sequence) ||
           dag->rank == ROOT_RANK(instance)) {
          should_ack = 1;
        }
      }
    }

    if(dag->preferred_parent != NULL &&
       rpl_parent_get_ipaddr(dag->preferred_parent) != NULL) {
      uint8_t out_seq = 0;
      if(rep != NULL) {
         
        if(RPL_ROUTE_IS_DAO_PENDING(rep) &&
           rep->state.dao_seqno_in == sequence) {
           
          out_seq = rep->state.dao_seqno_out;
        } else {
          out_seq = prepare_for_dao_fwd(sequence, rep);
        }
      }

      LOG_DBG("Forwarding DAO to parent ");
      LOG_DBG_6ADDR(rpl_parent_get_ipaddr(dag->preferred_parent));
      LOG_DBG_(" in seq: %d out seq: %d\n", sequence, out_seq);

      buffer = UIP_ICMP_PAYLOAD;
      buffer[3] = out_seq;  
      uip_icmp6_send(rpl_parent_get_ipaddr(dag->preferred_parent),
                     ICMP6_RPL, RPL_CODE_DAO, buffer_length);
    }
    if(should_ack) {
      LOG_DBG("Sending DAO ACK\n");
      uipbuf_clear();
      dao_ack_output(instance, &dao_sender_addr, sequence,
                     RPL_DAO_ACK_UNCONDITIONAL_ACCEPT);
    }
  }
#endif  
}