--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+#include "routing.h"
+
+/**
+ * ForwardingEngineM - Handles received packets of a certain protocol
+ * in a multihop context. The component uses a route selector to
+ * determine if the packet should be forwarded or passed to the upper
+ * layer. If the packet is forwarded, the next hop is given by the
+ * route selector.
+ *
+ * @author Romain Thouvenin
+ */
+
+//TODO probably need a lot of cleaning, and to be moved elsewhere
+generic module ForwardingEngineM () {
+ provides { //For the upper layer
+ interface AMSend[uint8_t id];
+ interface Receive[uint8_t id];
+ interface Intercept[uint8_t id];
+ interface LinkMonitor;
+ }
+ uses {
+ interface RouteSelect;
+ interface AMSend as SubSend;
+ interface AMPacket;
+ interface Packet as PPacket;
+ interface Packet as SubPacket;
+ interface PacketAcknowledgements as Acks;
+ interface Receive as SubReceive;
+ interface Timer<TMilli>;
+ }
+
+ provides interface MHControl;
+}
+
+implementation {
+ message_t buf; //first available, do NOT use it
+ message_t * avail = &buf;
+ message_t * waiting;
+ uint8_t typebuf;
+ uint8_t lenWaiting;
+ uint8_t amWaiting;
+ am_addr_t bufAddr;
+ am_addr_t * addrWaiting;
+ bool lockAvail, lockWaiting;
+ uint32_t wait_time;
+ bool acks;
+
+ enum {
+ WAIT_BEFORE_RETRY = 100,
+ MAX_WAIT = 10 * WAIT_BEFORE_RETRY
+ };
+
+ command error_t AMSend.send[uint8_t am](am_addr_t addr, message_t * msg, uint8_t len){
+ switch(call RouteSelect.selectRoute(msg, &addr, &am)){
+ case FW_SEND:
+ call PPacket.setPayloadLength(msg, len);
+ acks = DYMO_LINK_FEEDBACK && (call Acks.requestAck(msg) == SUCCESS);
+ return call SubSend.send(call AMPacket.destination(msg), msg, call SubPacket.payloadLength(msg));
+
+ case FW_WAIT:
+ atomic {
+ if(lockWaiting)
+ return EBUSY;
+ lockWaiting = TRUE;
+ }
+ waiting = msg;
+ amWaiting = am;
+ call PPacket.setPayloadLength(msg, len);
+ lenWaiting = call SubPacket.payloadLength(msg);
+ bufAddr = addr;
+ addrWaiting = &bufAddr;
+ wait_time = 0;
+ call Timer.startOneShot(WAIT_BEFORE_RETRY);
+ dbg("fwe", "FE: I'll retry later.\n");
+ return SUCCESS;
+
+ default: //We don't allow sending to oneself
+ return FAIL;
+ }
+ }
+
+ event message_t * SubReceive.receive(message_t * msg, void * payload, uint8_t len){
+ dbg("fwe", "FE: Received a message from %u\n", call AMPacket.source(msg));
+ signal MHControl.msgReceived(msg);
+ switch(call RouteSelect.selectRoute(msg, NULL, &typebuf)){
+ case FW_SEND:
+ atomic {
+ if(lockAvail)
+ return msg;
+ lockAvail = TRUE;
+ }
+ if ( signal Intercept.forward[typebuf](msg, call PPacket.getPayload(msg, call PPacket.payloadLength(msg)), call PPacket.payloadLength(msg)) ) {
+ call SubSend.send(call AMPacket.destination(msg), msg, len);
+ }
+ return avail;
+
+ case FW_RECEIVE:
+ payload = call PPacket.getPayload(msg, call PPacket.payloadLength(msg));
+ return signal Receive.receive[typebuf](msg, payload, call PPacket.payloadLength(msg));
+
+ case FW_WAIT:
+ atomic {
+ if(lockAvail || lockWaiting)
+ return msg;
+ lockAvail = lockWaiting = TRUE;
+ }
+ waiting = msg;
+ lenWaiting = len;
+ addrWaiting = NULL;
+ wait_time = 0;
+ call Timer.startOneShot(WAIT_BEFORE_RETRY);
+ return avail;
+
+ default:
+ return msg;
+ }
+ }
+
+ event void SubSend.sendDone(message_t * msg, error_t e){
+ dbg("fwe", "FE: Sending done.\n");
+ if( (e == SUCCESS) && acks ){
+ if( !(call Acks.wasAcked(msg)) ){
+ e = FAIL;
+ dbg("fwe", "FE: The message was not acked => FAIL.\n");
+ signal MHControl.sendFailed(msg, 2);
+ signal LinkMonitor.brokenLink(call AMPacket.destination(msg));
+ }
+ } else if (e != SUCCESS) {
+ dbg("fwe", "FE: But failed!\n");
+ signal MHControl.sendFailed(msg, 1);
+ }
+
+ if(lockAvail){
+ avail = msg;
+ atomic {
+ lockAvail = FALSE;
+ }
+ } else {
+ signal AMSend.sendDone[amWaiting](msg, e);
+ atomic {
+ lockWaiting = FALSE;
+ }
+ }
+ }
+
+ event void Timer.fired(){
+ switch(call RouteSelect.selectRoute(waiting, addrWaiting, &amWaiting)){
+ case FW_SEND:
+ dbg("fwe", "FE: I'm retrying to send my message.\n");
+ if (addrWaiting) {
+ call SubSend.send(call AMPacket.destination(waiting), waiting, lenWaiting);
+ } else if ( signal Intercept.forward[amWaiting](waiting,
+ call PPacket.getPayload(waiting, call PPacket.payloadLength(waiting)),
+ call PPacket.payloadLength(waiting)) ) {
+ call SubSend.send(call AMPacket.destination(waiting), waiting, lenWaiting);
+ }
+ call Timer.stop();
+ break;
+
+ case FW_WAIT:
+ dbg("fwe", "FE: I'll retry later again.\n");
+ wait_time += call Timer.getdt();
+ if(wait_time < MAX_WAIT){
+ call Timer.startOneShot(wait_time);
+ break;
+ }
+ //else: Continue to default
+
+ default:
+ if(addrWaiting)
+ signal AMSend.sendDone[amWaiting](waiting, FAIL);
+ if(lockAvail){
+ avail = waiting;
+ atomic {
+ lockAvail = FALSE;
+ }
+ }
+ atomic {
+ lockWaiting = FALSE;
+ }
+ }
+ }
+
+ command error_t AMSend.cancel[uint8_t am](message_t *msg){
+ if(lockWaiting){
+ call Timer.stop();
+ atomic {
+ lockWaiting = FALSE;
+ }
+ return SUCCESS;
+ } else {
+ return call SubSend.cancel(msg);
+ }
+ }
+
+ command void * AMSend.getPayload[uint8_t am](message_t *msg, uint8_t len){
+ return call PPacket.getPayload(msg, len);
+ }
+
+ command uint8_t AMSend.maxPayloadLength[uint8_t am](){
+ return call PPacket.maxPayloadLength();
+ }
+
+
+ /*** defaults ***/
+
+ default event message_t * Receive.receive[uint8_t am](message_t * msg, void * payload, uint8_t len){
+ return msg;
+ }
+
+ default event void AMSend.sendDone[uint8_t am](message_t * msg, error_t e){}
+
+ default event bool Intercept.forward[uint8_t am](message_t * msg, void * payload, uint8_t len){
+ return TRUE;
+ }
+
+ default event void MHControl.msgReceived(message_t * msg){ }
+
+ default event void MHControl.sendFailed(message_t * msg, uint8_t why){ }
+
+ default event void LinkMonitor.brokenLink(addr_t neighbor){ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+#include "AM.h"
+
+/**
+ * LinkMonitor - Interface to signals broken links in the neighborhood.
+ *
+ * @author Romain Thouvenin
+ */
+
+interface LinkMonitor {
+
+ event void brokenLink(am_addr_t neighbor);
+
+}
--- /dev/null
+module LoopBackM {
+ provides {
+ interface AMSend[uint8_t id];
+ interface Receive[uint8_t id];
+ }
+ uses {
+ interface AMSend as SubSend[uint8_t id];
+ interface Receive as SubReceive[uint8_t id];
+ interface AMPacket;
+ interface Packet;
+ }
+}
+implementation {
+
+ message_t first_avail;
+ message_t * avail = &first_avail;
+
+ message_t * buf_msg;
+ uint8_t buf_am;
+
+ task void sendDoneTask(){
+ signal AMSend.sendDone[buf_am](buf_msg, SUCCESS);
+ }
+
+ command error_t AMSend.send[uint8_t am](am_addr_t addr, message_t *msg, uint8_t len){ //TODO set acks
+ if(addr == call AMPacket.address()){
+ buf_am = am;
+ buf_msg = msg;
+ *avail = *msg;
+ dbg("lo", "LO: I am sending the buffer %p.\n", avail);
+ dbg("lo", "LO: its length is %hhu.\n", len);
+ call Packet.setPayloadLength(avail, len);
+ call AMPacket.setDestination(avail, addr);
+ call AMPacket.setSource(avail, addr);
+ post sendDoneTask();
+ avail = signal Receive.receive[call AMPacket.type(msg)](
+ avail,
+ call Packet.getPayload(avail, len),
+ len);
+ return SUCCESS;
+ } else {
+ return call SubSend.send[am](addr, msg, len);
+ }
+ }
+
+ command error_t AMSend.cancel[uint8_t am](message_t *msg){
+ if(call AMPacket.destination(msg) == call AMPacket.address()){
+ return FAIL;
+ } else {
+ return call SubSend.cancel[am](msg);
+ }
+ }
+
+ command void * AMSend.getPayload[uint8_t am](message_t *msg, uint8_t len){
+ return call SubSend.getPayload[am](msg, len);
+ }
+
+ command uint8_t AMSend.maxPayloadLength[uint8_t am](){
+ return call SubSend.maxPayloadLength[am]();
+ }
+
+ event void SubSend.sendDone[uint8_t am](message_t *msg, error_t error){
+ signal AMSend.sendDone[am](msg, error);
+ }
+
+
+ event message_t * SubReceive.receive[uint8_t am](message_t *msg, void *payload, uint8_t len){
+ return signal Receive.receive[am](msg, payload, len);
+ }
+
+ default event void AMSend.sendDone[uint8_t am](message_t *msg, error_t error){ }
+
+ default event message_t * Receive.receive[uint8_t am](message_t *msg, void *payload, uint8_t len){ return msg; }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+#include "routing.h"
+
+/**
+ * Interface to a route selection in a multi-hop context.
+ *
+ * @author Romain Thouvenin
+ */
+
+interface RouteSelect {
+
+ /**
+ * Ask the routing engine to fill a message with routing
+ * information, in order to send it to its target.
+ *
+ * @param msg The message to be sent
+ * @param destination The target of the route. If NULL, it is assumed it can be read in the packet
+ * @return The action that should be taken by the forwarding engine.
+ */
+ command fw_action_t selectRoute(message_t * msg, addr_t * destination, uint8_t * am_type);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+#include "routing_table.h"
+
+/**
+ * RoutingTable - Interface to manipulate a data structure that stores
+ * a table of routes toward a number of destinations.
+ *
+ * @author Romain Thouvenin
+ */
+
+interface RoutingTable {
+
+ /**
+ * Request for a route toward a destination.
+ * @param Address of the destination node
+ * @param info A pointer where to store the routing information
+ * associated to the destination, ignored if NULL
+ * @return SUCCESS if the route exists<br/>
+ * EBUSY if the route does not exist but may be available soon
+ * FAIL if the route exists but is broken
+ */
+ command error_t getRoute(addr_t address, rt_info_t * info);
+
+ command error_t getForwardingRoute(addr_t address, rt_info_t * info);
+
+ /**
+ * Signal that a route has been removed from the table.
+ * @param route_info Routing information associated to the evicted entry
+ * @param r reason of the eviction
+ */
+ event void evicted(const rt_info_t * route_info, reason_t r);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+#include "routing_table.h"
+
+typedef nx_struct rt_link {
+ nx_addr_t target;
+ nx_addr_t nexthop;
+} rt_link_t;
+
+interface RoutingTableInfo {
+
+ /**
+ * Size of the table.
+ * @return the number of entries stored in the table
+ */
+ command uint8_t size();
+
+ command uint8_t maxSize();
+
+ command uint8_t getTableContent(rt_info_t * buf);
+
+ command uint8_t getLinks(rt_link_t * buf);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+#include "routing.h"
+#include "routing_table.h"
+
+/**
+ * DymoEngineM - Implements the algorithms to generate and process
+ * DYMO messages.
+ *
+ * @author Romain Thouvenin
+ */
+
+module DymoEngineM {
+ provides {
+ interface SplitControl;
+ }
+ uses {
+ interface DymoTable;
+ interface RoutingTable;
+ interface DymoPacket;
+ interface AMSend;
+ interface AMPacket;
+ interface Receive;
+
+ interface Mount;
+ interface ConfigStorage;
+ }
+
+#ifdef DYMO_MONITORING
+ provides interface DymoMonitor;
+ uses {
+ interface Timer<TMilli>;
+ }
+#endif
+}
+
+implementation {
+ message_t * avail_msg; //to be returned by receive
+ message_t buf_avail; //first avail_msg
+ message_t buf_packet;
+ rt_info_t me;
+ rt_info_t buf_info;
+ addr_t ignoreNeeded;
+ bool busySend;
+
+ /* for processing */
+ bool busyProcess, busyIssue;
+ uint8_t cur_hopcnt;
+ uint8_t cur_info_pos;
+ rt_info_t buf_target;
+ addr_t fw_address; //set to 0 if the message must not be forwarded
+ message_t fw_msg;
+ bool sendRREP;
+
+#ifdef DYMO_MONITORING
+ uint32_t rreq_time;
+#endif
+
+ command error_t SplitControl.start(){
+ me.address = call AMPacket.address();
+ me.has_hopcnt = 1;
+ me.hopcnt = 0;
+
+ avail_msg = &buf_avail;
+ ignoreNeeded = 0;
+ sendRREP = FALSE;
+ busyProcess = FALSE;
+ busyIssue = FALSE;
+ busySend = FALSE;
+ buf_target.address = 0;
+
+#ifdef DYMO_MONITORING
+ rreq_time = 0;
+#endif
+
+ return call Mount.mount();
+ }
+
+ void incr_seqnum(){
+ if(me.seqnum == 65535)
+ me.seqnum = 256;
+ else
+ me.seqnum++;
+
+ call ConfigStorage.write(0x0, &me.seqnum, sizeof(me.seqnum));
+ }
+
+ event void ConfigStorage.writeDone(storage_addr_t addr, void *buf,
+ storage_len_t len, error_t err) {
+ // Verify addr and len
+
+ if (err == SUCCESS) {
+ if (call ConfigStorage.commit() != SUCCESS) {
+ // Handle failure
+ }
+ }
+ else {
+ // Handle failure
+ }
+ }
+
+ event void Mount.mountDone(error_t error) {
+ if (error == SUCCESS) {
+ if (call ConfigStorage.valid() == TRUE) {
+ if (call ConfigStorage.read(0x0, &me.seqnum, sizeof(me.seqnum)) != SUCCESS) {
+ me.seqnum = 1;
+ signal SplitControl.startDone(SUCCESS);
+ }
+ }
+ else {
+ // Invalid volume. Commit to make valid.
+ if (call ConfigStorage.commit() == SUCCESS) {
+ me.seqnum = 1;
+ }
+ else {
+ signal SplitControl.startDone(FAIL);
+ }
+ }
+ }
+ else{
+ signal SplitControl.startDone(error);
+ }
+ }
+
+ event void ConfigStorage.commitDone(error_t err) {
+ if ((err != SUCCESS) && (me.seqnum == 1)) {
+ signal SplitControl.startDone(err);
+ } else if (me.seqnum == 1) {
+ signal SplitControl.startDone(SUCCESS);
+ }
+ }
+
+ event void ConfigStorage.readDone(storage_addr_t addr, void* buf,
+ storage_len_t len, error_t err) __attribute__((noinline)) {
+
+ if (err == SUCCESS) {
+ me.seqnum = *(seqnum_t *)buf;
+ signal SplitControl.startDone(SUCCESS);
+ } else {
+ signal SplitControl.startDone(err);
+ }
+ }
+
+
+ /* Send a RREQ for buf_info */
+ task void issueRREQ(){
+ atomic {
+ if(busySend)
+ post issueRREQ();
+ else {
+ busySend = TRUE;
+ incr_seqnum();
+ call DymoPacket.createRM(&buf_packet, DYMO_RREQ, &me, &buf_info);
+ call AMSend.send(AM_BROADCAST_ADDR, &buf_packet, call DymoPacket.getSize(&buf_packet));
+ }
+ }
+ }
+
+ /* Send a RREP to buf_info */
+ task void issueRREP(){
+ atomic {
+ if(busySend)
+ post issueRREP();
+ else {
+ busySend = TRUE;
+ call DymoPacket.createRM(&buf_packet, DYMO_RREP, &me, &buf_info);
+ if(buf_target.address)
+ call DymoPacket.addInfo(&buf_packet, &buf_target);
+ call AMSend.send(buf_info.nexthop, &buf_packet, call DymoPacket.getSize(&buf_packet));
+ buf_target.address = 0;
+ }
+ }
+ }
+
+ /* Send a RERR with buf_info as unreachable */
+ task void issueRERR(){
+ atomic {
+ if(busySend)
+ post issueRERR();
+ else {
+ busySend = TRUE;
+ call DymoPacket.createRM(&buf_packet, DYMO_RERR, NULL, &buf_info);
+ call AMSend.send(AM_BROADCAST_ADDR, &buf_packet, call DymoPacket.getSize(&buf_packet));
+ }
+ }
+ }
+
+ /* Send current fw_msg to fw_address */
+ task void forward(){
+ atomic {
+ if(busySend)
+ post forward();
+ else {
+ busySend = TRUE;
+ call AMSend.send(fw_address, &fw_msg, call DymoPacket.getSize(&fw_msg));
+ }
+ }
+ }
+
+ event message_t* Receive.receive(message_t* msg, void* payload, uint8_t len){
+#ifdef DYMO_MONITORING
+ signal DymoMonitor.msgReceived(msg);
+#endif
+ dbg("de", "DE: Message (type %hhu) received.\n", call DymoPacket.getType(msg));
+ atomic {
+ if(busyProcess){
+ dbg("de", "DE: I'm busy, I can't handle this message, sorry.\n");
+ return msg; //we discard msg if a message is already being processed
+ } else {
+ busyProcess = TRUE;
+ }
+ }
+ cur_info_pos = 0;
+ fw_address = AM_BROADCAST_ADDR;
+ call DymoPacket.startProcessing(msg, &fw_msg);
+ return avail_msg;
+ }
+
+ event proc_action_t DymoPacket.hopsProcessed(message_t * msg, uint8_t hop_limit, uint8_t hop_count){
+ cur_hopcnt = hop_count; //TODO use this
+ if(hop_limit == 0){
+ fw_address = 0;
+ dbg("de", "DE: This message has reached its HL (%hhu hops) => discard.\n", hop_count);
+ return ACTION_DISCARD_MSG;
+ } else {
+ return ACTION_KEEP;
+ }
+ }
+
+ proc_action_t process_rm_info(message_t * msg, rt_info_t * info){
+ cur_info_pos++;
+ if(cur_info_pos == 1){ //target
+
+ if(info->address == me.address){
+
+ if(call DymoPacket.getType(msg) == DYMO_RREQ){
+ dbg("de", "DE: This RREQ is for me => RREP.\n");
+ if(info->seqnum < me.seqnum)
+ incr_seqnum();
+ sendRREP = TRUE; //to send a RREP when we receive the next event (= originator info)
+ } else {
+ dbg("de", "DE: This RREP is for me, cool!\n");
+ }
+ fw_address = 0;
+ return ACTION_DISCARD_MSG;
+
+ } else { //not for me
+
+ info->nexthop = call AMPacket.source(msg);
+ if(call DymoPacket.getType(msg) == DYMO_RREQ){
+
+#if DYMO_INTER_RREP
+ //if we know a route to the target, we send a intermediate RREP and don't forward the message
+ ignoreNeeded = info->address;
+ if (call RoutingTable.getRoute(info->address, &buf_info) == SUCCESS) {
+#if DYMO_FORCE_INTER_RREP
+ if( !info->seqnum || !(call DymoTable.isSuperior(info, DYMO_RREQ)) ){
+#else
+ if( info->seqnum && !(call DymoTable.isSuperior(info, DYMO_RREQ)) ){
+#endif
+ dbg("de", "DE: This RREQ is for %u, but I know the route => RREP.\n", info->address);
+ buf_target = buf_info;
+ sendRREP = TRUE;
+ fw_address = 0;
+ return ACTION_DISCARD_MSG;
+ }
+ }
+#endif
+ return ACTION_KEEP;
+
+ } else { //RREP
+
+ ignoreNeeded = info->address;
+ dbg("de", "DE: This RREP is for %u.\n", info->address);
+ if(call RoutingTable.getForwardingRoute(info->address, &buf_info) == SUCCESS){
+ fw_address = buf_info.nexthop;
+ return ACTION_KEEP;
+ } else {
+ fw_address = 0;
+ return ACTION_DISCARD_MSG;
+ }
+
+ }//end RREP
+
+ }//end not for me
+
+ } else if((call DymoPacket.getType(msg) == DYMO_RREQ) //end if(info==target)
+ && (cur_info_pos == 2)
+ && (info->address == me.address)){
+
+ fw_address = 0;
+ sendRREP = FALSE;
+ return ACTION_DISCARD_MSG;
+
+ } else {
+
+ info->nexthop = call AMPacket.source(msg);
+ if(call DymoTable.update(info, call DymoPacket.getType(msg)) == EINVAL){
+
+ if(cur_info_pos == 2){ //origin
+ dbg("de", "DE: I am discarding a msg with a bad origin (%u)\n", info->address);
+ fw_address = 0;
+ return ACTION_DISCARD_MSG;
+ } else { //Additional info
+ return ACTION_DISCARD;
+ }
+
+ } else {
+
+ if((cur_info_pos == 2) && sendRREP){
+ buf_info = *info;
+ atomic {
+ if(!busyIssue){
+ busyIssue = 1;
+ post issueRREP();
+ }
+ }
+ sendRREP = 0;
+ }
+
+#ifdef DYMO_MONITORING
+ if( rreq_time //TODO probably misses a test
+ && (cur_info_pos == 2)
+ && (call DymoPacket.getType(msg) == DYMO_RREP) ) {
+ rreq_time = (call Timer.getNow()) - rreq_time;
+ signal DymoMonitor.routeDiscovered(rreq_time, info->address);
+ rreq_time = 0;
+ }
+#endif
+ return ACTION_KEEP;
+
+ }
+
+ } //end info!=target
+ }//end event
+
+ proc_action_t process_err_info(message_t * msg, rt_info_t * info){
+ info->nexthop = call AMPacket.source(msg);
+ if(call DymoTable.update(info, call DymoPacket.getType(msg)) == EINVAL){
+ return ACTION_DISCARD;
+ } else {
+ cur_info_pos++; //we only count kept pieces of info
+ return ACTION_KEEP;
+ }
+ }
+
+ event proc_action_t DymoPacket.infoProcessed(message_t * msg, rt_info_t * info){
+ if(call DymoPacket.getType(msg) == DYMO_RERR)
+ return process_err_info(msg, info);
+ else
+ return process_rm_info(msg, info);
+ }
+
+ event void DymoPacket.messageProcessed(message_t * msg){
+ avail_msg = msg;
+ if( (call DymoPacket.getType(msg) == DYMO_RERR) && cur_info_pos ){
+
+ post forward();
+
+ } else if( (call DymoPacket.getType(msg) != DYMO_RERR) && fw_address ){
+
+#if DYMO_APPEND_INFO
+ call DymoPacket.addInfo(&fw_msg, me);
+#endif
+ dbg("de", "DE: I'll forward this RM.\n");
+ post forward();
+
+ } else {
+
+ atomic {
+ busyProcess = 0;
+ }
+ dbg("de", "DE: I'm not busy anymore.\n");
+
+ }
+ dbg("de", "DE: Message (type %hhu) successfully processed.\n", call DymoPacket.getType(msg));
+ }
+
+ event void DymoTable.routeNeeded(addr_t destination){
+ if(ignoreNeeded == destination){
+ ignoreNeeded = 0;
+ } else {
+ buf_info.address = destination;
+ buf_info.seqnum = 0;
+ buf_info.has_hopcnt = FALSE;
+ atomic {
+ if(!busyIssue){
+ busyIssue = TRUE;
+#ifdef DYMO_MONITORING
+ rreq_time = call Timer.getNow();
+#endif
+ post issueRREQ();
+ }
+ }
+ }
+ }
+
+ event void DymoTable.brokenRouteNeeded(const rt_info_t * route_info){
+ buf_info = *route_info;
+ buf_info.has_hopcnt = FALSE;
+ atomic {
+ if(!busyIssue){
+ busyIssue = TRUE;
+ post issueRERR();
+ }
+ }
+ }
+
+ event void RoutingTable.evicted(const rt_info_t * route_info, reason_t r){
+ if(r == REASON_UNREACHABLE){
+ buf_info = *route_info;
+ buf_info.has_hopcnt = FALSE;
+ atomic {
+ if(!busyIssue){
+ busyIssue = TRUE;
+ post issueRERR();
+ }
+ }
+ }
+ }
+
+ event void AMSend.sendDone(message_t *msg, error_t error){
+ atomic {
+ busySend = FALSE;
+ }
+ if(msg == &fw_msg){
+ atomic{
+ busyProcess = FALSE;
+ }
+ } else if(msg == &buf_packet) {
+ atomic {
+ busyIssue = FALSE;
+ }
+ }
+
+ if(error == SUCCESS){
+ if(msg == &fw_msg)
+ dbg("de", "DE: Message (type %hhu) forwarded.\n", call DymoPacket.getType(msg));
+ else
+ dbg("de", "DE: Message (type %hhu) sent.\n", call DymoPacket.getType(msg));
+ } else
+ dbg("de", "DE: Failed to send message (type %hhu).\n", call DymoPacket.getType(msg));
+
+#ifdef DYMO_MONITORING
+ if(error == SUCCESS)
+ signal DymoMonitor.msgSent(msg);
+#endif
+ }
+
+ command error_t SplitControl.stop(){ }
+
+#ifdef DYMO_MONITORING
+
+ event void Timer.fired(){}
+
+ default event void DymoMonitor.msgReceived(message_t * msg){}
+
+ default event void DymoMonitor.msgSent(message_t * msg){}
+
+ default event void DymoMonitor.routeDiscovered(uint32_t delay, addr_t target){}
+
+#endif
+}
+
--- /dev/null
+#include "message.h"
+
+interface DymoMonitor {
+
+ event void msgReceived(message_t * msg);
+
+ event void msgSent(message_t * msg);
+
+ event void routeDiscovered(uint32_t delay, addr_t target);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+#include "routing.h"
+
+/**
+ * DymoNetworkC - Top level configuration providing a multihop network
+ * layer and implementing DYMO (DYnamic Manet On-demand) routing.
+ *
+ * @author Romain Thouvenin
+ */
+
+configuration DymoNetworkC {
+ provides {
+ interface AMSend as MHSend[uint8_t id];
+ interface AMPacket as MHPacket;
+ interface Packet;
+ interface Receive[uint8_t id];
+ interface Intercept[uint8_t id];
+ interface SplitControl;
+ }
+
+#ifdef DYMO_MONITORING
+ provides {
+ interface DymoMonitor;
+ interface RoutingTableInfo;
+ }
+#endif
+ provides interface MHControl;
+}
+
+implementation {
+ components ActiveMessageC;
+ components new AMReceiverC(AM_MULTIHOP) as MHReceiver, new AMReceiverC(AM_DYMO) as DymoReceiver;
+ components new AMSenderC(AM_MULTIHOP) as MHQueue, new AMSenderC(AM_DYMO) as DymoQueue;
+ components MHServiceC, DymoServiceC, NetControlM, DymoTableC;
+#ifdef LOOPBACK
+ components LoopBackM;
+#endif
+
+#ifdef LOOPBACK
+ MHSend = LoopBackM.AMSend;
+ Receive = LoopBackM.Receive;
+#else
+ MHSend = MHServiceC.MHSend;
+ Receive = MHServiceC.Receive;
+#endif
+ MHPacket = MHServiceC.MHPacket;
+ Packet = MHServiceC.Packet;
+ Intercept = MHServiceC.Intercept;
+
+ SplitControl = NetControlM.SplitControl;
+
+#ifdef LOOPBACK
+ LoopBackM.SubSend -> MHServiceC.MHSend;
+ LoopBackM.SubReceive -> MHServiceC.Receive;
+ LoopBackM.AMPacket -> MHServiceC.MHPacket;
+ LoopBackM.Packet -> MHServiceC.Packet;
+#endif
+
+ MHServiceC.AMPacket -> ActiveMessageC;
+ MHServiceC.SubPacket -> ActiveMessageC;
+ MHServiceC.AMSend -> MHQueue;
+ MHServiceC.SubReceive -> MHReceiver;
+ MHServiceC.Acks -> MHQueue;
+
+ DymoServiceC.AMPacket -> ActiveMessageC;
+ DymoServiceC.Packet -> ActiveMessageC;
+ DymoServiceC.AMSend -> DymoQueue;
+ DymoServiceC.Receive -> DymoReceiver;
+#if DYMO_LINK_FEEDBACK
+ DymoServiceC.LinkMonitor -> MHServiceC;
+#endif
+
+ NetControlM.AMControl -> ActiveMessageC.SplitControl;
+ NetControlM.TableControl -> DymoTableC.StdControl;
+ NetControlM.EngineControl -> DymoServiceC.SplitControl;
+
+#ifdef DYMO_MONITORING
+ RoutingTableInfo = DymoTableC.RoutingTableInfo;
+ DymoMonitor = DymoServiceC.DymoMonitor;
+#endif
+ MHControl = MHServiceC.MHControl;
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+#include "routing_table.h"
+
+/**
+ * DymoPacket - Interface to manipulate DYMO packets.
+ *
+ * @author Romain Thouvenin
+ */
+
+interface DymoPacket {
+
+ /*****************
+ * Packet header *
+ *****************/
+
+ /**
+ * Type of the packet.
+ * @return DYMO_RREQ, DYMO_RREP or DYMO_RERR
+ */
+ command dymo_msg_t getType(message_t * msg);
+
+ /**
+ * Size of the packet (all fields included).
+ */
+ command uint16_t getSize(message_t * msg);
+
+
+ /*********************
+ * Creating a packet *
+ *********************/
+
+ /**
+ * Create a routing message. This is not strictly a Routing Message
+ * as defined by DYMO specs: this is also the command to create a
+ * RERR.
+ * @param msg the buffer to fill
+ * @param msg_type The type of message (RREQ, RREP or RERR)
+ * @param origin The originator of the routing message, should be NULL for a RERR
+ * @param target The target of the routing message, or first unreachable node for a RERR
+ */
+ command void createRM(message_t * msg, dymo_msg_t msg_type,
+ const rt_info_t * origin, const rt_info_t * target);
+
+ /**
+ * Append additional information to a message. This is up to the
+ * implementation to choose where in the message the information
+ * should be added. In anycase, it must not be added before the
+ * target and originator.
+ * @param msg the existing message
+ * @param info The piece of information to append @return
+ * @return ESIZE if the payload has reached its maximum size<br/>
+ * SUCCESS otherwise
+ */
+ command error_t addInfo(message_t * msg, const rt_info_t * info);
+
+
+ /***********************
+ * Processing a packet *
+ ***********************/
+
+ /**
+ * Start the processing task of a DYMO message. Currently, the only
+ * way to access the content of a message is to read it entirely
+ * with this command. It will report all information found thanks to
+ * events above.
+ * @param msg The message to process
+ * @param newmsg The message that will contain the processed message
+ * to be forwarded. May be NULL if such a message is not wanted.
+ */
+ command void startProcessing(message_t * msg, message_t * newmsg);
+
+ /**
+ * Hop values have been extracted from the processed packet.
+ * @param msg the message being processed
+ * @param hop_limit the (decremented) hop limit value of the message
+ * @param hop_count the (incremented) hop count value of the message
+ * @return ACTION_DISCARD_MSG if a building a message to be forwarded
+ * is not wanted anymore (typically when hop_limit==0),
+ * anything else otherwise.
+ */
+ event proc_action_t hopsProcessed(message_t * msg, uint8_t hop_limit, uint8_t hop_count);
+
+ /**
+ * A piece of routing information has been extracted from the processed packet.
+ * @param msg the message being processed
+ * @param info the extracted piece of information. If present, hopcnt has been decremented.
+ * @return ACTION_KEEP to keep this information in the forwarded message<br/>
+ * ACTION_DISCARD to remove this information in the forwardedmessage<br/>
+ * ACTION_DISCARD_MSG to cancel the creation of the forwarded message.
+ */
+ event proc_action_t infoProcessed(message_t * msg, rt_info_t * info);
+
+ /**
+ * Processing task finished.
+ * No further processing event will be signaled for this message.
+ */
+ event void messageProcessed(message_t * msg);
+}
--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+#include "dymo_packet.h"
+
+/**
+ * DymoPacketM - Implementation of the DYMO packets format.
+ *
+ * @author Romain Thouvenin
+ */
+
+module DymoPacketM {
+ provides {
+ interface DymoPacket;
+ interface PacketMaker; //For tests and debugging
+ }
+ uses interface Packet;
+}
+//TODO generalize size values
+implementation {
+ message_t * currentMsg;
+ message_t * processedMsg;
+
+ /* Local functions */
+ void create_block(nx_uint8_t * payload, const rt_info_t * info);
+ uint8_t block_size(nx_uint8_t * block);
+ uint8_t block_info_size(nx_uint8_t * block);
+ uint8_t block_header_size(nx_uint8_t * block);
+ uint8_t block_num_addr(nx_uint8_t * block);
+ void block_get_info(nx_uint8_t * block, uint8_t pos, rt_info_t * info, bool update);
+ nx_uint8_t * block_get_pointer(nx_uint8_t * block, uint8_t pos, uint8_t * size);
+ bool block_can_contain(nx_uint8_t * block, const rt_info_t * info);
+ void block_add_info(nx_uint8_t * block, const rt_info_t * info);
+ void move_data(nx_uint8_t * data, uint8_t amount, uint8_t offset);
+
+
+ /*****************
+ * Packet header *
+ *****************/
+
+ command dymo_msg_t DymoPacket.getType(message_t * msg){
+ nx_uint8_t * p = call Packet.getPayload(msg, 1);
+ return *p;
+ }
+
+ command uint16_t DymoPacket.getSize(message_t * msg){
+ nx_uint8_t * p = call Packet.getPayload(msg, 3);
+ return *(nx_uint16_t *)(p + 1);
+ }
+
+
+ /*********************
+ * Creating a packet *
+ *********************/
+
+ command void DymoPacket.createRM(message_t * msg, dymo_msg_t msg_type,
+ const rt_info_t * origin, const rt_info_t * target){
+ nx_uint8_t * payload = call Packet.getPayload(msg, call Packet.maxPayloadLength());
+ nx_uint16_t * size_p;
+ *(payload++) = msg_type;
+ size_p = (nx_uint16_t *) payload;
+ payload += 2;
+ *(payload++) = DYMO_HOPLIMIT;
+ *(payload++) = 0;
+
+ create_block(payload, target);
+
+ if(origin){
+ if(block_can_contain(payload, origin)){
+ block_add_info(payload, origin);
+ *size_p = block_size(payload);
+ } else {
+ *size_p = block_size(payload);
+ payload += *size_p;
+ create_block(payload, origin);
+ *size_p += block_size(payload);
+ }
+ } else {
+ *size_p = block_size(payload);
+ }
+
+ //size of msg header
+ //it is here to save a few instructions or a byte
+ *size_p += 5;
+ }
+
+ command error_t DymoPacket.addInfo(message_t * msg, const rt_info_t * info){
+ nx_uint8_t * payload = call Packet.getPayload(msg, call Packet.maxPayloadLength());
+ nx_uint16_t * size_p = (nx_uint16_t *)(payload + 1);
+ nx_uint8_t * block = payload + 5;
+ uint8_t bsize;
+
+ while(block < payload + *size_p){
+ //We don't want to add something before the origin
+ if ( ((block > payload + 5) && block_can_contain(block, info))
+ || ((block == payload + 5) && (block_num_addr(block) > 1)) ) {
+
+ uint8_t isize = block_info_size(block);
+ if(*size_p + isize > call Packet.maxPayloadLength()){
+ return ESIZE;
+ } else {
+ bsize = block_size(block);
+ move_data(block + bsize, payload + *size_p - (block + bsize), isize);
+ block_add_info(block, info);
+ *size_p += isize;
+ return SUCCESS;
+ }
+
+ } else {
+ block += block_size(block);
+ }
+ }
+
+ create_block(block, info);
+ bsize = block_size(block);
+ if(*size_p + bsize > call Packet.maxPayloadLength()){
+ return ESIZE;
+ } else {
+ *size_p += bsize;
+ return SUCCESS;
+ }
+ }
+
+
+ /***********************
+ * Processing a packet *
+ ***********************/
+
+ task void processMessage(){
+ nx_uint8_t * payload = call Packet.getPayload(currentMsg, call Packet.maxPayloadLength());
+ nx_uint8_t * end = payload + *(nx_uint16_t *)(payload+1);
+ nx_uint8_t * fw_payload = NULL;
+ nx_uint16_t * fw_size = NULL;
+ nx_uint8_t *fw_block, *info_p;
+ rt_info_t info;
+ uint8_t i,n,s;
+ bool first_block = 1;
+ proc_action_t action;
+
+ payload += 3;
+ *(payload++) -= 1; //decr hopL
+ *(payload++) += 1; //incr hopC
+ action = signal DymoPacket.hopsProcessed(currentMsg, *(payload-2), *(payload-1));
+ if(processedMsg){
+ if(action != ACTION_DISCARD_MSG){
+ fw_payload = call Packet.getPayload(processedMsg, call Packet.maxPayloadLength());
+ memcpy(fw_payload, payload - 5, 5);
+ fw_size = (nx_uint16_t *)(fw_payload + 1);
+ *fw_size = 5;
+ fw_payload += 5;
+ } else {
+ processedMsg = NULL;
+ }
+ }
+
+ while(payload < end){
+ fw_block = NULL;
+ n = block_num_addr(payload);
+
+ for(i=0;i<n;i++){
+ block_get_info(payload, i, &info, !first_block || i);
+ action = signal DymoPacket.infoProcessed(currentMsg, &info);
+
+ if(processedMsg){
+ switch(action){
+ case ACTION_KEEP:
+ if(!fw_block){
+ s = block_header_size(payload);
+ memcpy(fw_payload, payload, s);
+ fw_block = fw_payload;
+ *(fw_block+1) = 0;
+ fw_payload += s;
+ }
+ info_p = block_get_pointer(payload, i, &s);
+ memcpy(fw_payload, info_p, s);
+ fw_payload += s;
+ *(fw_block+1) += 1; //increments NumAddr
+ break;
+
+ case ACTION_DISCARD_MSG:
+ processedMsg = NULL;
+ default:
+ }
+ }//if
+
+ }//for
+ payload += block_size(payload);
+ first_block = 0;
+ if(fw_block)
+ *fw_size += block_size(fw_block);
+ }
+
+ signal DymoPacket.messageProcessed(currentMsg);
+ }
+
+ command void DymoPacket.startProcessing(message_t * msg, message_t * newmsg){
+ currentMsg = msg;
+ processedMsg = newmsg;
+ post processMessage();
+ }
+
+
+
+ //TODO return block_size, it is always needed after a block creation
+ void create_block(nx_uint8_t * payload, const rt_info_t * info){
+ uint8_t semantics;
+
+ semantics = BLOCK_HEAD;
+ if(info->seqnum)
+ semantics |= BLOCK_SEQNUM;
+ if(info->has_hopcnt)
+ semantics |= BLOCK_HOPCNT;
+
+ *(payload++) = semantics;
+ *(payload++) = 1;
+ *(nx_addr_t *)payload = info->address;
+ payload += sizeof(addr_t);
+ if(info->seqnum){
+ *(nx_seqnum_t *)payload = info->seqnum;
+ payload += 2;
+ }
+ if(info->has_hopcnt){
+ *(payload++) = info->hopcnt;
+ }
+ }
+
+ void block_add_info(nx_uint8_t * block, const rt_info_t * info){
+ uint8_t semantics = *block;
+ nx_uint8_t * size_p = block + 1;
+ block += block_size(block);
+ *size_p += 1;
+ if(semantics & BLOCK_HEAD){
+ *block = info->address % 256;
+ block++;
+ } else {
+ *(nx_addr_t *)block = info->address;
+ block += sizeof(addr_t);
+ }
+
+ if(semantics & BLOCK_SEQNUM){
+ *(nx_seqnum_t *)block = info->seqnum;
+ block += sizeof(seqnum_t);
+ }
+
+ if(semantics & BLOCK_HOPCNT){
+ *block = info->hopcnt;
+ }
+ }
+
+ bool block_can_contain(nx_uint8_t * block, const rt_info_t * info){
+ if( (*block & BLOCK_SEQNUM) && !info->seqnum )
+ return 0;
+ if( !(*block & BLOCK_SEQNUM) && info->seqnum )
+ return 0;
+
+ if( (*block & BLOCK_HOPCNT) && !info->has_hopcnt )
+ return 0;
+ if( !(*block & BLOCK_HOPCNT) && info->has_hopcnt )
+ return 0;
+
+ if( (*block & BLOCK_HEAD) && (*(block + 2) != (info->address / 256)) )
+ return 0;
+
+ return 1;
+ }
+
+ uint8_t block_info_size(nx_uint8_t * block){
+ uint8_t result = 1;
+ if(!(*block & BLOCK_HEAD))
+ result++;
+ if(*block & BLOCK_SEQNUM)
+ result += 2;
+ if(*block & BLOCK_HOPCNT)
+ result++;
+ //TODO add max age
+ return result;
+ }
+
+ uint8_t block_header_size(nx_uint8_t * block){
+ if(*block & BLOCK_HEAD)
+ return 3;
+ else
+ return 2;
+ }
+
+ uint8_t block_num_addr(nx_uint8_t * block){
+ return *(block + 1);
+ }
+
+ uint8_t block_size(nx_uint8_t * block){
+ uint8_t result = 2;
+ if(*block & BLOCK_HEAD){
+ result++;
+ }
+ return result + block_num_addr(block) * block_info_size(block);
+ }
+
+ nx_uint8_t * block_get_pointer(nx_uint8_t * block, uint8_t pos, uint8_t * size){
+ if(size){
+ *size = block_info_size(block);
+ return block + block_header_size(block) + pos * (*size);
+ } else {
+ return block + block_header_size(block) + pos * block_info_size(block);
+ }
+ }
+
+ void block_get_info(nx_uint8_t * block, uint8_t pos, rt_info_t * info, bool update){
+ nx_uint8_t * semantics = block;
+ block = block_get_pointer(block, pos, NULL);
+
+ if(*semantics & BLOCK_HEAD){
+ info->address = *(semantics + 2) * 256 + *block;
+ block++;
+ } else {
+ info->address = *(nx_addr_t *)block;
+ block += sizeof(addr_t);
+ }
+
+ if(*semantics & BLOCK_SEQNUM){
+ info->seqnum = *(nx_seqnum_t *)block;
+ block += sizeof(seqnum_t);
+ } else {
+ info->seqnum = 0;
+ }
+
+ if(*semantics & BLOCK_HOPCNT){
+ info->has_hopcnt = 1;
+ if(update)
+ *block += 1;
+ info->hopcnt = *block;
+ block++;
+ } else {
+ info->has_hopcnt = 0;
+ }
+ }
+
+ void move_data(nx_uint8_t * data, uint8_t amount, uint8_t offset){
+ nx_uint8_t * newdata = data + amount + offset;
+ data += amount;
+ for(; amount > 0; amount--)
+ *--newdata = *--data;
+ }
+
+
+ /**************
+ * PakerMaker *
+ **************/
+
+ command uint16_t PacketMaker.getSize(message_t * msg){
+ return call DymoPacket.getSize(msg);
+ }
+
+ command void PacketMaker.createRM(message_t * msg, dymo_msg_t msg_type,
+ const rt_info_t * origin, const rt_info_t * target){
+ call DymoPacket.createRM(msg, msg_type, origin, target);
+ }
+
+ command error_t PacketMaker.addInfo(message_t * msg, const rt_info_t * info){
+ return call DymoPacket.addInfo(msg, info);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+#include "StorageVolumes.h"
+
+/**
+ * DymoServiceC - Implements the DYMO routing protocol
+ *
+ * @author Romain Thouvenin
+ */
+
+configuration DymoServiceC {
+ provides {
+ interface SplitControl;
+ }
+ uses {
+ interface Packet;
+ interface AMPacket;
+ interface AMSend;
+ interface Receive;
+ interface LinkMonitor;
+ }
+
+#ifdef DYMO_MONITORING
+ provides {
+ interface DymoMonitor;
+ }
+#endif
+}
+
+implementation {
+ components DymoTableC, DymoEngineM, DymoPacketM;
+ components new ConfigStorageC(VOLUME_DYMODATA);
+
+ SplitControl = DymoEngineM.SplitControl;
+ Packet = DymoPacketM.Packet;
+ AMPacket = DymoEngineM.AMPacket;
+ AMSend = DymoEngineM.AMSend;
+ Receive = DymoEngineM.Receive;
+ LinkMonitor = DymoTableC.LinkMonitor;
+
+ DymoEngineM.DymoPacket -> DymoPacketM;
+ DymoEngineM.RoutingTable -> DymoTableC;
+ DymoEngineM.DymoTable -> DymoTableC;
+
+ DymoEngineM.Mount -> ConfigStorageC;
+ DymoEngineM.ConfigStorage -> ConfigStorageC;
+
+#ifdef DYMO_MONITORING
+ components new TimerMilliC();
+
+ DymoMonitor = DymoEngineM.DymoMonitor;
+ DymoEngineM.Timer -> TimerMilliC;
+#endif
+}
--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+#include "routing_table.h"
+
+/**
+ * DymoTable - Interface to manipulate a dymo routing table
+ *
+ * @author Romain Thouvenin
+ */
+
+interface DymoTable {
+
+ /**
+ * Update the table with fresh information about a destination.
+ * @param route_info The routing information associated to the destination
+ * @param msg_type The type of message that provided this info
+ * @return SUCCESS if the route was added or updated<br/>
+ * EINVAL if route_info was inferior to existing route,
+ * or msg_type = rerr and the route does not exist
+ * FAIL if the table was full and no existing route could be deleted<br/>
+ */
+ command error_t update(const rt_info_t * route_info, dymo_msg_t msg_type);
+
+ command bool isSuperior(const rt_info_t * route_info, dymo_msg_t msg_type);
+
+ /**
+ * Signal that a component asked for an unknown route, a RREQ should
+ * be generated.
+ * @param destination Target node of the needed route.
+ */
+ event void routeNeeded(addr_t destination);
+
+ event void brokenRouteNeeded(const rt_info_t * route_info);
+}
--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+#include "dymo_table.h"
+
+/**
+ * DymoTableC - Provides a routing table with DYMO routing information.
+ *
+ * @author Romain Thouvenin
+ */
+
+configuration DymoTableC {
+ provides {
+ interface StdControl;
+ interface RoutingTable;
+ interface DymoTable;
+ }
+#ifdef DYMO_MONITORING
+ provides interface RoutingTableInfo;
+#endif
+
+ uses interface LinkMonitor;
+}
+
+implementation {
+ components new DymoTableM(MAX_TABLE_SIZE);
+ components new TimerMilliC() as BaseTimer;
+ components new VirtualizeTimerC(TMilli, MAX_TABLE_SIZE * NB_ROUTE_TIMERS) as Timers;
+ components TinySchedulerC;
+
+ StdControl = DymoTableM.StdControl;
+ RoutingTable = DymoTableM.RoutingTable;
+ DymoTable = DymoTableM.DymoTable;
+ LinkMonitor = DymoTableM.LinkMonitor;
+
+ DymoTableM.Timer -> Timers;
+
+ Timers.TimerFrom -> BaseTimer.Timer;
+
+#ifdef DYMO_MONITORING
+ RoutingTableInfo = DymoTableM.RoutingTableInfo;
+#endif
+}
--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+#include "dymo_table.h"
+
+/**
+ * DymoTableM - Implements a routing table with DYMO routing information.
+ * @param maxsize maximum number of entries in the table, cannot be higher than 51
+ *
+ * @author Romain Thouvenin
+ */
+
+generic module DymoTableM(uint8_t maxsize) {
+ provides {
+ interface StdControl;
+ interface RoutingTable;
+ interface DymoTable;
+ }
+ uses {
+ interface Timer<TMilli>[uint8_t id];
+ interface LinkMonitor;
+ }
+#ifdef DYMO_MONITORING
+ provides interface RoutingTableInfo;
+#endif
+}
+
+implementation {
+
+ rt_entry_t table[maxsize];
+ rt_info_t buf_info;
+ uint8_t size;
+ uint8_t num_entries;
+ uint8_t replace;
+
+ /* declared at the end */
+ void replace_info(uint8_t entry_id, const rt_info_t * route_info);
+ int8_t get_route(addr_t address);
+ void delete_route(uint8_t entry_id, reason_t r);
+ bool is_superior(const rt_info_t * info1, const rt_entry_t * entry, dymo_msg_t msg_type);
+ void set_timer(uint8_t entry_id, rt_timer_t timer_id);
+ void cancel_timer(uint8_t entry_id, rt_timer_t timer);
+ void cancel_timers(uint8_t entry_id);
+
+ command error_t StdControl.start(){
+ num_entries = 0;
+ size = 0;
+ replace = 0;
+ return SUCCESS;
+ }
+
+ command error_t StdControl.stop(){
+ uint8_t i;
+ for(i=0; i<num_entries; i++){
+ if( !(table[i].flags & FLAG_DELETED) ){
+ cancel_timers(i);
+ }
+ }
+ return SUCCESS;
+ }
+
+ command error_t RoutingTable.getForwardingRoute(addr_t address, rt_info_t * info){
+ int8_t i = get_route(address);
+ dbg("dt", "DT: Someone wants a forwarding route for %u.\n", address);
+ if(i == -1){
+ dbg("dt", "DT: But I don't have it. => brokenRouteNeeded\n");
+ buf_info.address = address;
+ buf_info.seqnum = 0;
+ buf_info.has_hopcnt = 0;
+ signal DymoTable.brokenRouteNeeded(&buf_info);
+ return FAIL;
+ }
+
+ //The caller may want to know what is in the table even if it is broken
+ if(info && !(table[i].flags & FLAG_DELETED)){
+ *info = table[i].info;
+ }
+
+ if(table[i].flags & (FLAG_BROKEN | FLAG_DELETED)){
+ dbg("dt", "DT: But it is deleted. => brokenRouteNeeded\n");
+ signal DymoTable.brokenRouteNeeded(&table[i].info); //TODO not if not used recently (for other signals too)
+ return FAIL;
+ }
+
+ cancel_timer(i, ROUTE_NEW);
+ table[i].flags &= ~FLAG_NEW;
+ cancel_timer(i, ROUTE_DELETE);
+ set_timer(i, ROUTE_USED);
+ table[i].flags |= FLAG_USED;
+ dbg("dt", "DT: Here it is: %u.\n", table[i].info.nexthop);
+ return SUCCESS;
+ }
+
+ command error_t RoutingTable.getRoute(addr_t address, rt_info_t * info){
+ int i = get_route(address);
+ dbg("dt", "DT: Someone wants a sending route for %u.\n", address);
+ if(i == -1){
+ dbg("dt", "DT: But I don't have it. => routeNeeded\n");
+ signal DymoTable.routeNeeded(address);
+ return EBUSY;
+ }
+
+ //The caller may want to know what is in the table even if it is broken
+ if(info){
+ *info = table[i].info;
+ }
+
+ if(table[i].flags & (FLAG_DELETED | FLAG_BROKEN)){
+ dbg("dt", "DT: But it is deleted or broken. => routeNeeded\n");
+ signal DymoTable.routeNeeded(address);
+ return EBUSY;
+ }
+
+ //We assume the route is going to be used
+ cancel_timer(i, ROUTE_NEW);
+ table[i].flags &= ~FLAG_NEW;
+ cancel_timer(i, ROUTE_DELETE);
+ set_timer(i, ROUTE_USED);
+ table[i].flags |= FLAG_USED;
+ dbg("dt", "DT: Here it is: %u-%u-%hhu.\n", table[i].info.nexthop, table[i].info.seqnum, table[i].info.hopcnt);
+ return SUCCESS;
+ }
+
+ command error_t DymoTable.update(const rt_info_t * route_info, dymo_msg_t msg_type){
+ int8_t i = get_route(route_info->address);
+
+ if(msg_type == DYMO_RERR){
+
+ if(i != -1){
+ if( (table[i].info.nexthop == route_info->nexthop)
+ && ((table[i].info.seqnum == 0)
+ || (route_info->seqnum == 0)
+ || (route_info->seqnum >= table[i].info.seqnum)) ){
+ table[i].flags |= FLAG_BROKEN;
+ dbg("dt", "DT: Route for %u evicted because of a RERR.\n", route_info->address);
+ signal RoutingTable.evicted(&table[i].info, REASON_UNREACHABLE);
+ return SUCCESS;
+ } else {
+ return EINVAL;
+ }
+ } else {
+ return EINVAL;
+ }
+
+ } else {
+
+ if(i == -1){
+
+ if(num_entries < maxsize){ //We have room to add a new route
+
+ replace_info(num_entries, route_info);
+ num_entries++;
+ size++;
+ dbg("dt", "DT: Updated route for %u in entry %hhu.\n", route_info->address, num_entries-1); //TODO debug below too
+ return SUCCESS;
+
+ } else { //We have to find a route to replace
+ //TODO possible optimization : caching the last deleted and broken route
+ int8_t j = -1; //will be set to a non-new route if found
+
+ //We look for a deleted route
+ for(i=0; i<num_entries; i++){
+ if(table[i].flags & FLAG_DELETED){
+ replace_info(i, route_info);
+ return SUCCESS;
+ }
+ }
+
+ //the table is full, we try to replace an existing route
+ for(i=0; i<num_entries; i++){
+ if(table[i].flags & FLAG_BROKEN){
+ replace_info(i, route_info);
+ return SUCCESS;
+ } else if( !(table[i].flags & FLAG_NEW) ){
+ j = i;
+ }
+ }
+
+ //no broken route found, we a take a non-new route
+ //TODO rather take a non-used route
+ if(j != -1){
+ delete_route(j, REASON_FULL);
+ replace_info(j, route_info);
+ return SUCCESS;
+ }
+
+ /* No room found. We delete a random route */
+ delete_route(replace, REASON_FULL);
+ replace_info(replace++, route_info);
+ if (replace == maxsize)
+ replace = 0;
+ return SUCCESS;
+
+ }
+
+ } else { //if(i == -1)
+
+ if(is_superior(route_info, table + i, msg_type)){
+ replace_info(i, route_info);
+ return SUCCESS;
+ } else {
+ return EINVAL;
+ }
+
+ }
+
+ }
+ }
+
+ command bool DymoTable.isSuperior(const rt_info_t * info, dymo_msg_t t){
+ int8_t i = get_route(info->address);
+ return ((i == -1) || is_superior(info, table + i, t));
+ }
+
+ event void Timer.fired[uint8_t timer_id](){
+ uint8_t e = timer_id / NB_ROUTE_TIMERS;
+ switch(timer_id % NB_ROUTE_TIMERS){
+ case ROUTE_AGE_MIN:
+ table[e].flags &= ~FLAG_NEW;
+ break;
+ case ROUTE_AGE_MAX:
+ dbg("dt", "DT: Route for %u is really old, I delete it.\n", table[e].info.address);
+ delete_route(e, REASON_OLD);
+ break;
+ case ROUTE_NEW:
+ table[e].flags &= ~FLAG_NEW;
+ set_timer(e, ROUTE_DELETE);
+ break;
+ case ROUTE_USED:
+ table[e].flags &= ~FLAG_USED;
+ set_timer(e, ROUTE_DELETE);
+ break;
+ case ROUTE_DELETE:
+ dbg("dt", "DT: Route for %u is unused, I delete it.\n", table[e].info.address);
+ delete_route(e, REASON_OLD);
+ break;
+ }
+ }
+
+ event void LinkMonitor.brokenLink(addr_t neighbor){
+ int8_t i = get_route(neighbor);
+ if (i != -1) {
+ table[i].flags |= FLAG_BROKEN;
+ signal RoutingTable.evicted(&table[i].info, REASON_UNREACHABLE);
+ if (table[i].flags & (FLAG_NEW | FLAG_USED)) {
+ cancel_timer(i, ROUTE_NEW);
+ cancel_timer(i, ROUTE_USED);
+ set_timer(i, ROUTE_DELETE);
+ }
+ }
+ }
+
+ void replace_info(uint8_t pos, const rt_info_t * route_info){
+ table[pos].info = *route_info;
+ table[pos].flags = FLAG_NEW;
+ cancel_timers(pos);
+ set_timer(pos, ROUTE_AGE_MIN);
+ set_timer(pos, ROUTE_AGE_MAX);
+ set_timer(pos, ROUTE_NEW);
+ }
+
+ /* Return the index of the route toward address if it exists, -1 otherwise */
+ int8_t get_route(addr_t address){
+ uint8_t i = 0;
+ for(i=0;i<num_entries;i++){
+ if(table[i].info.address == address){
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /* Remove a route from the table */
+ void delete_route(uint8_t entry_id, reason_t r){
+ table[entry_id].flags = FLAG_DELETED;
+ cancel_timers(entry_id);
+ dbg("dt", "DT: I'm deleting route number %hhu (for node %u).\n", entry_id, table[entry_id].info.address);
+ signal RoutingTable.evicted(&table[entry_id].info, r);
+ }
+
+ /* compare two pieces of routing information
+ * returns true if info1 > entry->info */
+ bool is_superior(const rt_info_t * info1, const rt_entry_t * entry, dymo_msg_t msg_type){
+ //a copy of the superior test in the specifications
+ //with nil values discarded
+ return ((info1->seqnum > entry->info.seqnum)
+ || ((info1->seqnum == entry->info.seqnum)
+ && info1->has_hopcnt
+ && entry->info.has_hopcnt
+ && ((info1->hopcnt < entry->info.has_hopcnt)
+ || ((info1->hopcnt == entry->info.has_hopcnt)
+ && ((msg_type == DYMO_RREP)
+ || (entry->flags & FLAG_BROKEN))))));
+ }
+
+ /* Start a timer for a route */
+ void set_timer(uint8_t entry_id, rt_timer_t timer_id){
+ call Timer.startOneShot[entry_id * NB_ROUTE_TIMERS + timer_id](timer_values[timer_id]);
+ }
+
+ /* Cancel a timer for a route */
+ void cancel_timer(uint8_t entry_id, rt_timer_t timer_id){
+ call Timer.stop[entry_id * NB_ROUTE_TIMERS + timer_id]();
+ }
+
+ /* Cancel all the timers of an entry */
+ void cancel_timers(uint8_t entry_id){
+ uint8_t i = entry_id * NB_ROUTE_TIMERS;
+ for(i=0; i<NB_ROUTE_TIMERS; i++){
+ call Timer.stop[i]();
+ }
+ }
+
+#ifdef DYMO_MONITORING
+
+ command uint8_t RoutingTableInfo.size(){
+ return size;
+ }
+
+ command uint8_t RoutingTableInfo.maxSize(){
+ return maxsize;
+ }
+
+ command uint8_t RoutingTableInfo.getTableContent(rt_info_t * buf){
+ uint8_t i=0, j=0;
+ for(i=0; i<num_entries; i++){
+ if( !(table[i].flags & (FLAG_DELETED | FLAG_BROKEN)) ){
+ buf[j++] = table[i].info;
+ }
+ }
+ return j;
+ }
+
+ command uint8_t RoutingTableInfo.getLinks(rt_link_t * buf){
+ uint8_t i=0, j=0;
+ for(i=0; i<num_entries; i++){
+ if( !(table[i].flags & (FLAG_DELETED | FLAG_BROKEN)) ){
+ buf[j].target = table[i].info.address;
+ buf[j].nexthop = table[i].info.nexthop;
+ j++;
+ }
+ }
+ return j;
+ }
+
+#endif
+
+ default event void RoutingTable.evicted(const rt_info_t * route_info, reason_t r){ }
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+/**
+ * NetControlM - Manages the control of all components involved in the
+ * DymoNetwork component.
+ *
+ * @author Romain Thouvenin
+ */
+
+// TODO generalize to a multiControl
+module NetControlM {
+ provides interface SplitControl;
+ uses {
+ interface SplitControl as AMControl;
+ interface StdControl as TableControl;
+ interface SplitControl as EngineControl;
+ }
+}
+
+implementation {
+ uint8_t started;
+
+ command error_t SplitControl.start(){
+ error_t e = call TableControl.start();
+ started = 1;
+
+ if(e == SUCCESS){
+
+ e = call AMControl.start();
+ if(e == SUCCESS)
+ return call EngineControl.start();
+ else
+ return e;
+
+ } else {
+ return e;
+ }
+ }
+
+ event void AMControl.startDone(error_t e){
+ if (e == SUCCESS) {
+ if (started++ == 2)
+ signal SplitControl.startDone(e);
+ } else if (started) {
+ started = 0;
+ signal SplitControl.startDone(e);
+ }
+ }
+
+ event void EngineControl.startDone(error_t e) {
+ if (e == SUCCESS) {
+ if (started++ == 2)
+ signal SplitControl.startDone(e);
+ } else if (started) {
+ started = 0;
+ signal SplitControl.startDone(e);
+ }
+ }
+
+ command error_t SplitControl.stop(){
+ if(call AMControl.stop() == SUCCESS)
+ return call TableControl.stop();
+ else
+ return FAIL;
+ }
+
+ event void AMControl.stopDone(error_t e){
+ signal SplitControl.stopDone(e);
+ }
+
+ event void EngineControl.stopDone(error_t e){ }
+
+}
--- /dev/null
+interface PacketMaker {
+
+ command uint16_t getSize(message_t * msg);
+
+ command void createRM(message_t * msg, dymo_msg_t msg_type,
+ const rt_info_t * origin, const rt_info_t * target);
+
+ command error_t addInfo(message_t * msg, const rt_info_t * info);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+#include "routing.h"
+
+typedef enum block_semantics {
+ BLOCK_HEAD = 0x1,
+ BLOCK_SEQNUM = 0x2,
+ BLOCK_HOPCNT = 0x4
+} block_semantics_t;
--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+#ifndef _DYMO_TABLE_H_
+#define _DYMO_TABLE_H_
+
+#include "routing_table.h"
+
+/**
+ * Types associated to a dymo routing table.
+ * @author Romain Thouvenin
+ */
+
+typedef struct rt_entry {
+ rt_info_t info;
+ uint8_t flags;
+} rt_entry_t;
+
+typedef enum {
+ FLAG_BROKEN = 0x01,
+ FLAG_NEW = 0x02,
+ FLAG_USED = 0x04,
+ FLAG_DELETED = 0x08,
+} rt_flag_t;
+
+typedef enum { //TODO optimize the number of timers
+ ROUTE_AGE_MIN = 0,
+ ROUTE_AGE_MAX,
+ ROUTE_NEW,
+ ROUTE_USED,
+ ROUTE_DELETE,
+ NB_ROUTE_TIMERS
+} rt_timer_t;
+
+uint32_t timer_values[NB_ROUTE_TIMERS] = {
+ 1000, //ROUTE_AGE_MIN
+ DYMO_ROUTE_AGE_MAX, //ROUTE_AGE_MAX
+ DYMO_ROUTE_TIMEOUT, //ROUTE_NEW
+ DYMO_ROUTE_TIMEOUT, //ROUTE_USED
+ DYMO_ROUTE_TIMEOUT * 2 //ROUTE_DELETE
+};
+
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+#include "routing.h"
+#include "routing_table.h"
+
+/**
+ * DymoEngineM - Implements the algorithms to generate and process
+ * DYMO messages. This the simultor version, without persistent
+ * storage of the seqnum.
+ *
+ * @author Romain Thouvenin
+ */
+
+module DymoEngineM {
+ provides {
+ interface SplitControl;
+ }
+ uses {
+ interface DymoTable;
+ interface RoutingTable;
+ interface DymoPacket;
+ interface AMSend;
+ interface AMPacket;
+ interface Receive;
+ }
+
+#ifdef DYMO_MONITORING
+ provides interface DymoMonitor;
+ uses {
+ interface Timer<TMilli>;
+ }
+#endif
+}
+
+implementation {
+ message_t * avail_msg; //to be returned by receive
+ message_t buf_avail; //first avail_msg
+ message_t buf_packet;
+ rt_info_t me;
+ rt_info_t buf_info;
+ addr_t ignoreNeeded;
+ bool busySend;
+
+ /* for processing */
+ bool busyProcess, busyIssue;
+ uint8_t cur_hopcnt;
+ uint8_t cur_info_pos;
+ rt_info_t buf_target;
+ addr_t fw_address; //set to 0 if the message must not be forwarded
+ message_t fw_msg;
+ bool sendRREP;
+
+#ifdef DYMO_MONITORING
+ uint32_t rreq_time;
+#endif
+
+
+ task void startDoneTask() {
+ signal SplitControl.startDone(SUCCESS);
+ }
+
+ command error_t SplitControl.start(){
+ me.address = call AMPacket.address();
+ me.seqnum = 1;
+ me.has_hopcnt = 1;
+ me.hopcnt = 0;
+
+ avail_msg = &buf_avail;
+ ignoreNeeded = 0;
+ sendRREP = FALSE;
+ busyProcess = FALSE;
+ busyIssue = FALSE;
+ busySend = FALSE;
+ buf_target.address = 0;
+
+#ifdef DYMO_MONITORING
+ rreq_time = 0;
+#endif
+
+ post startDoneTask();
+ return SUCCESS;
+ }
+
+ void incr_seqnum(){
+ if(me.seqnum == 65535)
+ me.seqnum = 256;
+ else
+ me.seqnum++;
+ }
+
+ /* Send a RREQ for buf_info */
+ task void issueRREQ(){
+ atomic {
+ if(busySend)
+ post issueRREQ();
+ else {
+ busySend = TRUE;
+ incr_seqnum();
+ call DymoPacket.createRM(&buf_packet, DYMO_RREQ, &me, &buf_info);
+ call AMSend.send(AM_BROADCAST_ADDR, &buf_packet, call DymoPacket.getSize(&buf_packet));
+ }
+ }
+ }
+
+ /* Send a RREP to buf_info */
+ task void issueRREP(){
+ atomic {
+ if(busySend)
+ post issueRREP();
+ else {
+ busySend = TRUE;
+ call DymoPacket.createRM(&buf_packet, DYMO_RREP, &me, &buf_info);
+ if(buf_target.address)
+ call DymoPacket.addInfo(&buf_packet, &buf_target);
+ call AMSend.send(buf_info.nexthop, &buf_packet, call DymoPacket.getSize(&buf_packet));
+ buf_target.address = 0;
+ }
+ }
+ }
+
+ /* Send a RERR with buf_info as unreachable */
+ task void issueRERR(){
+ atomic {
+ if(busySend)
+ post issueRERR();
+ else {
+ busySend = TRUE;
+ call DymoPacket.createRM(&buf_packet, DYMO_RERR, NULL, &buf_info);
+ call AMSend.send(AM_BROADCAST_ADDR, &buf_packet, call DymoPacket.getSize(&buf_packet));
+ }
+ }
+ }
+
+ /* Send current fw_msg to fw_address */
+ task void forward(){
+ atomic {
+ if(busySend)
+ post forward();
+ else {
+ busySend = TRUE;
+ call AMSend.send(fw_address, &fw_msg, call DymoPacket.getSize(&fw_msg));
+ }
+ }
+ }
+
+ event message_t* Receive.receive(message_t* msg, void* payload, uint8_t len){
+#ifdef DYMO_MONITORING
+ signal DymoMonitor.msgReceived(msg);
+#endif
+ dbg("de", "DE: Message (type %hhu) received.\n", call DymoPacket.getType(msg));
+ atomic {
+ if(busyProcess){
+ dbg("de", "DE: I'm busy, I can't handle this message, sorry.\n");
+ return msg; //we discard msg if a message is already being processed
+ } else {
+ busyProcess = TRUE;
+ }
+ }
+ cur_info_pos = 0;
+ fw_address = AM_BROADCAST_ADDR;
+ call DymoPacket.startProcessing(msg, &fw_msg);
+ return avail_msg;
+ }
+
+ event proc_action_t DymoPacket.hopsProcessed(message_t * msg, uint8_t hop_limit, uint8_t hop_count){
+ cur_hopcnt = hop_count; //TODO use this
+ if(hop_limit == 0){
+ fw_address = 0;
+ dbg("de", "DE: This message has reached its HL (%hhu hops) => discard.\n", hop_count);
+ return ACTION_DISCARD_MSG;
+ } else {
+ return ACTION_KEEP;
+ }
+ }
+
+ proc_action_t process_rm_info(message_t * msg, rt_info_t * info){
+ cur_info_pos++;
+ if(cur_info_pos == 1){ //target
+
+ if(info->address == me.address){
+
+ if(call DymoPacket.getType(msg) == DYMO_RREQ){
+ dbg("de", "DE: This RREQ is for me => RREP.\n");
+ if(info->seqnum < me.seqnum)
+ incr_seqnum();
+ dbg("de", "DE: My seqnum for the RREP: %u.\n", me.seqnum);
+ sendRREP = TRUE; //to send a RREP when we receive the next event (= originator info)
+ } else {
+ dbg("de", "DE: This RREP is for me, cool!\n");
+ }
+ fw_address = 0;
+ return ACTION_DISCARD_MSG;
+
+ } else { //not for me
+
+ info->nexthop = call AMPacket.source(msg);
+ if(call DymoPacket.getType(msg) == DYMO_RREQ){
+
+#if DYMO_INTER_RREP
+ //if we know a route to the target, we send a intermediate RREP and don't forward the message
+ ignoreNeeded = info->address;
+ if (call RoutingTable.getRoute(info->address, &buf_info) == SUCCESS) {
+#if DYMO_FORCE_INTER_RREP
+ if( !info->seqnum || !(call DymoTable.isSuperior(info, DYMO_RREQ)) ){
+#else
+ if( info->seqnum && !(call DymoTable.isSuperior(info, DYMO_RREQ)) ){
+#endif
+ dbg("de", "DE: This RREQ is for %u, but I know the route => RREP.\n", info->address);
+ dbg("de", "DE: My seqnum for the RREP: %u.\n", me.seqnum);
+ buf_target = buf_info;
+ sendRREP = TRUE;
+ fw_address = 0;
+ return ACTION_DISCARD_MSG;
+ }
+ }
+#endif
+ return ACTION_KEEP;
+
+ } else { //RREP
+
+ ignoreNeeded = info->address;
+ dbg("de", "DE: This RREP is for %u.\n", info->address);
+ if(call RoutingTable.getForwardingRoute(info->address, &buf_info) == SUCCESS){
+ fw_address = buf_info.nexthop;
+ return ACTION_KEEP;
+ } else {
+ fw_address = 0;
+ return ACTION_DISCARD_MSG;
+ }
+
+ }//end RREP
+
+ }//end not for me
+
+ } else if((call DymoPacket.getType(msg) == DYMO_RREQ) //end if(info==target)
+ && (cur_info_pos == 2)
+ && (info->address == me.address)){
+
+ fw_address = 0;
+ sendRREP = FALSE;
+ return ACTION_DISCARD_MSG;
+
+ } else {
+
+ info->nexthop = call AMPacket.source(msg);
+ if(call DymoTable.update(info, call DymoPacket.getType(msg)) == EINVAL){
+
+ if(cur_info_pos == 2){ //origin
+ dbg("de", "DE: I am discarding a msg with a bad origin (%u-%u-%hhu)\n", info->address, info->seqnum, info->hopcnt);
+ fw_address = 0;
+ return ACTION_DISCARD_MSG;
+ } else { //Additional info
+ dbg("de", "DE: I am discarding a bad piece of info (%u-%u-%hhu)\n", info->address, info->seqnum, info->hopcnt);
+ return ACTION_DISCARD;
+ }
+
+ } else {
+
+ if((cur_info_pos == 2) && sendRREP){
+ buf_info = *info;
+ atomic {
+ if(!busyIssue){
+ busyIssue = 1;
+ post issueRREP();
+ }
+ }
+ sendRREP = 0;
+ }
+
+#ifdef DYMO_MONITORING
+ if( rreq_time //TODO probably misses a test
+ && (cur_info_pos == 2)
+ && (call DymoPacket.getType(msg) == DYMO_RREP) ) {
+ rreq_time = (call Timer.getNow()) - rreq_time;
+ signal DymoMonitor.routeDiscovered(rreq_time, info->address);
+ rreq_time = 0;
+ }
+#endif
+ return ACTION_KEEP;
+
+ }
+
+ } //end info!=target
+ }//end event
+
+ proc_action_t process_err_info(message_t * msg, rt_info_t * info){
+ info->nexthop = call AMPacket.source(msg);
+ if(call DymoTable.update(info, call DymoPacket.getType(msg)) == EINVAL){
+ return ACTION_DISCARD;
+ } else {
+ cur_info_pos++; //we only count kept pieces of info
+ return ACTION_KEEP;
+ }
+ }
+
+ event proc_action_t DymoPacket.infoProcessed(message_t * msg, rt_info_t * info){
+ if(call DymoPacket.getType(msg) == DYMO_RERR)
+ return process_err_info(msg, info);
+ else
+ return process_rm_info(msg, info);
+ }
+
+ event void DymoPacket.messageProcessed(message_t * msg){
+ avail_msg = msg;
+ if( (call DymoPacket.getType(msg) == DYMO_RERR) && cur_info_pos ){
+
+ post forward();
+
+ } else if( (call DymoPacket.getType(msg) != DYMO_RERR) && fw_address ){
+
+#if DYMO_APPEND_INFO
+ call DymoPacket.addInfo(&fw_msg, me);
+#endif
+ dbg("de", "DE: I'll forward this RM.\n");
+ post forward();
+
+ } else {
+
+ atomic {
+ busyProcess = 0;
+ }
+ dbg("de", "DE: I'm not busy anymore.\n");
+
+ }
+ dbg("de", "DE: Message (type %hhu) successfully processed.\n", call DymoPacket.getType(msg));
+ }
+
+ event void DymoTable.routeNeeded(addr_t destination){
+ if(ignoreNeeded == destination){
+ ignoreNeeded = 0;
+ } else {
+ buf_info.address = destination;
+ buf_info.seqnum = 0;
+ buf_info.has_hopcnt = FALSE;
+ atomic {
+ if(!busyIssue){
+ busyIssue = TRUE;
+#ifdef DYMO_MONITORING
+ rreq_time = call Timer.getNow();
+#endif
+ post issueRREQ();
+ }
+ }
+ }
+ }
+
+ event void DymoTable.brokenRouteNeeded(const rt_info_t * route_info){
+ buf_info = *route_info;
+ buf_info.has_hopcnt = FALSE;
+ atomic {
+ if(!busyIssue){
+ busyIssue = TRUE;
+ post issueRERR();
+ }
+ }
+ }
+
+ event void RoutingTable.evicted(const rt_info_t * route_info, reason_t r){
+ if(r == REASON_UNREACHABLE){
+ buf_info = *route_info;
+ buf_info.has_hopcnt = FALSE;
+ atomic {
+ if(!busyIssue){
+ busyIssue = TRUE;
+ post issueRERR();
+ }
+ }
+ }
+ }
+
+ event void AMSend.sendDone(message_t *msg, error_t error){
+ atomic {
+ busySend = FALSE;
+ }
+ if(msg == &fw_msg){
+ atomic{
+ busyProcess = FALSE;
+ }
+ } else if(msg == &buf_packet) {
+ atomic {
+ busyIssue = FALSE;
+ }
+ }
+
+ if(error == SUCCESS){
+ if(msg == &fw_msg)
+ dbg("de", "DE: Message (type %hhu) forwarded.\n", call DymoPacket.getType(msg));
+ else
+ dbg("de", "DE: Message (type %hhu) sent.\n", call DymoPacket.getType(msg));
+ } else
+ dbg("de", "DE: Failed to send message (type %hhu).\n", call DymoPacket.getType(msg));
+
+#ifdef DYMO_MONITORING
+ if(error == SUCCESS)
+ signal DymoMonitor.msgSent(msg);
+#endif
+ }
+
+ command error_t SplitControl.stop(){ }
+
+#ifdef DYMO_MONITORING
+
+ event void Timer.fired(){}
+
+ default event void DymoMonitor.msgReceived(message_t * msg){}
+
+ default event void DymoMonitor.msgSent(message_t * msg){}
+
+ default event void DymoMonitor.routeDiscovered(uint32_t delay, addr_t target){}
+
+#endif
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+/**
+ * DymoServiceC - Implements the DYMO routing protocol This is the
+ * simulator version, without persistent storage of the sequence
+ * number.
+ *
+ * @author Romain Thouvenin
+ */
+
+configuration DymoServiceC {
+ provides {
+ interface SplitControl;
+ }
+ uses {
+ interface Packet;
+ interface AMPacket;
+ interface AMSend;
+ interface Receive;
+ interface LinkMonitor;
+ }
+
+#ifdef DYMO_MONITORING
+ provides {
+ interface DymoMonitor;
+ }
+#endif
+}
+
+implementation {
+ components DymoTableC, DymoEngineM, DymoPacketM;
+
+ SplitControl = DymoEngineM.SplitControl;
+ Packet = DymoPacketM.Packet;
+ AMPacket = DymoEngineM.AMPacket;
+ AMSend = DymoEngineM.AMSend;
+ Receive = DymoEngineM.Receive;
+ LinkMonitor = DymoTableC.LinkMonitor;
+
+ DymoEngineM.DymoPacket -> DymoPacketM;
+ DymoEngineM.RoutingTable -> DymoTableC;
+ DymoEngineM.DymoTable -> DymoTableC;
+
+#ifdef DYMO_MONITORING
+ components new TimerMilliC();
+
+ DymoMonitor = DymoEngineM.DymoMonitor;
+ DymoEngineM.Timer -> TimerMilliC;
+#endif
+}
--- /dev/null
+interface MHControl {
+
+ event void msgReceived(message_t * msg);
+
+ event void sendFailed(message_t * msg, uint8_t why);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+#include "routing.h"
+
+/**
+ * MHEngineM - Implements a simple transport protocol, which is
+ * nothing more than AM on top of the existing AM stack.
+ *
+ * @author Romain Thouvenin
+ */
+module MHEngineM {
+ provides interface RouteSelect;
+ uses {
+ interface AMPacket as MHPacket;
+ interface AMPacket;
+ interface RoutingTable;
+ }
+}
+
+implementation {
+
+ rt_info_t info;
+
+ command fw_action_t RouteSelect.selectRoute(message_t * msg, addr_t * destination, uint8_t * am_type){
+ dbg("mhe", "MHE: Somebody wants a route, let's see...\n");
+ if( call MHPacket.isForMe(msg)
+ || (destination && (*destination == call MHPacket.address())) ){
+
+ *am_type = call MHPacket.type(msg);
+ return FW_RECEIVE;
+
+ } else {
+
+ error_t e;
+ if(destination)
+ e = call RoutingTable.getRoute(*destination, &info);
+ else
+ e = call RoutingTable.getForwardingRoute(call MHPacket.destination(msg), &info);
+
+ if(e == SUCCESS){
+
+ dbg("mhe", "MHE: I've selected a route to %u through %u.\n", info.address, info.nexthop);
+ call AMPacket.setDestination(msg, info.nexthop);
+
+ if(destination){
+ call MHPacket.setType(msg, *am_type);
+ call MHPacket.setDestination(msg, *destination);
+ call MHPacket.setSource(msg, call MHPacket.address());
+ } else {
+ *am_type = call MHPacket.type(msg);
+ }
+ return FW_SEND;
+
+ } else if(e == EBUSY){
+ dbg("mhe", "MHE: No route is available for now.\n");
+ return FW_WAIT;
+ } else {
+ dbg("mhe", "MHE: I'm discarding the message.\n");
+ return FW_DISCARD;
+ }
+
+ }
+ }
+
+ event void RoutingTable.evicted(const rt_info_t * rt_info, reason_t r){}
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+#include "mhpacket.h"
+
+#define HEADER ((mhpacket_header_t *)(call SubPacket.getPayload(amsg, call SubPacket.maxPayloadLength())))
+
+/**
+ * MHPacketM - Implements ActiveMessage on top of ActiveMessage,
+ * to transport data in a multihop network.
+ *
+ * @author Romain Thouvenin
+ */
+
+module MHPacketM {
+ provides {
+ interface Packet;
+ interface AMPacket as MHPacket;
+ }
+ uses {
+ interface Packet as SubPacket;
+ interface AMPacket;
+ }
+}
+
+implementation {
+
+ /**********
+ * Packet *
+ **********/
+
+ command void Packet.clear(message_t *msg){
+ call SubPacket.clear(msg);
+ }
+
+ command void * Packet.getPayload(message_t *msg, uint8_t len){
+ nx_uint8_t * p = call SubPacket.getPayload(msg, len);
+ return (void *)(p + sizeof(mhpacket_header_t));
+ }
+
+ command uint8_t Packet.maxPayloadLength(){
+ return call SubPacket.maxPayloadLength() - sizeof(mhpacket_header_t);
+ }
+
+ command uint8_t Packet.payloadLength(message_t *amsg){
+ return HEADER->len;
+ }
+
+ command void Packet.setPayloadLength(message_t *amsg, uint8_t len){
+ HEADER->len = len;
+ call SubPacket.setPayloadLength(amsg, len + sizeof(mhpacket_header_t));
+ }
+
+
+ /**********
+ * AMPacket *
+ **********/
+
+ command am_addr_t MHPacket.address(){
+ return call AMPacket.address();
+ }
+
+ command am_addr_t MHPacket.destination(message_t *amsg){
+ return HEADER->dest;
+ }
+
+ command bool MHPacket.isForMe(message_t *amsg){
+ return ((HEADER->dest == call MHPacket.address()) || (HEADER->dest == AM_BROADCAST_ADDR));
+ }
+
+ command void MHPacket.setDestination(message_t *amsg, am_addr_t addr){
+ HEADER->dest = addr;
+ }
+
+ command void MHPacket.setSource(message_t *amsg, am_addr_t addr){
+ HEADER->src = addr;
+ }
+
+ command void MHPacket.setType(message_t *amsg, am_id_t t){
+ HEADER->type = t;
+ call AMPacket.setType(amsg, AM_MULTIHOP);
+ }
+
+ command am_addr_t MHPacket.source(message_t *amsg){
+ return HEADER->src;
+ }
+
+ command am_id_t MHPacket.type(message_t *amsg){
+ return HEADER->type;
+ }
+
+ /* *** UNIMPLEMENTED ! *** */
+ //TODO what to do with this?
+
+ command am_group_t MHPacket.group(message_t* amsg) {
+ return 0;
+ }
+
+ command void MHPacket.setGroup(message_t* amsg, am_group_t grp) { }
+
+ command am_group_t MHPacket.localGroup() {
+ return 0;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+/**
+ * MHServiceC - Implements a simple multihop transport protocol
+ *
+ * @author Romain Thouvenin
+ */
+
+configuration MHServiceC {
+ provides { //For upper layer
+ interface AMSend as MHSend[uint8_t id];
+ interface Receive[uint8_t id];
+ interface Intercept[uint8_t id];
+ interface AMPacket as MHPacket;
+ interface Packet;
+ interface LinkMonitor;
+ }
+ uses { //From lower layer
+ interface AMPacket;
+ interface Packet as SubPacket;
+ interface AMSend;
+ interface Receive as SubReceive;
+ interface PacketAcknowledgements as Acks;
+ }
+
+ provides interface MHControl;
+}
+
+implementation {
+ components DymoTableC, MHEngineM, MHPacketM;
+ components new ForwardingEngineM(), new TimerMilliC();
+
+ //provides
+ MHSend = ForwardingEngineM.AMSend;
+ Receive = ForwardingEngineM.Receive;
+ Intercept = ForwardingEngineM.Intercept;
+ LinkMonitor = ForwardingEngineM.LinkMonitor;
+ MHPacket = MHPacketM.MHPacket;
+ Packet = MHPacketM.Packet;
+ Acks = ForwardingEngineM.Acks;
+
+ //uses
+ ForwardingEngineM.AMPacket = AMPacket;
+ MHEngineM.AMPacket = AMPacket;
+ MHPacketM.AMPacket = AMPacket;
+ MHPacketM.SubPacket = SubPacket;
+ ForwardingEngineM.SubPacket = SubPacket;
+ ForwardingEngineM.SubSend = AMSend;
+ ForwardingEngineM.SubReceive = SubReceive;
+
+ //MHEngine
+ MHEngineM.MHPacket -> MHPacketM.MHPacket;
+ MHEngineM.RoutingTable -> DymoTableC;
+
+ //ForwardingEngine
+ ForwardingEngineM.RouteSelect -> MHEngineM;
+ ForwardingEngineM.PPacket -> MHPacketM.Packet;
+ ForwardingEngineM.Timer -> TimerMilliC;
+
+ MHControl = ForwardingEngineM.MHControl;
+}
--- /dev/null
+#ifndef MHPACKET_H
+#define MHPACKET_H
+
+#include "AM.h"
+#include "message.h"
+#include "routing.h"
+
+typedef nx_struct mhpacket_header {
+ nx_uint8_t len;
+ nx_uint8_t type;
+ nx_am_addr_t src;
+ nx_am_addr_t dest;
+} mhpacket_header_t;
+
+typedef nx_struct mhpacket {
+ mhpacket_header_t header;
+ nx_uint8_t data[];
+} mhpacket_t;
+
+enum { //for mig
+ AM_MHPACKET = AM_MULTIHOP,
+};
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+#ifndef _DYMO_ROUTING_H_
+#define _DYMO_ROUTING_H_
+
+#include "AM.h"
+
+typedef am_addr_t addr_t;
+typedef nx_am_addr_t nx_addr_t;
+typedef uint16_t seqnum_t;
+typedef nx_uint16_t nx_seqnum_t;
+
+#ifndef MAX_TABLE_SIZE
+#define MAX_TABLE_SIZE 5
+#endif
+
+#ifndef DYMO_HOPLIMIT
+#define DYMO_HOPLIMIT 10
+#endif
+
+#ifndef DYMO_ROUTE_AGE_MAX
+#define DYMO_ROUTE_AGE_MAX 300000
+#endif
+
+#ifndef DYMO_ROUTE_TIMEOUT
+#define DYMO_ROUTE_TIMEOUT 10000
+#endif
+
+#ifndef DYMO_APPEND_INFO
+#define DYMO_APPEND_INFO 0 //1 to append info to forwarded RMs
+#endif
+
+#ifndef DYMO_INTER_RREP
+#define DYMO_INTER_RREP 1 //1 to allow intermediate RREP
+#endif
+
+#ifndef DYMO_FORCE_INTER_RREP
+#define DYMO_FORCE_INTER_RREP 1 //1 to send intermediate RREP even without target's seqnum in the RREQ
+#endif
+
+#ifndef DYMO_LINK_FEEDBACK
+#define DYMO_LINK_FEEDBACK 1 //1 to use acks to detect broken links
+#endif
+
+enum {
+ AM_MULTIHOP = 9,
+ AM_DYMO = 8
+};
+
+typedef enum {
+ DYMO_RREQ = 10,
+ DYMO_RREP,
+ DYMO_RERR
+} dymo_msg_t;
+
+//processing action
+typedef enum {
+ ACTION_KEEP, //info is kept in the forwarded message
+ // ACTION_UPDATE, //info is kept, and updated with the provided info
+ ACTION_DISCARD, //info is not kept in the forwarded message
+ ACTION_DISCARD_MSG //The message won't be forwarded, no need to build a forwarded message anymore
+} proc_action_t;
+
+typedef enum {
+ FW_SEND, //Put the message in the sending queue
+ FW_RECEIVE, //Give the message to the upper layer
+ FW_WAIT, //Retry later
+ FW_DISCARD, //Discard the message
+} fw_action_t;
+
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2007 Romain Thouvenin <romain.thouvenin@gmail.com>
+ * Published under the terms of the GNU General Public License (GPLv2).
+ */
+
+#ifndef _ROUTING_TABLE_H_
+#define _ROUTING_TABLE_H_
+
+#include "routing.h"
+
+/**
+ * Types associated to a routing table.
+ */
+
+typedef struct rt_info {
+ addr_t address;
+ addr_t nexthop;
+ seqnum_t seqnum;
+ bool has_hopcnt;
+ uint8_t hopcnt;
+} rt_info_t;
+
+typedef enum {
+ REASON_FULL,
+ REASON_OLD,
+ REASON_UNREACHABLE
+} reason_t;
+
+#endif