[Tinyos-2-commits] CVS: tinyos-2.x/tos/lib/net/blip ICMP.h, NONE, 1.1 ICMPResponderC.nc, NONE, 1.1 ICMPResponderP.nc, NONE, 1.1 IPAddressC.nc, NONE, 1.1 IPAddressP.nc, NONE, 1.1 IPDispatch.h, NONE, 1.1 IPDispatchC.nc, NONE, 1.1 IPDispatchP.nc, NONE, 1.1 IPRoutingP.nc, NONE, 1.1 PrintfUART.h, NONE, 1.1 TcpC.nc, NONE, 1.1 TcpP.nc, NONE, 1.1 TcpSocketC.nc, NONE, 1.1 UdpC.nc, NONE, 1.1 UdpP.nc, NONE, 1.1 UdpSocketC.nc, NONE, 1.1 table.c, NONE, 1.1 table.h, NONE, 1.1

sdhsdh sdhsdh at users.sourceforge.net
Mon Jan 19 16:28:18 PST 2009


Update of /cvsroot/tinyos/tinyos-2.x/tos/lib/net/blip
In directory ddv4jf1.ch3.sourceforge.com:/tmp/cvs-serv6583/tos/lib/net/blip

Added Files:
	ICMP.h ICMPResponderC.nc ICMPResponderP.nc IPAddressC.nc 
	IPAddressP.nc IPDispatch.h IPDispatchC.nc IPDispatchP.nc 
	IPRoutingP.nc PrintfUART.h TcpC.nc TcpP.nc TcpSocketC.nc 
	UdpC.nc UdpP.nc UdpSocketC.nc table.c table.h 
Log Message:
 - initial commit of blip (berkeley low-power ip) stack


--- NEW FILE: ICMP.h ---
/*
 * "Copyright (c) 2008 The Regents of the University  of California.
 * All rights reserved."
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 *
 */
#ifndef _ICMP_H_
#define _ICMP_H_

enum {
  ICMP_EXT_TYPE_PREFIX = 3,
  ICMP_EXT_TYPE_BEACON = 17,
};

enum {
  // jitter start requests by 10 seconds
  TRICKLE_JITTER = 10240,
  // have a trickle timer with a period of 4
  TRICKLE_PERIOD = 4096,

  // send a maximum of three trickle messages
  TRICKLE_MAX = (TRICKLE_PERIOD << 5),
  
};

typedef nx_struct icmp6_echo_hdr {
  nx_uint8_t        type;     /* type field */
  nx_uint8_t        code;     /* code field */
  nx_uint16_t       cksum;    /* checksum field */
  nx_uint16_t       ident;
  nx_uint16_t       seqno;
} icmp_echo_hdr_t;

typedef nx_struct radv {
  nx_uint8_t        type;
  nx_uint8_t        code;
  nx_uint16_t       cksum;
  nx_uint8_t        hlim;
  nx_uint8_t        flags;
  nx_uint16_t       lifetime;
  nx_uint32_t       reachable_time;
  nx_uint32_t       retrans_time;
  nx_uint8_t        options[0];
} radv_t;

typedef nx_struct rsol {
  nx_uint8_t type;
  nx_uint8_t code;
  nx_uint16_t cksum;
  nx_uint32_t reserved;
} rsol_t;

typedef nx_struct rpfx {
  nx_uint8_t type;
  nx_uint8_t length;
  nx_uint8_t pfx_len;
  nx_uint8_t flags;
  nx_uint32_t valid_lifetime;
  nx_uint32_t preferred_lifetime;
  nx_uint32_t reserved;
  nx_uint8_t  prefix[16];
} pfx_t;

typedef nx_struct {
  nx_uint8_t type;
  nx_uint8_t length;
  nx_uint16_t metric;
} rqual_t;

struct icmp_stats {
  uint16_t seq;
  uint8_t ttl;
  uint32_t rtt;
};

#endif

--- NEW FILE: ICMPResponderC.nc ---
/*
 * "Copyright (c) 2008 The Regents of the University  of California.
 * All rights reserved."
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 *
 */

#include <6lowpan.h>

configuration ICMPResponderC {
  provides interface ICMP;
  provides interface ICMPPing[uint16_t client];
  provides interface Statistics<icmp_statistics_t>;
} implementation {
  components NoLedsC as LedsC;
  components IPDispatchC, IPRoutingP, ICMPResponderP, IPAddressC;

  ICMP = ICMPResponderP;
  ICMPPing = ICMPResponderP;
  Statistics = ICMPResponderP;

  ICMPResponderP.Leds -> LedsC;

  ICMPResponderP.IP -> IPDispatchC.IP[IANA_ICMP];

  ICMPResponderP.IPAddress -> IPAddressC;

  ICMPResponderP.IPRouting -> IPRoutingP;

  components RandomC;
  ICMPResponderP.Random -> RandomC;

  components new TimerMilliC() as STimer,
    new TimerMilliC() as ATimer,
    new TimerMilliC() as PTimer;
  ICMPResponderP.Solicitation -> STimer;
  ICMPResponderP.Advertisement -> ATimer;
  ICMPResponderP.PingTimer -> PTimer;

  components HilTimerMilliC;
  ICMPResponderP.LocalTime -> HilTimerMilliC;
}

--- NEW FILE: ICMPResponderP.nc ---
/*
 * "Copyright (c) 2008 The Regents of the University  of California.
 * All rights reserved."
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 *
 */

#include <lib6lowpan.h>
#include <6lowpan.h>
#include <ip_malloc.h>
#include "in_cksum.h"
#include "PrintfUART.h"
#include "ICMP.h"

extern uint8_t multicast_prefix[8];

module ICMPResponderP {
  provides interface ICMP;
  provides interface ICMPPing[uint16_t client];
  provides interface Statistics<icmp_statistics_t>;

  uses interface IP;
  uses interface IPAddress;

  uses interface Leds;

  uses interface Timer<TMilli> as Solicitation;
  uses interface Timer<TMilli> as Advertisement;
  uses interface Timer<TMilli> as PingTimer;
  uses interface LocalTime<TMilli>;
  uses interface Random;

  uses interface IPRouting;

} implementation {

  icmp_statistics_t stats;
  uint32_t solicitation_period;
  uint32_t advertisement_period;

  uint16_t ping_seq, ping_n, ping_rcv, ping_ident;
  struct in6_addr ping_dest;

#ifndef SIM
#define CHECK_NODE_ID
#else
#define CHECK_NODE_ID if (TOS_NODE_ID == BASESTATION_ID) return
#endif

  command uint16_t ICMP.cksum(struct split_ip_msg *msg, uint8_t nxt_hdr) {
    return msg_cksum(msg, nxt_hdr);
  }


  command void ICMP.sendSolicitations() {
    uint16_t jitter = (call Random.rand16()) % TRICKLE_JITTER;
    CHECK_NODE_ID;
    if (call Solicitation.isRunning()) return;
    solicitation_period = TRICKLE_PERIOD;
    call Solicitation.startOneShot(jitter);
  }

  command void ICMP.sendAdvertisements() {
    uint16_t jitter = (call Random.rand16()) % TRICKLE_JITTER;
    CHECK_NODE_ID;
    if (call Advertisement.isRunning()) return;
    advertisement_period = TRICKLE_PERIOD;
    call Advertisement.startOneShot(jitter);
  }

  command void ICMP.sendTimeExceeded(struct ip6_hdr *hdr, unpack_info_t *u_info, uint16_t amount_here) {
    uint8_t i_hdr_buf[sizeof(struct icmp6_hdr) + 4];
    struct split_ip_msg *msg = (struct split_ip_msg *)ip_malloc(sizeof(struct split_ip_msg));
    struct generic_header g_hdr[3];
    struct icmp6_hdr *i_hdr = (struct icmp6_hdr *)i_hdr_buf;

    if (msg == NULL) return;

    dbg("ICMPResponder", "send time exceeded\n");

    msg->headers = NULL;
    msg->data = u_info->payload_start;
    msg->data_len = amount_here;

    // make sure to include the udp header if necessary
    if (u_info->nxt_hdr == IANA_UDP) {
      g_hdr[2].hdr.udp = (struct udp_hdr *)u_info->transport_ptr;
      g_hdr[2].len = sizeof(struct udp_hdr);
      g_hdr[2].next = NULL;
      
      // since the udp headers are included in the offset we need to
      // add that length so the payload length in the encapsulated
      // packet will be correct.
      hdr->plen = htons(ntohs(hdr->plen) + sizeof(struct udp_hdr));
      msg->headers = &g_hdr[2];
    }
    // the fields in the packed packet is not necessarily the same as
    // the fields in canonical packet which was packed.  This is due
    // to the insertion of transient routing headers.
    hdr->nxt_hdr = u_info->nxt_hdr;
    hdr->plen = htons(ntohs(hdr->plen) - u_info->payload_offset);

    // the IP header is part of the payload
    g_hdr[1].hdr.data = (void *)hdr;
    g_hdr[1].len = sizeof(struct ip6_hdr);
    g_hdr[1].next = msg->headers;
    msg->headers = &g_hdr[1];

    // and is preceeded by the icmp time exceeded message
    g_hdr[0].hdr.data = (void *)i_hdr;
    g_hdr[0].len = sizeof(struct icmp6_hdr) + 4;
    g_hdr[0].next = msg->headers;
    msg->headers = &g_hdr[0];

    ip_memcpy(&msg->hdr.ip6_dst, &hdr->ip6_src, 16);
    call IPAddress.getIPAddr(&msg->hdr.ip6_src);

    i_hdr->type = ICMP_TYPE_ECHO_TIME_EXCEEDED;
    i_hdr->code = ICMP_CODE_HOPLIMIT_EXCEEDED;
    i_hdr->cksum = 0;
    ip_memclr((void *)(i_hdr + 1), 4);

    msg->hdr.nxt_hdr = IANA_ICMP;

    i_hdr->cksum = call ICMP.cksum(msg, IANA_ICMP);

    call IP.send(msg);

    ip_free(msg);
  }
  /*
   * Solicitations
   */ 
  void sendSolicitation() {
    struct split_ip_msg *ipmsg = (struct split_ip_msg *)ip_malloc(sizeof(struct split_ip_msg) + sizeof(rsol_t));
    rsol_t *msg = (rsol_t *)(ipmsg + 1);

    if (ipmsg == NULL) return;

    dbg("ICMPResponder", "Solicitation\n");
    //stats.sol_tx++;

    msg->type = ICMP_TYPE_ROUTER_SOL;
    msg->code = 0;
    msg->cksum = 0;
    msg->reserved = 0;

    ipmsg->headers = NULL;
    ipmsg->data = (void *)msg;
    ipmsg->data_len = sizeof(rsol_t);
    
    // this is required for solicitation messages
    ipmsg->hdr.hlim = 0xff;


    call IPAddress.getLLAddr(&ipmsg->hdr.ip6_src);
    inet_pton6("ff02::2", &ipmsg->hdr.ip6_dst);

    msg->cksum = call ICMP.cksum(ipmsg, IANA_ICMP);

    call IP.send(ipmsg);

    ip_free(ipmsg);
  }

  void sendPing(struct in6_addr *dest, uint16_t seqno) {
    struct split_ip_msg *ipmsg = (struct split_ip_msg *)ip_malloc(sizeof(struct split_ip_msg) + 
                                                                  sizeof(icmp_echo_hdr_t) + 
                                                                  sizeof(nx_uint32_t));
    icmp_echo_hdr_t *e_hdr = (icmp_echo_hdr_t *)ipmsg->next;
    nx_uint32_t *sendTime = (nx_uint32_t *)(e_hdr + 1);

    if (ipmsg == NULL) return;
    ipmsg->headers = NULL;
    ipmsg->data = (void *)e_hdr;
    ipmsg->data_len = sizeof(icmp_echo_hdr_t) + sizeof(nx_uint32_t);

    e_hdr->type = ICMP_TYPE_ECHO_REQUEST;
    e_hdr->code = 0;
    e_hdr->cksum = 0;
    e_hdr->ident = ping_ident;
    e_hdr->seqno = seqno;
    *sendTime = call LocalTime.get();

    memcpy(&ipmsg->hdr.ip6_dst, dest->s6_addr, 16);
    call IPAddress.getIPAddr(&ipmsg->hdr.ip6_src);

    e_hdr->cksum = call ICMP.cksum(ipmsg,IANA_ICMP);

    call IP.send(ipmsg);
    ip_free(ipmsg);
  }

  /*
   * Router advertisements
   */ 
  void handleRouterAdv(void *payload, uint16_t len, struct ip_metadata *meta) {
    
    radv_t *r = (radv_t *)payload;
    pfx_t  *pfx = (pfx_t *)(r->options);
    uint16_t cost = 0;
    rqual_t *beacon = (rqual_t *)(pfx + 1);

    if (len > sizeof(radv_t) + sizeof(pfx_t) && 
        beacon->type == ICMP_EXT_TYPE_BEACON) {
      cost = beacon->metric;
      dbg("ICMPResponder", " * beacon cost: 0x%x\n", cost);
    } else 
        dbg("ICMPResponder", " * no beacon cost\n");

    call IPRouting.reportAdvertisement(meta->sender, r->hlim,
                                       meta->lqi, cost);

    if (pfx->type != ICMP_EXT_TYPE_PREFIX) return;

    call IPAddress.setPrefix((uint8_t *)pfx->prefix);

    // TODO : get short address here...
  }

  void sendAdvertisement() {
    struct split_ip_msg *ipmsg = (struct split_ip_msg *)ip_malloc(sizeof(struct split_ip_msg) + 
                                                                  sizeof(radv_t) + 
                                                                  sizeof(pfx_t) +
                                                                  sizeof(rqual_t));
    uint16_t len = sizeof(radv_t);
    radv_t *r = (radv_t *)(ipmsg + 1);
    pfx_t *p = (pfx_t *)r->options;
    rqual_t *q = (rqual_t *)(p + 1);

    if (ipmsg == NULL) return;
    // don't sent the advertisement if we don't have a valid route
    if (!call IPRouting.hasRoute()) {
      ip_free(ipmsg);
      return;
    }

    r->type = ICMP_TYPE_ROUTER_ADV;
    r->code = 0;
    r->hlim = call IPRouting.getHopLimit();
    r->flags = 0;
    r->lifetime = 1;
    r->reachable_time = 0;
    r->retrans_time = 0;

    ipmsg->hdr.hlim = 0xff;
    
    if (globalPrefix) {
      len += sizeof(pfx_t);
      p->type = ICMP_EXT_TYPE_PREFIX;
      p->length = 8;
      memcpy(p->prefix, call IPAddress.getPublicAddr(), 8);
    }

    len += sizeof(rqual_t);
    q->type = ICMP_EXT_TYPE_BEACON;
    q->length = 2;
    q->metric = call IPRouting.getQuality();

    call IPAddress.getLLAddr(&ipmsg->hdr.ip6_src);
    inet_pton6("ff02::1", &ipmsg->hdr.ip6_dst);

    //dbg("ICMPResponder", "My Address: [0x%x] [0x%x] [0x%x] [0x%x]\n", ipmsg->hdr.src_addr[12], ipmsg->hdr.src_addr[13], ipmsg->hdr.src_addr[14], ipmsg->hdr.src_addr[15]);
    dbg("ICMPResponder", "adv hop limit: 0x%x\n", r->hlim);

    if (r->hlim >= 0xf0) {
      ip_free(ipmsg);
      return;
    }

    ipmsg->data = (void *)r;
    ipmsg->data_len = len;
    ipmsg->headers = NULL;

    r->cksum = 0;
    r->cksum = call ICMP.cksum(ipmsg, IANA_ICMP);

    call IP.send(ipmsg);
    ip_free(ipmsg);
  }


  event void IP.recv(struct ip6_hdr *iph,
                     void *payload, 
                     struct ip_metadata *meta) {
    icmp_echo_hdr_t *req = (icmp_echo_hdr_t *)payload;
    uint16_t len = ntohs(iph->plen);
    stats.rx++;
  
    // for checksum calculation
    printfUART ("icmp type: 0x%x code: 0x%x cksum: 0x%x ident: 0x%x seqno: 0x%x len: 0x%x\n",
                req->type, req->code, req->cksum, req->ident, req->seqno, len);

    switch (req->type) {
    case ICMP_TYPE_ROUTER_ADV:
        handleRouterAdv(payload, len, meta);
        //stats.adv_rx++;
        break;
    case ICMP_TYPE_ROUTER_SOL:
      // only reply to solicitations if we have established a default route.
      if (call IPRouting.hasRoute()) {
          call ICMP.sendAdvertisements();
      }
      break;
    case ICMP_TYPE_ECHO_REPLY:
      {
        nx_uint32_t *sendTime = (nx_uint32_t *)(req + 1);
        struct icmp_stats p_stat;
        p_stat.seq = req->seqno;
        p_stat.ttl = 0;// buf->hdr.hlim;
        p_stat.rtt = (call LocalTime.get()) - (*sendTime);
        signal ICMPPing.pingReply[req->ident](&iph->ip6_src, &p_stat);
        ping_rcv++;
      }
      break;
    case ICMP_TYPE_ECHO_REQUEST:
      {
        // send a ping reply.
        struct split_ip_msg msg;
        msg.headers = NULL;
        msg.data = payload;
        msg.data_len = len;
        call IPAddress.getIPAddr(&msg.hdr.ip6_src);
        memcpy(&msg.hdr.ip6_dst, &iph->ip6_src, 16);      
        
        req->type = ICMP_TYPE_ECHO_REPLY;
        req->code = 0;
        req->cksum = 0;
        req->cksum = call ICMP.cksum(&msg, IANA_ICMP);
        
        // remember, this can't really fail in a way we care about
        call IP.send(&msg);
        break;
      }
    }
  }


  event void Solicitation.fired() {
    sendSolicitation();
    dbg("ICMPResponder", "solicitation period: 0x%x max: 0x%x\n", solicitation_period, TRICKLE_MAX);
    solicitation_period <<= 1;
    if (solicitation_period < TRICKLE_MAX) {
      call Solicitation.startOneShot(solicitation_period);
    } else {
      signal ICMP.solicitationDone();
    }
  }

  event void Advertisement.fired() {
    dbg("ICMPResponder", "==> Sending router advertisement\n");
    sendAdvertisement();
    advertisement_period <<= 1;
    if (advertisement_period < TRICKLE_MAX) {
      call Advertisement.startOneShot(advertisement_period);
    }
  }


  
  command error_t ICMPPing.ping[uint16_t client](struct in6_addr *target, uint16_t period, uint16_t n) {
    if (call PingTimer.isRunning()) return ERETRY;
    call PingTimer.startPeriodic(period);

    memcpy(&ping_dest, target, 16);
    ping_n = n;
    ping_seq = 0;
    ping_rcv = 0;
    ping_ident = client;
    return SUCCESS;
  }

  event void PingTimer.fired() {
    // send a ping request
    if (ping_seq == ping_n) {
      signal ICMPPing.pingDone[ping_ident](ping_rcv, ping_n);
      call PingTimer.stop();
      return;
    }
    sendPing(&ping_dest, ping_seq);
    ping_seq++;
  }



  command void Statistics.get(icmp_statistics_t *statistics) {
    statistics = &stats;
  }
  
  command void Statistics.clear() {
    ip_memclr((uint8_t *)&stats, sizeof(icmp_statistics_t));
  }

  default event void ICMPPing.pingReply[uint16_t client](struct in6_addr *source, 
                                                         struct icmp_stats *ping_stats) {
  }

  default event void ICMPPing.pingDone[uint16_t client](uint16_t n, uint16_t m) {

  }

}

--- NEW FILE: IPAddressC.nc ---
/*
 * "Copyright (c) 2008 The Regents of the University  of California.
 * All rights reserved."
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 *
 */


configuration IPAddressC {
  provides interface IPAddress;

} implementation {
  components IPAddressP, ActiveMessageAddressC;

  IPAddress = IPAddressP;

#ifndef SIM
  IPAddressP.ActiveMessageAddress -> ActiveMessageAddressC;
#else
  IPAddressP.setAmAddress -> ActiveMessageAddressC;
#endif
}

--- NEW FILE: IPAddressP.nc ---
/*
 * "Copyright (c) 2008 The Regents of the University  of California.
 * All rights reserved."
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 *
 */

#include <6lowpan.h>

// defined in lib6lowpan
extern struct in6_addr __my_address;
extern uint8_t globalPrefix;

module IPAddressP {
  provides interface IPAddress;

#ifndef SIM
  uses interface ActiveMessageAddress;
#else 
  uses async command void setAmAddress(am_addr_t a);
#endif
} implementation {


  command hw_addr_t IPAddress.getShortAddr() {
    return TOS_NODE_ID;
  }

  command void IPAddress.setShortAddr(hw_addr_t newAddr) {
    TOS_NODE_ID = newAddr;
#ifndef SIM
    call ActiveMessageAddress.setAddress(call ActiveMessageAddress.amGroup(), newAddr);
#else
    call setAmAddress(newAddr);
#endif
  }

  command void IPAddress.getLLAddr(struct in6_addr *addr) {
    memcpy(addr->s6_addr, linklocal_prefix, 8);
    memcpy(&addr->s6_addr[8], &__my_address.s6_addr[8], 8);
  }

  command void IPAddress.getIPAddr(struct in6_addr *addr) {
    __my_address.s6_addr16[7] = htons(TOS_NODE_ID);
    memcpy(addr, &__my_address, 16);
  }

  command struct in6_addr *IPAddress.getPublicAddr() {
    __my_address.s6_addr16[7] = htons(TOS_NODE_ID);
    return &__my_address;
  }

  command void IPAddress.setPrefix(uint8_t *pfx) {
    ip_memclr(__my_address.s6_addr, sizeof(struct in6_addr));
    ip_memcpy(__my_address.s6_addr, pfx, 8);
    globalPrefix = 1;
  }

  command bool IPAddress.haveAddress() {
    return globalPrefix;
  }

#ifndef SIM
  async event void ActiveMessageAddress.changed() {

  }
#endif

}

--- NEW FILE: IPDispatch.h ---
/*
 * "Copyright (c) 2008 The Regents of the University  of California.
 * All rights reserved."
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 *
 */
#ifndef _IPDISPATCH_H_
#define _IPDISPATCH_H_

#include <message.h>
#include <lib6lowpan.h>

enum {
  N_PARENTS = 3,
  N_EPOCHS = 2,
  N_EPOCHS_COUNTED = 1,
  N_RECONSTRUCTIONS = 2,
  N_FORWARD_ENT = IP_NUMBER_FRAGMENTS,
};

enum {
  CONF_EVICT_THRESHOLD = 5, // Neighbor is 'mature'
  CONF_PROM_THRESHOLD = 5, // Acceptable threshold for promotion
  MAX_CONSEC_FAILURES = 40, // Max Failures before reroute is attempted
  PATH_COST_DIFF_THRESH = 10, // Threshold for 'similar' path costs
  LQI_DIFF_THRESH = 10, // Threshold for 'similar' LQI's
  LINK_EVICT_THRESH = 50, // ETX * 10
  LQI_ADMIT_THRESH = 0x200, 
  RSSI_ADMIT_THRESH = 0,
  RANDOM_ROUTE = 20, //Percentage of time to select random default route
};

enum {
  WITHIN_THRESH = 1,
  ABOVE_THRESH = 2,
  BELOW_THRESH = 3,
};

enum {
  TGEN_BASE_TIME = 512,
  TGEN_MAX_INTERVAL = 60L * 1024L * 5L,
};

struct epoch_stats {
  uint16_t success;
  uint16_t total;
  uint16_t receptions;
};

struct report_stats {
  uint8_t messages;
  uint8_t transmissions;
  uint8_t successes;
};

enum {
  T_PIN_OFFSET    = 0,
  T_PIN_MASK      = 1 << T_PIN_OFFSET,
  T_VALID_OFFSET  = 2,
  T_VALID_MASK    = 1 << T_VALID_OFFSET,
  T_MARKED_OFFSET = 3,
  T_MARKED_MASK   = 1 << T_MARKED_OFFSET,
  T_MATURE_OFFSET = 4,
  T_MATURE_MASK   = 1 << T_MATURE_OFFSET,
  T_EVICT_OFFSET  = 5,
  T_EVICT_MASK    = 1 << T_EVICT_OFFSET,
};

enum {
  // store the top-k neighbors.  This could be a poor topology
  // formation critera is very dense networks.  we may be able to
  // really use the fact that the "base" has infinite memory.
  N_NEIGH = 8,
  N_LOW_NEIGH = 2,
  N_FREE_NEIGH = (N_NEIGH - N_LOW_NEIGH),
  N_FLOW_ENT = 6,
  N_FLOW_CHOICES = 2,
  N_PARENT_CHOICES = 3,
  T_DEF_PARENT = 0xfffd,
  T_DEF_PARENT_SLOT = 0,
};

typedef struct {
  // The extra 2 is because one dest could be from source route, other
  //  from the dest being a direct neighbor
  hw_addr_t dest[N_FLOW_CHOICES + N_PARENT_CHOICES + 2];
  uint8_t   current:4;
  uint8_t   nchoices:4;
  uint8_t   retries;
  uint8_t   actRetries;
  uint8_t   delay;
} send_policy_t;

typedef struct {
  send_policy_t policy;
  uint8_t frags_sent;
  bool failed;
  uint8_t refcount;
} send_info_t;

typedef struct {
  send_info_t *info;
  message_t  *msg;
} send_entry_t;

typedef struct {
  uint8_t timeout;
  hw_addr_t l2_src;
  uint16_t old_tag;
  uint16_t new_tag;
  send_info_t *s_info;
} forward_entry_t;

/* typedef struct { */
/*   /\* how to dispatch this packet *\/ */
/*   union { */
/*     struct sockaddr_in6 sock; */
/*     ip6_addr_t src; */
/*   } address; */
/*   /\* packet metadata *\/ */
/*   union { */
/*     uint16_t udp_port; */
/*   } dispatch; */
/*   struct ip_metadata metadata; */

/*   /\* the lib6lowpan reconstruct structure *\/ */
/*   reconstruct_t recon; */
/* } ip_recon_t; */

enum {
  F_VALID_MASK = 0x01,
  //F_TOTAL_VALID_ENTRY_MASK = 0x80, // For entire entry (not just specific choice)
  F_FULL_PATH_OFFSET = 1,
  F_FULL_PATH_MASK = 0x02,

  MAX_PATH_LENGTH = 10,
  N_FULL_PATH_ENTRIES = (N_FLOW_CHOICES * N_FLOW_ENT),
};
  
struct flow_path {
  uint8_t path_len;
  cmpr_ip6_addr_t path[MAX_PATH_LENGTH];
};

struct f_entry {
  uint8_t flags;
  union {
    struct flow_path *pathE;
    cmpr_ip6_addr_t nextHop;
  };
};

// Need to add another entry to avoid useless padding
//  Or can make sure that the flow_table has an even
//  number of entries.
struct flow_entry {
  uint8_t flags;
  uint8_t count;
  struct flow_match match;
  struct f_entry entries[N_FLOW_CHOICES];
};

//#define IS_VALID_SLOT(f) (((f)->entries[0].flags & F_TOTAL_VALID_ENTRY_MASK) == F_TOTAL_VALID_ENTRY_MASK)
#define IS_VALID_SLOT(f) (((f)->flags & F_VALID_MASK) == F_VALID_MASK)
//#define SET_VALID_SLOT(f) (f)->entries[0].flags |= F_TOTAL_VALID_ENTRY_MASK
#define SET_VALID_SLOT(f) (f)->flags |= F_VALID_MASK
//#define SET_INVALID_SLOT(f) (f)->entries[0].flags &= ~F_TOTAL_VALID_ENTRY_MASK
#define SET_INVALID_SLOT(f) (f)->flags &= ~F_VALID_MASK
#define IS_VALID_ENTRY(e) (((e).flags & F_VALID_MASK) == F_VALID_MASK)
#define SET_VALID_ENTRY(e) (e).flags |= F_VALID_MASK
#define SET_INVALID_ENTRY(e) (e).flags &= ~F_VALID_MASK
#define IS_FULL_TYPE(e) (((e).flags & F_FULL_PATH_MASK) == F_FULL_PATH_MASK)
#define IS_HOP_TYPE(e) !IS_FULL_TYPE(e)
#define SET_FULL_TYPE(e) ((e).flags |= F_FULL_PATH_MASK)
#define SET_HOP_TYPE(e) ((e).flags &= ~F_FULL_PATH_MASK)
  

struct neigh_entry {
  uint8_t flags;
  uint8_t hops; // Put this before neighbor to remove potential padding issues
  hw_addr_t neighbor;
  uint16_t costEstimate;
  uint16_t linkEstimate;
  struct epoch_stats stats[N_EPOCHS];
}
#ifdef MIG
 __attribute__((packed));
#else
;
#endif

#define IS_NEIGH_VALID(e) (((e)->flags & T_VALID_MASK) == T_VALID_MASK)
#define SET_NEIGH_VALID(e) ((e)->flags |= T_VALID_MASK)
#define SET_NEIGH_INVALID(e) ((e)->flags &= ~T_VALID_MASK)
#define PINNED(e) (((e)->flags & T_PIN_MASK) == T_PIN_MASK)
#define REMOVABLE(e) (((e)->refCount == 0) && !(PINNED(e)))
#define SET_PIN(e) (((e)->flags |= T_PIN_MASK))
#define UNSET_PIN(e) (((e)->flags &= ~T_PIN_MASK))
#define IS_MARKED(e) (((e)->flags & T_MARKED_MASK) == T_MARKED_MASK)
#define SET_MARK(e) (((e)->flags |= T_MARKED_MASK))
#define UNSET_MARK(e) (((e)->flags &= ~T_MARKED_MASK))
#define IS_MATURE(e) (((e)->flags & T_MATURE_MASK) == T_MATURE_MASK)
#define SET_MATURE(e) ((e)->flags |= T_MATURE_MASK)
#define SET_EVICT(e) ((e).flags |= T_EVICT_MASK)
#define UNSET_EVICT(e) ((e).flags &= ~T_EVICT_MASK)
#define SHOULD_EVICT(e) ((e).flags & T_EVICT_MASK)


typedef enum {
  S_FORWARD,
  S_REQ,
} send_type_t;


typedef nx_struct {
  nx_uint16_t sent;
  nx_uint16_t forwarded;
  nx_uint8_t rx_drop;
  nx_uint8_t tx_drop;
  nx_uint8_t fw_drop;
  nx_uint8_t rx_total;
  nx_uint8_t real_drop;
  nx_uint8_t hlim_drop;
  nx_uint8_t senddone_el;
  nx_uint8_t fragpool;
  nx_uint8_t sendinfo;
  nx_uint8_t sendentry;
  nx_uint8_t sndqueue;
  nx_uint8_t encfail;
  nx_uint16_t heapfree;
} ip_statistics_t;


typedef nx_struct {
  nx_uint8_t hop_limit;
  nx_uint16_t parent;
  nx_uint16_t parent_metric;
  nx_uint16_t parent_etx;
} route_statistics_t;

typedef nx_struct {
/*   nx_uint8_t sol_rx; */
/*   nx_uint8_t sol_tx; */
/*   nx_uint8_t adv_rx; */
/*   nx_uint8_t adv_tx; */
/*   nx_uint8_t unk_rx; */
  nx_uint16_t rx;
} icmp_statistics_t;

typedef nx_struct {
  nx_uint16_t total;
  nx_uint16_t failed;
  nx_uint16_t seqno;
  nx_uint16_t sender;
} udp_statistics_t;

#endif

--- NEW FILE: IPDispatchC.nc ---
/*
 * "Copyright (c) 2008 The Regents of the University  of California.
 * All rights reserved."
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 *
 */

/*
 * Provides message dispatch based on the next header field of IP packets.
 *
 */
#include "IPDispatch.h"

configuration IPDispatchC {
  provides {
    interface SplitControl;
    interface IPAddress;
    interface IP[uint8_t nxt_hdr];

    interface Statistics<ip_statistics_t> as IPStats;
    interface Statistics<route_statistics_t> as RouteStats;
    interface Statistics<icmp_statistics_t> as ICMPStats;

  }
} implementation {
  
#ifndef SIM
  components CC2420ActiveMessageC as MessageC;
#else
  components ActiveMessageC as MessageC;
#endif
  components MainC, IPDispatchP, IPAddressC, IPRoutingP; 
  components NoLedsC as LedsC;
  components RandomC;

  SplitControl = IPDispatchP.SplitControl;
  IPAddress = IPAddressC;
  IP = IPDispatchP;

  IPDispatchP.Boot -> MainC;

#ifndef SIM
  IPDispatchP.IEEE154Send -> MessageC;
  IPDispatchP.IEEE154Receive -> MessageC;
#else
  IPDispatchP.IEEE154Send -> MessageC.AMSend[0];
  IPDispatchP.IEEE154Receive -> MessageC.Receive[0];
#endif
  IPDispatchP.Packet -> MessageC.SubAMPacket;
#ifdef LOW_POWER_LISTENING
  IPDispatchP.LowPowerListening -> MessageC;
#endif


  IPDispatchP.IEEE154Packet -> MessageC;
  IPDispatchP.PacketLink -> MessageC;
  IPDispatchP.CC2420Packet -> MessageC;

#ifdef DBG_TRACK_FLOWS
  IPDispatchP.getFlowID -> MessageC;
#endif


  IPDispatchP.Leds -> LedsC;

  IPDispatchP.IPAddress -> IPAddressC;

  components new TimerMilliC();
  IPDispatchP.ExpireTimer -> TimerMilliC;

  components new PoolC(message_t, IP_NUMBER_FRAGMENTS) as FragPool;

  components new PoolC(send_entry_t, IP_NUMBER_FRAGMENTS) as SendEntryPool;
  components new QueueC(send_entry_t *, IP_NUMBER_FRAGMENTS);

  components new PoolC(send_info_t, N_FORWARD_ENT) as SendInfoPool;

  IPDispatchP.FragPool -> FragPool;
  IPDispatchP.SendEntryPool -> SendEntryPool;
  IPDispatchP.SendInfoPool  -> SendInfoPool;
  IPDispatchP.SendQueue -> QueueC;

  components ICMPResponderC;
  components new TimerMilliC() as TGenTimer;
  IPDispatchP.ICMP -> ICMPResponderC;
  IPRoutingP.ICMP  -> ICMPResponderC;
  IPDispatchP.RadioControl -> MessageC;

  IPDispatchP.IPRouting -> IPRoutingP;
  IPRoutingP.Boot -> MainC;
  IPRoutingP.Leds -> LedsC;
  IPRoutingP.IPAddress -> IPAddressC;
  IPRoutingP.Random -> RandomC;
  IPRoutingP.TrafficGenTimer -> TGenTimer;
  IPRoutingP.TGenSend -> IPDispatchP.IP[NXTHDR_UNKNOWN];

  IPStats    = IPDispatchP;
  RouteStats = IPRoutingP;
  ICMPStats  = ICMPResponderC;

  components new TimerMilliC() as RouteTimer;
  IPRoutingP.SortTimer -> RouteTimer;

#ifdef DELUGE
  components NWProgC;
#endif

}

--- NEW FILE: IPDispatchP.nc ---
/*
 * "Copyright (c) 2008 The Regents of the University  of California.
 * All rights reserved."
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
[...974 lines suppressed...]
   * Statistics interface
   */
  command void Statistics.get(ip_statistics_t *statistics) {
    stats.fragpool = call FragPool.size();
    stats.sendinfo = call SendInfoPool.size();
    stats.sendentry= call SendEntryPool.size();
    stats.sndqueue = call SendQueue.size();
    stats.heapfree = ip_malloc_freespace();
    ip_memcpy(statistics, &stats, sizeof(ip_statistics_t));
  }

  command void Statistics.clear() {
    ip_memclr((uint8_t *)&stats, sizeof(ip_statistics_t));
  }

  default event void IP.recv[uint8_t nxt_hdr](struct ip6_hdr *iph,
                                              void *payload,
                                              struct ip_metadata *meta) {
  }
}

--- NEW FILE: IPRoutingP.nc ---
/*
 * "Copyright (c) 2008 The Regents of the University  of California.
 * All rights reserved."
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 *
 */

#include "IPDispatch.h"
#include "PrintfUART.h"

module IPRoutingP {
  provides interface IPRouting;
  provides interface Statistics<route_statistics_t>;
  uses interface ICMP;
  uses interface Boot;
  uses interface IPAddress;
  uses interface Random;

  uses interface Timer<TMilli> as SortTimer;

  uses interface IP as TGenSend;
  uses interface Timer<TMilli> as TrafficGenTimer;

  uses interface Leds;

} implementation {

  enum {
    SHORT_EPOCH = 0,
    LONG_EPOCH = 1,
  };

  //uint16_t current_epoch;
  //route_statistics_t stats;
  uint16_t last_qual;
  uint8_t last_hops;

  uint8_t num_low_neigh;

  bool soliciting;

  // pointer into the neighbor table of the current entry that is our
  // first choice.
  struct neigh_entry *default_route;
  uint16_t default_route_failures;

  uint32_t traffic_interval;
  bool traffic_sent;

  struct neigh_entry neigh_table[N_NEIGH];

  void printTable();
  error_t freeFullPath(struct flow_path* path);
  void updateFlowCounts(struct flow_entry *target);
  void updateRankings();
  void swapNodes(struct neigh_entry *highNode, struct neigh_entry *lowNode);
  uint8_t checkThresh(uint32_t firstVal, uint32_t secondVal, uint16_t thresh);
  void evictNeighbor(struct neigh_entry *neigh);
  uint16_t getMetric(struct neigh_entry *neigh);

  uint16_t adjustLQI(uint8_t val) {
    uint16_t result = (80 - (val - 50));
    result = (((result * result) >> 3) * result) >> 3;  // result = (result ^ 3) / 64
    dbg("Lqi", "adjustLqi in: 0x%x out: 0x%x\n", val, result);
    return result;
  }

  void clearStats(struct neigh_entry *r) {
    int j;
    for (j = 0; j < N_EPOCHS; j++) {
      r->stats[j].total   = 0;
      r->stats[j].success = 0;
      r->stats[j].receptions = 0;
    }
  }

  void clearEpoch(uint8_t target_epoch) {
    int i;
    for (i = 0; i < N_NEIGH; i++) {
      neigh_table[i].stats[target_epoch].total = 0;
      neigh_table[i].stats[target_epoch].success = 0;
      neigh_table[i].stats[target_epoch].receptions = 0;
    }
  }

  cmpr_ip6_addr_t shortenAddr(ip6_addr_t addr) {
    cmpr_ip6_addr_t shortAddr;
    ip_memcpy(&shortAddr, &addr[14],2);
    return shortAddr;
  } 

  void restartTrafficGen() {
    traffic_interval = TGEN_BASE_TIME;
    // jitter the period by 10% to prevent synchronization
    traffic_interval += (call Random.rand16()) % (TGEN_BASE_TIME);
    if (call TrafficGenTimer.isRunning())
      call TrafficGenTimer.stop();

    call TrafficGenTimer.startOneShot(traffic_interval);
  }

  event void TrafficGenTimer.fired() {
    struct split_ip_msg *msg;
    if (traffic_sent) goto done;
    msg = (struct split_ip_msg *)ip_malloc(sizeof(struct split_ip_msg));
    if (msg == NULL) goto done;

    ip_memclr((uint8_t *)&msg->hdr, sizeof(struct ip6_hdr));
    inet_pton6("ff05::1", &msg->hdr.ip6_dst);
    call IPAddress.getIPAddr(&msg->hdr.ip6_src);
    msg->data = NULL;
    msg->data_len = 0;
    msg->headers = NULL;

    dbg("IPRouting", "Sending generated message\n");
    call TGenSend.send(msg);
    ip_free(msg);
  done:
    // restart timer
    dbg("IPRouting", "Done checking for tgen\n");
    traffic_sent = FALSE;
    traffic_interval *= 2;
    if (traffic_interval > TGEN_MAX_INTERVAL)
      traffic_interval = TGEN_MAX_INTERVAL;
    call TrafficGenTimer.startOneShot(traffic_interval);
  }

  event void TGenSend.recv(struct ip6_hdr *iph,
                           void *payload, 
                           struct ip_metadata *meta) {

  }

  event void Boot.booted() {
    int i;

    for (i = 0; i < N_NEIGH; i++) {
      neigh_table[i].flags = 0;
      clearStats(&neigh_table[i]);
    }

    // current_epoch = 0;
    soliciting = FALSE;
    //reRouting = FALSE;
    default_route_failures = 0;
    default_route = &neigh_table[0];
    // boot with this true so the router will invalidate any state
    // associated from us when it gets the first packet.
    last_qual = 0xffff;
    last_hops = 0xff;
    num_low_neigh = 0;
    call Statistics.clear();
    call SortTimer.startPeriodic(1024L * 60);

    traffic_sent = FALSE;
    restartTrafficGen();
  }
  
  command bool IPRouting.isForMe(struct ip6_hdr *hdr) {
    // the destination prefix is either link-local or global, or
    // multicast (we accept all multicast packets), and the suffix is
    // me.
    struct in6_addr *my_address = call IPAddress.getPublicAddr();
    return (((cmpPfx(my_address->s6_addr, hdr->ip6_dst.s6_addr) || 
              cmpPfx(linklocal_prefix, hdr->ip6_dst.s6_addr)) 
             && cmpPfx(&my_address->s6_addr[8], &hdr->ip6_dst.s6_addr[8])) 
            || cmpPfx(multicast_prefix, hdr->ip6_dst.s6_addr));
  }


  struct neigh_entry *getNeighEntry(cmpr_ip6_addr_t a) {
    int i;
    for (i = 0; i < N_NEIGH; i++) {
      if (neigh_table[i].neighbor == a)
        return &(neigh_table[i]);
    }
    return NULL;
  }


  uint16_t getConfidence(struct neigh_entry *neigh) {
    //uint8_t i;
    uint16_t conf = 0;
    if (neigh != NULL && IS_NEIGH_VALID(neigh)) {
      //for (i = 0; i < N_EPOCHS_COUNTED; i++) {
      //conf += neigh->stats[(current_epoch + N_EPOCHS - i) % N_EPOCHS].total;
        //}
      conf = neigh->stats[LONG_EPOCH].total;
    }
    return conf;
  }

  uint16_t getReceptions(struct neigh_entry *neigh) {
    //uint8_t i;
    uint16_t receptions = 0;
    if ((neigh != NULL) && (IS_NEIGH_VALID(neigh))) {
      //for (i = 0; i < N_EPOCHS_COUNTED; i++) {
      //receptions += neigh->stats[(current_epoch + N_EPOCHS - i) % N_EPOCHS].receptions;
        //}
      receptions += neigh->stats[receptions].receptions;
    }
    return receptions;
  }

  uint16_t getSuccess(struct neigh_entry *neigh) {
    //uint8_t i;
    uint16_t succ = 0;
    if ((neigh != NULL) && (IS_NEIGH_VALID(neigh))) {
      //for (i = 0; i < N_EPOCHS_COUNTED; i++) {
      //succ += neigh->stats[(current_epoch + N_EPOCHS - i) % N_EPOCHS].success;
      //}
      succ += neigh->stats[LONG_EPOCH].success;
    }
    return succ;
  }

  uint16_t getLinkCost(struct neigh_entry *neigh) {
    uint16_t conf, succ;
    conf = getConfidence(neigh);
    succ = getSuccess(neigh);
    // we can return a real confidence if we have enough data
    if (succ == 0 || conf == 0) return 0xff;
    return ((conf * 10) / succ);
  }
        

  void printTable() {
#ifdef PRINTFUART_ENABLED
    uint8_t i;
#ifdef CENTRALIZED_ROUTING
    uint8_t j, k;
#endif
    dbg("Table", "----------------------------------------___\n");
    dbg("Table", "ind\tvalid\tmature\tneigh\thops\tconf\trecep\tcost\tetx\tlqi\tmetric\n");
    for (i = 0; i < N_NEIGH; i++) {
      if (&neigh_table[i] == default_route)
        dbg("Table", "-- default --\n");
      dbg("Table", "0x%x\t0x%x\t0x%x\t0x%x\t0x%x\t0x%x\t0x%x\t0x%x\t0x%x\t0x%x\t0x%x\n", i, 
          (neigh_table[i].flags & T_VALID_MASK), (IS_MATURE(&(neigh_table[i]))),
          neigh_table[i].neighbor, neigh_table[i].hops, getConfidence(&(neigh_table[i])), 
          getReceptions(&(neigh_table[i])), neigh_table[i].costEstimate, 
          getLinkCost(&(neigh_table[i])), neigh_table[i].linkEstimate, 
          getMetric(&(neigh_table[i])));
    }
    dbg("Table", "----------------------------------------\n");
#endif
  }

  uint16_t getMetric(struct neigh_entry *r) {
    return (((r == NULL) || (!(IS_NEIGH_VALID(r)))) ? 
            0xffff : (r->costEstimate + getLinkCost(r)));
  }

  // Selects a potential neighbor that is not the current default route
  void chooseNewRandomDefault(bool force) {
    uint8_t i;
    uint8_t numNeigh = 0;
    uint8_t chosenNeigh;

    dbg("IPRouting", "Looking for a new default route\n");

    for (i = 1; i < N_NEIGH; i++) {
      if (!(IS_NEIGH_VALID(&(neigh_table[i])))) break;
      if (&neigh_table[i] == default_route) continue;
      if (neigh_table[i].hops < neigh_table[0].hops) 
        numNeigh++;
    }

    // There exist other neighbors with respectable hop counts
    if (numNeigh) {
      chosenNeigh = (call Random.rand16()) % numNeigh;
      for (i = 1; i < N_NEIGH; i++) {
        if (&neigh_table[i] == default_route) continue;
        if (neigh_table[i].hops < neigh_table[0].hops) {
          if (chosenNeigh) {
            chosenNeigh--;
          } else {
            default_route = &neigh_table[i];
            default_route_failures = 0;
            return;
          }
        }
      }
    }

    if (!force) goto done;

    numNeigh = 0;
    for (i = 1; i < N_NEIGH; i++) {
      if (!(IS_NEIGH_VALID(&(neigh_table[i])))) break;
      if (&neigh_table[i] == default_route) continue;
      if (neigh_table[i].costEstimate < neigh_table[0].costEstimate)
        numNeigh++;
    }

    if (numNeigh) {
      chosenNeigh = (call Random.rand16()) % numNeigh;
      for (i = 1; i < N_NEIGH; i++) {
        if (&neigh_table[i] == default_route) continue;
        //if (neigh_table[i].costEstimate < getMetric(&(neigh_table[0]))) {
        if (neigh_table[i].costEstimate < neigh_table[0].costEstimate) {
          if (chosenNeigh) { 
            chosenNeigh--;
          } else {
            default_route = &neigh_table[i];
            default_route_failures = 0;
            return;
          }
        }
      }
    }
  done:
    dbg("IPRouting", "No random route found\n");
    default_route = &neigh_table[0];
    default_route_failures = 0;
  }
  /*
   * return: a send policy for a given attempt, including destination and one-hop neighbor.
   *        if no default route is available, returns FAIL unless the
   *        packet is destined to a link-local address, or a
   *        all-node/all-routers local multicast group.
   *
   */
  command error_t IPRouting.getNextHop(struct ip6_hdr *hdr, 
                                       struct source_header *sh,
                                       hw_addr_t prev_hop,
                                       send_policy_t *ret) {
    
    int i;
    prev_hop = 0;
    ret->retries = 10;
    ret->delay = 30;
    ret->current = 0;
    ret->nchoices = 0;
 

    // we only use the address in the source header if the record option is not used
    // otherwise, we use normal routing.
    if (hdr->nxt_hdr == NXTHDR_SOURCE && 
        (sh->dispatch & IP_EXT_SOURCE_RECORD_MASK) != IP_EXT_SOURCE_RECORD && 
        (sh->dispatch & IP_EXT_SOURCE_INVAL) != IP_EXT_SOURCE_INVAL) {
      // if it's source routed, grab the next address out of the header.

      // if (sh->current == sh->nentries) return FAIL;

      ret->dest[0] = ntohs(sh->hops[sh->current]);
      ret->nchoices = 1;

      dbg("IPRouting", "source dispatch: 0x%x, next hop: 0x%x, current: 0x%x\n", 
                 sh->dispatch, ntohs(sh->hops[sh->current]), sh->current);

    } else if (hdr->ip6_dst.s6_addr16[0] == htons(0xff02)) {
      //hdr->dst_addr[0] == 0xff && (hdr->dst_addr[1] & 0xf) == 0x2) {
      // if it's multicast, for now, we send it to the local broadcast
      ret->dest[0] = 0xffff;
      ret->nchoices = 1;
      ret->retries = 0;
      ret->delay = 0;
      return SUCCESS;
    } else if (cmpPfx(hdr->ip6_dst.s6_addr, linklocal_prefix)) {
      ret->dest[0] = ntohs(hdr->ip6_dst.s6_addr16[7]); //  (hdr->dst_addr[14] << 8) | hdr->dst_addr[15];
      ret->nchoices = 1;
      return SUCCESS; // Currently only want one choice for broadcast
    } 
      //     if (getNeighEntry(ntohs(shortenAddr(hdr->dst_addr))) != NULL) {
      //       dbg("IPRouting", "Directly adding next hop of dest: 0x%x\n", ntohs(shortenAddr(hdr->dst_addr)));
      //       ret->dest[ret->nchoices++] = ntohs(shortenAddr(hdr->dst_addr));
      //     }
    
    
    //dbg("IPRouting", "flags: 0x%x neigh: 0x%x\n", r->flags, r->neighbor);
    if (IS_NEIGH_VALID(default_route) && prev_hop != default_route->neighbor) {
      ret->dest[ret->nchoices++] = default_route->neighbor;
    } else {
      dbg("IPRouting", "Invalid default route... quitting\n");
      return FAIL;
    }
    i = 0;
    while (ret->nchoices < N_PARENT_CHOICES && i < N_NEIGH) {
      if (IS_NEIGH_VALID(&neigh_table[i]) &&
          &neigh_table[i] != default_route &&
          neigh_table[i].neighbor != prev_hop) {
        ret->dest[ret->nchoices++] = neigh_table[i].neighbor;
      }
      i++;
    }
    
    if (ret->nchoices == 0)
      return FAIL;
    
    dbg("IPRouting", "getNextHop: nchoices: 0x%x\n", ret->nchoices);
    
    return SUCCESS;
  }

  command uint8_t IPRouting.getHopLimit() {
    // advertise our best path to the root
    if (IS_NEIGH_VALID(&(neigh_table[0])))// && IS_MATURE(&neigh_table[0]))
      return neigh_table[0].hops + 1;
    else return 0xf0;
  }

  command uint16_t IPRouting.getQuality() {
    if (IS_NEIGH_VALID(&(neigh_table[0])))
      return getMetric(&(neigh_table[0]));
    else return 0xffff;
  }

  
  /*
   * An advertisement was received from a neighboring node
   *
   * Processing steps:
   * 1) First must check to see if the neighbor already exists in the table
   *  a) If so, we are just updating its information
   * 2) If not in table, check to make sure that the lqi passes the low-filter bar.
   *  a) If not, return
   * 3) If there is an empty space
   *  a) Insert it in the open space
   *  b) (Do we then want to move it up to where it belongs based on total path cost?)
   * 4) If there is no open space
   *  a) If the last entry doesn't meet the confidence threshold (CONF_EVICT_THRESHOLD), do nothing
   *  b) Otherwise, replace last entry if:
   *    i) Advertised Path Cost difference is greater than PATH_COST_DIFF_THRESH
   *    ii) Advertised Path Cost difference is within PATH_COST_DIFF_THRESH, 
   *         and Link estimate is lower by at least LQI_DIFF_THRESH
   * 5) Make sure to update the receptions statistic
   */ 
  command void IPRouting.reportAdvertisement(hw_addr_t neigh, uint8_t hops, 
                                             uint8_t lqi, uint16_t cost) {
    //int i, place = N_NEIGH;
    //bool mustInsert = FALSE, exists = FALSE;
    //uint8_t maxCost = 0;
    //bool recount = FALSE;
    struct neigh_entry *neigh_slot =  NULL;
    dbg("IPRouting", "report adv: 0x%x 0x%x 0x%x 0x%x\n", neigh, hops, lqi, cost);
    dbg("IPRouting", "my Cost: 0x%x, num_low_neigh: 0x%x, N_LOW_NEIGH: 0x%x\n", 
               getMetric(&(neigh_table[0])), num_low_neigh, N_LOW_NEIGH);
   
    // If neighbor does not exist in table 
    if ((neigh_slot = getNeighEntry(neigh)) == NULL) {
      dbg("IPRouting", "Advertisement from new neighbor 0x%x!\n", neigh);
      if (adjustLQI(lqi) > LQI_ADMIT_THRESH || cost == 0xffff) {
        dbg("IPRouting", "Poor Link.  Rejecting\n");
        return;
      }
      // free spots in the table.
      if(!(IS_NEIGH_VALID(&(neigh_table[N_NEIGH - 1])))) {
        
        dbg("IPRouting", "Neighbor being inserted in empty slot: 0x%x\n", N_NEIGH - 1);
        for (neigh_slot = &(neigh_table[N_NEIGH - 1]); 
             neigh_slot > &(neigh_table[0]); neigh_slot--) {
          // we might go ahead of other neighbors if we haven't heard
          // from them either and our cost is better.
          if (IS_NEIGH_VALID(neigh_slot - 1) &&
              getConfidence(neigh_slot - 1) == 0 &&
              (((struct neigh_entry *)(neigh_slot - 1))->costEstimate > cost)) {
            swapNodes((neigh_slot - 1), neigh_slot);
          } else if (IS_NEIGH_VALID(neigh_slot - 1)) {
            // if we didn't catch on the first check and the next
            // highest guy in the table is valid, we'll just go at the
            // end.  If this never catches, the loop will terminate
            // with neigh_slot == &neigh_table[0].
            break;
          }
        }
        ip_memclr((void *)neigh_slot, sizeof(struct neigh_entry));
      } else {
        // evict the bottom guy?
        dbg("IPRouting", "No empty slots...looking to replace bottom entry\n");
        //if (getConfidence(&(neigh_table[N_NEIGH - 1])) >= CONF_EVICT_THRESHOLD) {
        if (IS_MATURE(&(neigh_table[N_NEIGH - 1])) ||
            hops <= neigh_table[N_NEIGH - 1].hops) {
          dbg("IPRouting", "Bottom entry evictable\n");
          // we're a lot better,
          if ((checkThresh(neigh_table[N_NEIGH - 1].costEstimate, cost, 
                           PATH_COST_DIFF_THRESH) == BELOW_THRESH) || 
              // or we're about equal and the link estimate is better
              ((checkThresh(neigh_table[N_NEIGH - 1].costEstimate, cost, 
                            PATH_COST_DIFF_THRESH) == WITHIN_THRESH) && 
               (checkThresh(neigh_table[N_NEIGH - 1].linkEstimate, adjustLQI(lqi), 
                            LQI_DIFF_THRESH) == BELOW_THRESH))) {
            dbg("Evictions", "evict: bottom entry\n");

            // use evict to correctly handle the case when we evict
            // the default route.
            evictNeighbor(&neigh_table[N_NEIGH - 1]);
            neigh_slot = &(neigh_table[N_NEIGH - 1]);
          }
        }
      }
    } else {
      if (cost == 0xffff) {
        dbg("Evictions", "evict with cost 0xffff\n");
        evictNeighbor(neigh_slot);
        return;
      }
      // Do this to prevent double counting because of reportReception
      neigh_slot->stats[SHORT_EPOCH].receptions--; 
    }
      
    if (neigh_slot != NULL) {
      SET_NEIGH_VALID(neigh_slot);
      neigh_slot->neighbor = neigh;
      neigh_slot->hops = hops;
      neigh_slot->costEstimate = cost;
      neigh_slot->linkEstimate = adjustLQI(lqi);
      neigh_slot->stats[SHORT_EPOCH].receptions++;
      dbg("IPRouting", "currentEpoch: 0x%x, Receptions in epoch: 0x%x, Total Receptions: 0x%x\n", 
                 SHORT_EPOCH, neigh_slot->stats[SHORT_EPOCH].receptions, getReceptions(neigh_slot));
    }
    printTable();
  }

  /*
   * Reports packet reception
   *
   * Updates the link estimate, as well as the number of receptions
   */
  command void IPRouting.reportReception(hw_addr_t neigh, uint8_t lqi) {
    struct neigh_entry *e = getNeighEntry(neigh);
    dbg("IPRouting", "Packet received from 0x%x lqi: %u\n", neigh, lqi);
    //if (e == NULL) e = addNeighEntry(neigh);
    if (e != NULL) {
      e->linkEstimate = adjustLQI(lqi);
      // e->stats[current_epoch].receptions++;
      //if (e == &(neigh_table[0]))
        //resetNeighLow();
      //else if (getMetric(e) < getMetric(&(neigh_table[0]))) {
        //sortFlowTable();
        //resetNeighLow();
      //}
    }
  }

  // Updates success (and failure) statistics
  // Also needs to reroute if the number of failures hits the threshold 
  command void IPRouting.reportTransmission(send_policy_t *policy) {
    int i;
    struct neigh_entry *e = NULL;
    
    // If not a broadcast address:
    //  1. If none of the provided addresses worked, then we should send out a solicitation
    //  2. All the failed nodes should have their totals increased by the max number of retries
    //  3. The successful node should update both its total and success by one
    //  4. If the successful node meets one of the following two conditions, it should be moved up one spot:
    //   a) It has a lower path cost and higher confidence than the above entry
    //   b) It has a similar path cost and confidence above a threshold (CONF_PROM_THRESHOLD)
    //  5. If we have had too many consecutive losses (MAX_CONSEC_FAILURES) toggle ReRouting
    if (policy->dest[0] != HW_BROADCAST_ADDR) {
      // stats.messages++;
      dbg("IPRouting", "reportTransmission: current: 0x%x, nchoices: 0x%x, retries: 0x%x\n", 
                 policy->current, policy->nchoices, policy->actRetries); 

      // update the failed neighbor statistics
      for (i = 0; i < policy->current; i++) {
        e = getNeighEntry(policy->dest[i]);
        if (e != NULL) {
          // SDH : presumably retries == actRetries
          e->stats[SHORT_EPOCH].total += policy->retries;
          
          if (e == default_route) {
            default_route_failures++;
          }

          dbg("IPRouting", "reportTransmissions: 0x%x failed\n", e->neighbor);

          // stats.transmissions += policy->retries;
        }
      }

      if (default_route_failures > MAX_CONSEC_FAILURES) {
        dbg("IPRouting", "Too many consecutive failures!\n");
        chooseNewRandomDefault(TRUE);
      }
      
      // if we succeeded sending the packet, increment the success on that one.
      e = getNeighEntry(policy->dest[policy->current]);
      if ((policy->current < policy->nchoices) && e != NULL) {
        e->stats[SHORT_EPOCH].success += 1;
        e->stats[SHORT_EPOCH].total += policy->actRetries;

        dbg("IPRouting", "Success: 0x%x, Total: 0x%x, ETX: 0x%x (addr 0x%x)\n", 
                   getSuccess(e), getConfidence(e), getLinkCost(e), e->neighbor);
        dbg("IPRouting", "Actual attempts was 0x%x\n", policy->actRetries);

        if (e == default_route)
          default_route_failures++;


        if ((e != &(neigh_table[0])) && 
            // we have higher confidence and lower cost
            (((getConfidence(e) > CONF_PROM_THRESHOLD) && // getConfidence(e - 1)) && 
              (checkThresh(getMetric(e), getMetric(e-1), PATH_COST_DIFF_THRESH) == BELOW_THRESH)) || 
             // we have similar cost and sufficient confidenceIP
             ((checkThresh(getMetric(e), getMetric(e-1), PATH_COST_DIFF_THRESH) == WITHIN_THRESH) && 
              (getConfidence(e) > CONF_PROM_THRESHOLD)))) {

          dbg("IPRouting", "Promoting node 0x%x over node 0x%x\n", e->neighbor, (e-1)->neighbor);
          swapNodes((e - 1), e);
        }

        // stats.successes += 1;
        // stats.transmissions += policy->actRetries;
      } else {
        dbg("IPRouting", "FAILURE!!!!!\n");
      }
    }
  }

  /*
   * @returns TRUE if the routing engine has established a default route.
   */
  command bool IPRouting.hasRoute() {    
    return (IS_NEIGH_VALID(&(neigh_table[0])));
  }

  void insertSourceHeader(struct split_ip_msg *msg, struct flow_entry *entry) {
    // these actually need to be static
    static uint8_t source_buf[sizeof(struct source_header) + MAX_PATH_LENGTH * sizeof(uint16_t)];
    static struct generic_header g_sh;
    struct source_header *sh = (struct source_header *)source_buf;
    uint8_t i;

    sh->len = sizeof(struct source_header) + entry->entries[0].pathE->path_len * sizeof(uint16_t);
    sh->current = 0;
    sh->dispatch = IP_EXT_SOURCE_DISPATCH;
    sh->nxt_hdr = msg->hdr.nxt_hdr;
    msg->hdr.nxt_hdr = NXTHDR_SOURCE;
    g_sh.hdr.ext = (struct ip6_ext *)sh;
    g_sh.len = sh->len;
    g_sh.next = msg->headers;
    msg->headers = &g_sh;

    dbg("Install", "Inserted source header with length 0x%x and next hop: 0x%x and dispatch 0x%x\n", 
        entry->entries[0].pathE->path_len, entry->entries[0].pathE->path[0], sh->dispatch);

    for (i = 0; i < entry->entries[0].pathE->path_len; i++) {
      sh->hops[i] = ntohs(entry->entries[0].pathE->path[i]);
    }
  }

  uint8_t convertTo8(uint16_t target) {
    if (target > 0xFF)
      return 0xFF;
    return target;
  }

#ifdef DBG_TRACK_FLOWS
  command void IPRouting.clearFlows() {
    int i, j;
    for (i = 0; i < N_FLOW_ENT; i++) {
      SET_INVALID_SLOT((&(flow_table[i])));
      flow_table[i].count = N_FLOW_ENT;
      for (j = 0; j < N_FLOW_CHOICES; j++) {
        SET_INVALID_ENTRY(flow_table[i].entries[j]);
      }
    }

    for (i = 0; i < N_FULL_PATH_ENTRIES; i++) {
      full_path_entries[i].path_len = 0;
    }
  }
#endif

  /*
   * Inserts all necessary routing headers for the packet
   * 
   * If packet is going to the root, inserts a topology information
   *  collection header
   *
   * AT: Should move source-routing header insertion to another function
   *  to avoid unnecessary allocation of buffer space
   */
  command void IPRouting.insertRoutingHeaders(struct split_ip_msg *msg) {
    // these actually need to be static
    static uint8_t sh_buf[sizeof(struct topology_header) + 
                          (sizeof(struct topology_entry) * N_NEIGH)];
    static struct generic_header record_route;
    struct topology_header *th = (struct topology_header *)sh_buf;
    int i, j = 0;

    // AT: We theoretically only want to attach this topology header if we're
    //  sending this message to a controller.  Isn't it easier to just check
    //  to see if the dest address matches that of the sink?
    // SDH: how do you know what the address of the sink is?  
    if (msg->hdr.nxt_hdr == IANA_UDP || msg->hdr.nxt_hdr == NXTHDR_UNKNOWN) {
      traffic_sent = TRUE;

      th->len = sizeof(struct topology_header);
      // only add topology information directly behind actual payload
      // headers.
      // SDH : TODO : check that this will not fragment the packet...
      // AT: Why do we care about the number of hops? Debugging purposes?
      th->nxt_hdr = msg->hdr.nxt_hdr;

      // For all these 16-bit values, we're only using 8 bit values
      for (i = 0; i < N_NEIGH; i++) {
        if (IS_NEIGH_VALID(&neigh_table[i]) && j < 4 && 
            (IS_MATURE(&neigh_table[i]) || default_route == &neigh_table[i])) {
          th->topo[j].etx = convertTo8(getLinkCost(&neigh_table[i]));
          th->topo[j].conf = convertTo8(getConfidence(&neigh_table[i]));
          th->topo[j].hwaddr = htons(neigh_table[i].neighbor);
          j++;
          th->len += sizeof(struct topology_entry);
          dbg("Lqi", "link est: 0x%x hops: 0x%x\n", 
              neigh_table[i].linkEstimate, neigh_table[i].hops);
        }
      }
       
      record_route.hdr.ext = (struct ip6_ext *)th;
      record_route.len = th->len;
      record_route.next = msg->headers;
      if (j > 0) {
        msg->hdr.nxt_hdr = NXTHDR_TOPO;
        msg->headers = &record_route;
      }
    }
    
  }

  /*
   * Sort timer will no longer be used only for sorting, but rather to expire an epoch and
   *  change entry statistics
   */
  event void SortTimer.fired() {
    dbg("IPRouting", "Epoch ended!\n");
    printTable();

    if (!call IPRouting.hasRoute() && !soliciting) {
      call ICMP.sendSolicitations();
      soliciting = TRUE;
    }

    if (checkThresh(call IPRouting.getQuality(), last_qual, 5) != WITHIN_THRESH ||
        last_hops != call IPRouting.getHopLimit()) {
      call ICMP.sendAdvertisements();
      last_qual = call IPRouting.getQuality();
      last_hops = call IPRouting.getHopLimit();
    }

    updateRankings();

    if (call Random.rand16() % 32 < 8) {
      dbg("IPRouting", "Attemting exploration\n");
      chooseNewRandomDefault(FALSE);
    } else {
      // default_route = &neigh_table[0];
      default_route_failures = 0;
    }
  }

  
  /*
   * This is called when the ICMP engine finishes sending out router solicitations.
   *
   * We will keep sending solicitations so long as we have not
   * established a default route.
   *
   */
  event void ICMP.solicitationDone() {
    //int i;

    dbg("IPRouting", "done soliciting\n");

    soliciting = FALSE;

    if (!call IPRouting.hasRoute()) {
      call ICMP.sendSolicitations();
      soliciting = TRUE;
    }
  }

  command void Statistics.get(route_statistics_t *statistics) {
    //struct neigh_entry *p = getNeighEntry((getFlowEntry_Header(NULL))->entries[0].nextHop);
    // struct neigh_entry *p = &(neigh_table[0]);
    // stats.hop_limit = call IPRouting.getHopLimit();
//    if (p != NULL) {
//      ip_memcpy(&stats.parent, p, sizeof(struct neigh_entry));
      // stats.parentmetric = getMetric(p);
//    }
    statistics->hop_limit = call IPRouting.getHopLimit();
    statistics->parent = (uint16_t) default_route->neighbor; 
    statistics->parent_metric = call IPRouting.getQuality(); 
    statistics->parent_etx = getMetric(default_route);
  }

  command void Statistics.clear() {
    // ip_memclr((uint8_t *)&stats, sizeof(route_statistics_t));
  }

  void evictNeighbor(struct neigh_entry *neigh) {
    struct neigh_entry *iterator;
    bool reset_default = FALSE;

    dbg("IPRouting", "Evicting neighbor 0x%x\n", neigh->neighbor);
    dbg("Evictions", "evict: 0x%x\n", neigh->neighbor);

    SET_NEIGH_INVALID(neigh);

    if (neigh == default_route) {
      reset_default = TRUE;
    }

    ip_memclr((uint8_t *)(neigh), sizeof(struct neigh_entry));
    for (iterator = neigh; iterator < &(neigh_table[N_NEIGH - 1]); iterator++) {
      if (!IS_NEIGH_VALID(iterator + 1)) break;
      swapNodes(iterator, iterator + 1);
    }

    if (reset_default) {
      // send new topology updates quickly to let an edge router know
      // that something happened.
      restartTrafficGen();
      default_route = &neigh_table[0];
      default_route_failures = 0;
    }

    printTable();
  }

  // Typically called after an epoch change
  void updateRankings() {
    uint8_t i;
    bool evicted = FALSE;

    for (i = 0; i < N_NEIGH; i++) {
      UNSET_EVICT(neigh_table[i]);
      if (!IS_NEIGH_VALID(&neigh_table[i])) continue;
      neigh_table[i].stats[LONG_EPOCH].total += neigh_table[i].stats[SHORT_EPOCH].total;
      neigh_table[i].stats[LONG_EPOCH].receptions += neigh_table[i].stats[SHORT_EPOCH].receptions;
      neigh_table[i].stats[LONG_EPOCH].success += neigh_table[i].stats[SHORT_EPOCH].success;
      
      if (neigh_table[i].stats[LONG_EPOCH].total & (0xf000)) {
        // if we're this big, the etx computation might overflow.
        // Make it smaller by dividing top and bottom by 2.
        neigh_table[i].stats[LONG_EPOCH].total >>= 1;
        neigh_table[i].stats[LONG_EPOCH].success >>= 1;
      }
      
      if (neigh_table[i].stats[LONG_EPOCH].total > CONF_EVICT_THRESHOLD) 
        SET_MATURE(&neigh_table[i]);

      if (IS_MATURE(&(neigh_table[i]))) {
        uint16_t cost;
        // if we didn't try the link, don't evict it            
        if (neigh_table[i].stats[SHORT_EPOCH].total == 0) goto done_iter;
        if (neigh_table[i].stats[SHORT_EPOCH].success == 0) {
          cost = 0xff;
        } else {
          cost = (10 * neigh_table[i].stats[SHORT_EPOCH].total) / 
            neigh_table[i].stats[SHORT_EPOCH].success;
        }
        if (cost > LINK_EVICT_THRESH) {
          dbg("Evictions", "cost: 0x%x, slot %i\n", cost, i);
          SET_EVICT(neigh_table[i]);
        }
      }
    done_iter:
      neigh_table[i].stats[SHORT_EPOCH].total = 0;
      neigh_table[i].stats[SHORT_EPOCH].receptions = 0;
      neigh_table[i].stats[SHORT_EPOCH].success = 0;
    }
    for (i = 0; i < N_NEIGH; i++) {
      if (IS_NEIGH_VALID(&neigh_table[i]) &&
          SHOULD_EVICT(neigh_table[i])) {
#if 0
        // SDH : because of the overflow bug, this was never being
        // triggered.  I'm not sure it's actually a good idea because
        // it seems to increase path lengths for heavily used routes.
        // Let's disable it for now.
        dbg("Evictions", "performing evict: %i\n", i);
        evictNeighbor(&neigh_table[i]);
        i --;
#endif
        evicted = TRUE;
      }
    }
    if (evicted)
      call ICMP.sendSolicitations();
  }

  void swapNodes(struct neigh_entry *highNode, struct neigh_entry *lowNode) {
    struct neigh_entry tempNode;
    if (highNode == NULL || lowNode == NULL) return;
    ip_memcpy(&tempNode, highNode, sizeof(struct neigh_entry));
    ip_memcpy(highNode, lowNode, sizeof(struct neigh_entry));
    ip_memcpy(lowNode, &tempNode, sizeof(struct neigh_entry));

    if (highNode == default_route) default_route = lowNode;
    else if (lowNode == default_route) default_route = highNode;
  }

  uint8_t checkThresh(uint32_t firstVal, uint32_t secondVal, uint16_t thresh) {
    if (((firstVal > secondVal) && ((firstVal - secondVal) <= thresh)) || 
        ((secondVal >= firstVal) && (secondVal - firstVal) <= thresh)) return WITHIN_THRESH;
    if (((firstVal > secondVal) && ((firstVal - secondVal) > thresh))) return ABOVE_THRESH;
    return BELOW_THRESH;
  }
}

--- NEW FILE: PrintfUART.h ---
/*
 * "Copyright (c) 2008 The Regents of the University  of California.
 * All rights reserved."
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 *
 */
/*                          
 * Copyright (c) 2005
 *	The President and Fellows of Harvard College.
 *
 * 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 University 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 UNIVERSITY 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 UNIVERSITY 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.
 */

/* 
 * Writes printf like output to the UART.  
 * This works only on the AVR and MSP430 Microcontrollers!
 * <p>
 * Note: For AVR we explicitly place the print statements in ROM; for
 * MSP430 this is done by default!  For AVR, if we don't place it
 * explicitely in ROM, the statements will go in RAM, which will
 * quickly cause a descent size program to run out of RAM.  By default
 * it doesn't disable the interupts; disabling the interupts when
 * writing to the UART, slows down/makes the mote quite unresponsive,
 * and can lead to problems!  If you wish to disable all printfs to
 * the UART, then comment the flag: <code>PRINTFUART_ENABLED</code>.

 * <p> <pre>
 * How to use:
 *   // (0) In your Makefile, define PRINTFUART_ENABLED
 *   CFLAGS += -DPRINTFUART_ENABLED
 *   // (1) Call printfUART_init() from your initialization function 
 *   //     to initialize the UART
 *   printfUART_init();
 *   // (2) Set your UART client to the correct baud rate.  Look at 
 *   //     the comments in printfUART_init(), to figure out what 
 *   //     baud to use for your particular mote
 *
 *   // (3) Send printf statements like this:
 *   printfUART("Hello World, we are in year= %u\n", 2004);
 *   printfUART("Printing uint32_t variable, value= %lu\n", 4294967295);
 *
 * Examples and caveats:
 *   // (1) - Must use curly braces in single section statements.  
 *            (Look in the app.c to see why -- hint: it's a macro)
 *   if (x < 3)
 *       {printfUART("The value of x is %i\n", x);}
 *   // (2) - Otherwise it more or less works like regular printf
 *   printfUART("\nThe value of x=%u, and y=%u\n", x, y); 
 * </pre>
 * <pre>URL: http://www.eecs.harvard.edu/~konrad/projects/motetrack</pre>
 * @author Konrad Lorincz
 * @version 2.0, January 5, 2005
 */
#ifndef PRINTFUART_H
#define PRINTFUART_H
#include <stdarg.h>
#include <stdio.h>

// -------------------------------------------------------------------
#ifdef PRINTFUART_ENABLED
    #define DEBUGBUF_SIZE 256
    char debugbuf[DEBUGBUF_SIZE];
    char debugbufROMtoRAM[DEBUGBUF_SIZE];

    #if defined(PLATFORM_MICAZ) || defined(PLATFORM_MICA2) || defined(PLATFORM_MICA2DOT)
        #define printfUART(__format...) {                          \
            static const char strROM[] PROGMEM = __format;         \
            strcpy_P((char*) &debugbufROMtoRAM, (PGM_P) &strROM);  \
            sprintf(debugbuf, debugbufROMtoRAM);                   \
            writedebug();                                          \
        }   
    #else  // assume MSP430 architecture (e.g. TelosA, TelosB, etc.)
        #define printfUART(__format...) {      \
            sprintf(debugbuf, __format);       \
            writedebug();                      \
        }  
    #endif
#else
    #define printfUART(X, args...) dbg("printf", X, ## args)
// #define printfUART(__format...) {}
    void printfUART_init() {}
#endif

#define NOprintfUART(__format...)


// -------------------------------------------------------------------
#ifdef PRINTFUART_ENABLED

/**
 * Initialize the UART port.  Call this from your startup routine.
 */
#define printfUART_init() {atomic printfUART_init_private();}
void printfUART_init_private()
{
    #if defined(PLATFORM_MICAZ) || defined(PLATFORM_MICA2)
        // 56K baud
        outp(0,UBRR0H);
        outp(15, UBRR0L);                              //set baud rate
        outp((1<<U2X),UCSR0A);                         // Set UART double speed
        outp(((1 << UCSZ1) | (1 << UCSZ0)) , UCSR0C);  // Set frame format: 8 data-bits, 1 stop-bit
        inp(UDR0);
        outp((1 << TXEN) ,UCSR0B);   // Enable uart reciever and transmitter

    #else
    #if defined(PLATFORM_MICA2DOT)  
        // 19.2K baud
        outp(0,UBRR0H);            // Set baudrate to 19.2 KBps
        outp(12, UBRR0L);
        outp(0,UCSR0A);            // Disable U2X and MPCM
        outp(((1 << UCSZ1) | (1 << UCSZ0)) , UCSR0C);
        inp(UDR0);
        outp((1 << TXEN) ,UCSR0B);
  
    #else
    #if defined(PLATFORM_IMOTE2)
      //async command result_t UART.init() {
        
        /*** 
           need to configure the ST UART pins for the correct functionality
           
           GPIO<46> = STDRXD = ALT2(in)
           GPIO<47> = STDTXD = ALT1(out)
        *********/
        //atomic{
          
        //configure the GPIO Alt functions and directions
        _GPIO_setaltfn(46,2);   // STD_RXD
        _GPIO_setaltfn(47,1);   // STD_TXD
        
        _GPDR(46) &= ~_GPIO_bit(46);  // input
        _GPDR(47) |= _GPIO_bit(47);   // output
        
        STLCR |=LCR_DLAB; //turn on DLAB so we can change the divisor
        STDLL = 8;  //configure to 115200;
        STDLH = 0;
        STLCR &= ~(LCR_DLAB);  //turn off DLAB
        
        STLCR |= 0x3; //configure to 8 bits
        
        STMCR &= ~MCR_LOOP;
        STMCR |= MCR_OUT2;
        STIER |= IER_RAVIE;
        STIER |= IER_TIE;
        STIER |= IER_UUE; //enable the UART
        
        //STMCR |= MCR_AFE; //Auto flow control enabled;
        //STMCR |= MCR_RTS;
        
        STFCR |= FCR_TRFIFOE; //enable the fifos
        
//        call Interrupt.allocate();
//        call Interrupt.enable();
        //configure all the interrupt stuff
        //make sure that the interrupt causes an IRQ not an FIQ
        // __REG(0x40D00008) &= ~(1<<21);
        //configure the priority as IPR1
        //__REG(0x40D00020) = (1<<31 | 21);
        //unmask the interrupt
        //__REG(0x40D00004) |= (1<<21);
        
        CKEN |= CKEN5_STUART; //enable the UART's clk    


    #else  // assume TelosA, TelosB, etc.
        // Variabel baud 
        // To change the baud rate, see /tos/platform/msp430/msp430baudrates.h
        uint8_t source = SSEL_SMCLK;
        uint16_t baudrate = 0x0012; // UBR_SMCLK_57600=0x0012
        uint8_t mctl = 0x84;        // UMCTL_SMCLK_57600=0x84
        //uint16_t baudrate = 0x0009; // UBR_SMCLK_115200=0x0009
        //uint8_t mctl = 0x10;        // UMCTL_SMCLK_115200=0x10


        uint16_t l_br = 0;
        uint8_t l_mctl = 0;
        uint8_t l_ssel = 0;

        TOSH_SEL_UTXD1_MODFUNC();
        TOSH_SEL_URXD1_MODFUNC();


        UCTL1 = SWRST;  
        UCTL1 |= CHAR;  // 8-bit char, UART-mode
    
        U1RCTL &= ~URXEIE;  // even erroneous characters trigger interrupts

        UCTL1 = SWRST;
        UCTL1 |= CHAR;  // 8-bit char, UART-mode

        if (l_ssel & 0x80) {
            U1TCTL &= ~(SSEL_0 | SSEL_1 | SSEL_2 | SSEL_3);
            U1TCTL |= (l_ssel & 0x7F); 
        }
        else {
            U1TCTL &= ~(SSEL_0 | SSEL_1 | SSEL_2 | SSEL_3);
            U1TCTL |= SSEL_ACLK; // use ACLK, assuming 32khz
        }

        if ((l_mctl != 0) || (l_br != 0)) {
            U1BR0 = l_br & 0x0FF;
            U1BR1 = (l_br >> 8) & 0x0FF;
            U1MCTL = l_mctl;
        }
        else {
            U1BR0 = 0x03;   // 9600 baud
            U1BR1 = 0x00;
            U1MCTL = 0x4A;
        }
      
        ME2 &= ~USPIE1;   // USART1 SPI module disable
        ME2 |= (UTXE1 | URXE1);   // USART1 UART module enable
      
        U1CTL &= ~SWRST;
    
        IFG2 &= ~(UTXIFG1 | URXIFG1);
        IE2 &= ~(UTXIE1 | URXIE1);  // interrupt disabled

   

        //async command void USARTControl.setClockSource(uint8_t source) {
        //    atomic {
                l_ssel = source | 0x80;
                U1TCTL &= ~(SSEL_0 | SSEL_1 | SSEL_2 | SSEL_3);
                U1TCTL |= (l_ssel & 0x7F); 
                //    }
                //}
                //async command void USARTControl.setClockRate(uint16_t baudrate, uint8_t mctl) {
                //atomic {
                l_br = baudrate;
                l_mctl = mctl;
                U1BR0 = baudrate & 0x0FF;
                U1BR1 = (baudrate >> 8) & 0x0FF;
                U1MCTL = mctl;
                //}
                //}

                //async command result_t USARTControl.enableRxIntr(){
                //atomic {
                IFG2 &= ~URXIFG1;
                IE2 |= URXIE1;
                //}
                //return SUCCESS;
                //}

                //async command result_t USARTControl.enableTxIntr(){
                //atomic {
                IFG2 &= ~UTXIFG1;
                IE2 |= UTXIE1;
                //}
                //return SUCCESS;
                //}     

    #endif
    #endif
    #endif
}

#if defined(PLATFORM_MICAZ) || defined(PLATFORM_MICA2) || defined(PLATFORM_MICA2DOT)
#else
#if defined(PLATFORM_IMOTE2)
#else // assume AVR architecture (e.g. TelosA, TelosB)
    bool isTxIntrPending()
    {
        if (U1TCTL & TXEPT) {
            return TRUE;
        }
        return FALSE;
    }
#endif
#endif

/**
 * Outputs a char to the UART.
 */
void UARTPutChar(char c)
{
    if (c == '\n')
        UARTPutChar('\r');


    #if defined(PLATFORM_MICAZ) || defined(PLATFORM_MICA2) || defined(PLATFORM_MICA2DOT)
        loop_until_bit_is_set(UCSR0A, UDRE);
        outb(UDR0,c);

    #else
    #if defined(PLATFORM_IMOTE2)
        STTHR = c;    

    #else // assume AVR architecture (e.g. TelosA, TelosB)
        U1TXBUF = c;  
        while( !isTxIntrPending() )  
            continue;
    #endif
    #endif
}

/**
 * Outputs the entire debugbuf to the UART, or until it encounters '\0'.
 */
void writedebug()
{
    uint16_t i = 0;
    
    while (debugbuf[i] != '\0' && i < DEBUGBUF_SIZE)
        UARTPutChar(debugbuf[i++]);
}

#endif  // PRINTFUART_ENABLED
// -------------------------------------------------------------------

#if 0
// --------------------------------------------------------------
#define assertUART(x) if (!(x)) { __assertUART(__FILE__, __LINE__); }
void __assertUART(const char* file, int line)
{
    printfUART("ASSERT FAILED: file= %s, lineNbr= %i\n", file, line);
    // for some reason, CLR means on
    TOSH_MAKE_RED_LED_OUTPUT();
    TOSH_MAKE_YELLOW_LED_OUTPUT();
    TOSH_MAKE_GREEN_LED_OUTPUT();
    TOSH_CLR_RED_LED_PIN();
    TOSH_CLR_YELLOW_LED_PIN();
    TOSH_CLR_GREEN_LED_PIN();
    exit(1);
}
// --------------------------------------------------------------
#endif

#endif  // PRINTFUART_H


--- NEW FILE: TcpC.nc ---

configuration TcpC {
  provides interface Tcp[uint8_t client];
} implementation {

  components MainC, IPDispatchC, TcpP, IPAddressC;
  components new TimerMilliC();

  Tcp = TcpP;

  MainC -> TcpP.Init;
  TcpP.Boot -> MainC;
  TcpP.IP -> IPDispatchC.IP[IANA_TCP];

  TcpP.Timer -> TimerMilliC;
  TcpP.IPAddress -> IPAddressC;
}

--- NEW FILE: TcpP.nc ---

#include <table.h>
#include <ip.h>

module TcpP {
  provides interface Tcp[uint8_t client];
  provides interface Init;
  uses {
    interface Boot;

    interface IP;    
    interface Timer<TMilli>;
    interface IPAddress;
  }
} implementation {

  enum {
    N_CLIENTS = uniqueCount("TCP_CLIENT"),
  };

#include <tcplib.h>
  struct tcplib_sock socks[N_CLIENTS];

  int find_client(struct tcplib_sock *conn) {
    int i;
    for (i = 0; i < N_CLIENTS; i++)
      if (&socks[i] == conn) break;

    return i;
  }

  void conn_d(struct tcplib_sock *sock, int error) {
    int cid = find_client(sock);
    if (cid < N_CLIENTS)
      signal Tcp.connectDone[cid](error == 0);
  }

  void rx(struct tcplib_sock *sock, void *data, int len) {
    int cid = find_client(sock);
    if (cid < N_CLIENTS)
      signal Tcp.recv[cid](data, len);
  }

  void cl(struct tcplib_sock *sock) {
    tcplib_close(sock);
  }

  void cd(struct tcplib_sock *sock) {
    int cid = find_client(sock);
    tcplib_init_sock(sock);
    if (cid < N_CLIENTS)
      signal Tcp.closed[cid](0);
  }

  void init_ops(struct tcplib_sock *sock) {
    sock->ops.connect_done = conn_d;
    sock->ops.recvfrom = rx;
    sock->ops.closed = cl;
    sock->ops.close_done = cd;
  }



  void setSrcAddr(struct split_ip_msg *msg) {
    if (msg->hdr.ip6_dst.s6_addr16[0] == htons(0xff02) ||
        msg->hdr.ip6_dst.s6_addr16[0] == htons(0xfe80)) {
//         (msg->hdr.dst_addr[0] == 0xff && (msg->hdr.dst_addr[1] & 0xf) == 0x2) ||
//         (msg->hdr.dst_addr[0] == 0xfe && msg->hdr.dst_addr[2] == 0x80)) {
      call IPAddress.getLLAddr(&msg->hdr.ip6_src);
    } else {
      call IPAddress.getIPAddr(&msg->hdr.ip6_src);
    }
  }

  struct tcplib_sock socks[uniqueCount("TCP_CLIENT")];

  struct tcplib_sock *tcplib_accept(struct tcplib_sock *conn,
                                    struct sockaddr_in6 *from) {
    void *rx_buf = NULL, *tx_buf = NULL;
    int rx_buf_len, tx_buf_len;
    int cid = find_client(conn);

    printfUART("tcplib_accept: cid: %i\n", cid);

    if (cid == N_CLIENTS) return NULL;
    if (signal Tcp.accept[cid](from, &rx_buf, &rx_buf_len,
                               &tx_buf, &tx_buf_len)) {
      if (rx_buf == NULL || tx_buf == NULL) return NULL;
      conn->rx_buf = rx_buf;
      conn->rx_buf_len = rx_buf_len;
      conn->tx_buf = tx_buf;
      conn->tx_buf_len = tx_buf_len;
      init_ops(conn);
      return conn;
    }
    return NULL;
  }

  void tcplib_send_out(struct split_ip_msg *msg, struct tcp_hdr *tcph) {
    printfUART("tcp output\n");
    setSrcAddr(msg);
    tcph->chksum = htons(msg_cksum(msg, IANA_TCP));
    call IP.send(msg);
  }

#include "circ.c"
#include "tcplib.c"

  command error_t Init.init() {
    int i;
    for (i = 0; i < uniqueCount("TCP_CLIENT"); i++) {
      tcplib_init_sock(&socks[i]);
    }
    return SUCCESS;
  }

  event void Boot.booted() {
    call Timer.startPeriodic(512);
  }

  event void Timer.fired() {
    tcplib_timer_process();
  }

  event void IP.recv(struct ip6_hdr *iph, 
                     void *payload, 
                     struct ip_metadata *meta) {
    
    printfUART("tcp packet received\n");
    tcplib_process(iph, payload);
  }


  command error_t Tcp.bind[uint8_t client](uint16_t port) {
    struct sockaddr_in6 addr;
    ip_memclr(addr.sin6_addr.s6_addr, 16);
    addr.sin6_port = htons(port);
    tcplib_bind(&socks[client], &addr);
    return SUCCESS;
  }

  command error_t Tcp.connect[uint8_t client](struct sockaddr_in6 *dest,
                                              void *rx_buf, int rx_buf_len,
                                              void *tx_buf, int tx_buf_len) {
    socks[client].rx_buf;
    socks[client].rx_buf_len = rx_buf_len;
    socks[client].tx_buf = tx_buf;
    socks[client].tx_buf_len = tx_buf_len;
    tcplib_connect(&socks[client], dest);
  }

  command error_t Tcp.send[uint8_t client](void *payload, uint16_t len) {
    tcplib_send(&socks[client], payload, len);
    return SUCCESS;
  }
  
  command error_t Tcp.close[uint8_t client]() {
    if (!tcplib_close(&socks[client]))
      return SUCCESS;
    return FAIL;
  }

  default event bool Tcp.accept[uint8_t cid](struct sockaddr_in6 *from, 
                                             void **rx_buf, int *rx_buf_len,
                                             void **tx_buf, int *tx_buf_len) {
    return FALSE;
  }

 default event void Tcp.connectDone[uint8_t cid](error_t e) {}
 default event void Tcp.recv[uint8_t cid](void *payload, uint16_t len) {  }
 default event void Tcp.closed[uint8_t cid](error_t e) { }
 



  
}

--- NEW FILE: TcpSocketC.nc ---


generic configuration TcpSocketC() {
  provides interface Tcp;
} implementation {

  components TcpC;

  Tcp = TcpC.Tcp[unique("TCP_CLIENT")];
  
}

--- NEW FILE: UdpC.nc ---

configuration UdpC {
  provides interface UDP[uint8_t clnt];                         
} implementation {

  components MainC, IPDispatchC, UdpP, IPAddressC;
  UDP = UdpP;

  MainC -> UdpP.Init;
  UdpP.IP -> IPDispatchC.IP[IANA_UDP];
  UdpP.IPAddress -> IPAddressC;
}

--- NEW FILE: UdpP.nc ---

#include <ip_malloc.h>
#include <in_cksum.h>

module UdpP {
  provides interface UDP[uint8_t clnt];
  provides interface Init;
  uses interface IP;
  uses interface IPAddress;
} implementation {

  enum {
    N_CLIENTS = uniqueCount("UDP_CLIENT"),
  };

  uint16_t local_ports[N_CLIENTS];

  enum {
    LOCAL_PORT_START = 51024U,
    LOCAL_PORT_STOP  = 54999U,
  };
  uint16_t last_localport = LOCAL_PORT_START;

  uint16_t alloc_lport(uint8_t clnt) {
    int i, done = 0;
    uint16_t compare = htons(last_localport);
    last_localport = (last_localport < LOCAL_PORT_START) ? last_localport + 1 : LOCAL_PORT_START;
    while (!done) {
      done = 1;
      for (i = 0; i < N_CLIENTS; i++) {
        if (local_ports[i] == compare) {
          last_localport = (last_localport < LOCAL_PORT_START) ? last_localport + 1 : LOCAL_PORT_START;
          compare = htons(last_localport);
          done = 0;
          break;
        }
      }
    }
    return last_localport;
  }

  command error_t Init.init() {
    ip_memclr((uint8_t *)local_ports, sizeof(uint16_t) * N_CLIENTS);
    return SUCCESS;
  }

  void setSrcAddr(struct split_ip_msg *msg) {
    if (msg->hdr.ip6_dst.s6_addr16[7] == htons(0xff02) ||
        msg->hdr.ip6_dst.s6_addr16[7] == htons(0xfe80)) {
      call IPAddress.getLLAddr(&msg->hdr.ip6_src);
    } else {
      call IPAddress.getIPAddr(&msg->hdr.ip6_src);
    }
  }


  command error_t UDP.bind[uint8_t clnt](uint16_t port) {
    int i;
    port = htons(port);
    for (i = 0; i < N_CLIENTS; i++)
      if (i != clnt && local_ports[i] == port)
        return FAIL;
    local_ports[clnt] = port;
    return SUCCESS;
  }


  event void IP.recv(struct ip6_hdr *iph,
                     void *payload,
                     struct ip_metadata *meta) {
    int i;
    struct sockaddr_in6 addr;
    struct udp_hdr *udph = (struct udp_hdr *)payload;

    dbg("UDP", "UDP - IP.recv: len: %i srcport: %i dstport: %i\n",
        ntohs(iph->plen), ntohs(udph->srcport), ntohs(udph->dstport));

    for (i = 0; i < N_CLIENTS; i++)
      if (local_ports[i] == udph->dstport)
        break;

    if (i == N_CLIENTS) {
      // TODO : send ICMP port closed message here.
      return;
    }
    ip_memcpy(&addr.sin6_addr, &iph->ip6_src, 16);
    addr.sin6_port = udph->srcport;

    signal UDP.recvfrom[i](&addr, (void *)(udph + 1), ntohs(iph->plen) - sizeof(struct udp_hdr), meta);
  }

  /*
   * Injection point of IP datagrams.  This is only called for packets
   * being sent from this mote; packets which are being forwarded
   * never lave the stack and so never use this entry point.
   *
   * @msg an IP datagram with header fields (except for length)
   * @plen the length of the data payload added after the headers.
   */
  command error_t UDP.sendto[uint8_t clnt](struct sockaddr_in6 *dest, void *payload, 
                                           uint16_t len) {
    struct split_ip_msg *msg;
    struct udp_hdr *udp;
    struct generic_header *g_udp;
    error_t rc;

    // todo check + alloc local port

    msg = (struct split_ip_msg *)ip_malloc(sizeof(struct split_ip_msg) +
                                        sizeof(struct udp_hdr) +
                                        sizeof(struct generic_header));

    if (msg == NULL) {
      dbg("Drops", "drops: UDP send: malloc failure\n");
      return ERETRY;
    }
    udp = (struct udp_hdr *)(msg + 1);
    g_udp = (struct generic_header *)(udp + 1);

    // fill in all the packet fields
    ip_memclr((uint8_t *)&msg->hdr, sizeof(struct ip6_hdr));
    ip_memclr((uint8_t *)udp, sizeof(struct udp_hdr));
    
    setSrcAddr(msg);
    memcpy(&msg->hdr.ip6_dst, dest->sin6_addr.s6_addr, 16);
    
    if (local_ports[clnt] == 0 && alloc_lport(clnt) == 0) return FAIL;
    udp->srcport = local_ports[clnt];
    udp->dstport = dest->sin6_port;
    udp->len = htons(len + sizeof(struct udp_hdr));
    udp->chksum = 0;

    // set up the pointers
    g_udp->len = sizeof(struct udp_hdr);
    g_udp->hdr.udp = udp;
    g_udp->next = NULL;
    msg->headers = g_udp;
    msg->data_len = len;
    msg->data = payload;

    udp->chksum = htons(msg_cksum(msg, IANA_UDP)); 

    rc = call IP.send(msg);

    ip_free(msg);
    return rc;

  }

  default event void UDP.recvfrom[uint8_t clnt](struct sockaddr_in6 *from, void *payload,
                                               uint16_t len, struct ip_metadata *meta) {

 }
}

--- NEW FILE: UdpSocketC.nc ---

generic configuration UdpSocketC() {
  provides interface UDP;
} implementation {
  
  components UdpC;

  UDP = UdpC.UDP[unique("UDP_CLIENT")];
}

--- NEW FILE: table.c ---

#include <stdlib.h>
#include "table.h"

void table_init(table_t *table, void *data,
                uint16_t elt_len, uint16_t n_elts) {
  table->data = data;
  table->elt_len  = elt_len;
  table->n_elts   = n_elts;
}

void *table_search(table_t *table, int (*pred)(void *)) {
  int i;
  void *cur;
  for (i = 0; i < table->n_elts; i++) {
    cur = table->data + (i * table->elt_len);
    switch (pred(cur)) {
    case 1: return cur;
    case -1: return NULL;
    default: continue;
    }
  }
  return NULL;
}

void table_map(table_t *table, void(*fn)(void *)) {
  int i;
  for (i = 0; i < table->n_elts; i++)
    fn(table->data + (i * table->elt_len));
}


--- NEW FILE: table.h ---
#ifndef TABLE_H_
#define TABLE_H_

#include <stdint.h>

typedef struct {
  void *data;
  uint16_t elt_len;
  uint16_t n_elts;
} table_t;

void table_init(table_t *table, void *data,uint16_t elt_len, uint16_t n_elts);
void *table_search(table_t *table, int (*pred)(void *));
void table_map(table_t *table, void (*fn)(void *));

#endif



More information about the Tinyos-2-commits mailing list