You are on page 1of 85

1.

DIRECTORIO APPS
readme txt

A number of applications using the 6lowpan library:

- reply: A sample application using the network protocol; It sends


back any data received on a socket port.

- sense-1: As soon as a mote is attached to the network it starts


sensing data and sending it back to the edge mote.

- sense-2: As soon as a mote is attached to the network it starts


sensing data and sending it back to the edge mote. A packet filter
is used to merge packets on their way to the root.

1.1 DIRECTORIO REPLY

Readme.txt
Files:
======

- reply.cs: Source of the assembly downloaded on a mote.


It binds a socket to port 1024 and sends any packets received
back to the source address.

- reply.js: socket for mrsh

- sim-w.mrsh, sim-wl.mrsh:
Scripts to create network with motes running 'reply'
Use: sonoran -i ./reply.mrsh 3 2

Getting started:
================

Use "make clean; make" to build 'reply'. Make sure that you also
compiled and installed the network library in 'src' and the tunnel
in 'tunnel':

Start the 'tunnel' and setup the IPv6 routes.

Run "sonoran -i reply.mrsh 3 2".

Where 3 is total number of motes, 2 the number of motes a parent


accepts as children in the network tree.

The script installs three motes in a simulation, one edge mote


and two wireless mote. Assemblies are loaded and the protocol
started on both motes. At some point, the shell prints:

Node added: 02-00-00-00-00-AE-2F-00 : 0


Node added: 02-00-00-00-00-AE-2F-01 : 2 : 02-00-00-00-00-AE-2F-00
Node added: 02-00-00-00-00-AE-2F-02 : 3 : 02-00-00-00-00-AE-2F-00

The mote is attached to the network. Now, use the socket RS from
reply.js to send 10 bytes to a mote (to port 1024).

> a1 sock-send RS 1024 10

ReplySock: 02-00-00-00-00-AE-2F-02, 1024, 00010203040506070809

And ten bytes get back from the mote.

IP tunneling:
=============

The script uses the command "v6l-tunnel" to setup a virtual IP


tunnel. By default, wirless motes are reachable for IPv6 messages
at fc00:db8:5::<eui64-of-mote> (see ../tunnel/readme.txt)

> ping6 fc00:db8:5::0200:0000:00AE:2F01


PING6(56=40+8+8 bytes) fc00:db8:5::ff -->
fc00:db8:5::200:0:ae:2f01
16 bytes from fc00:db8:5::200:0:ae:2f01, icmp_seq=0 hlim=64
time=77.576 ms
16 bytes from fc00:db8:5::200:0:ae:2f01, icmp_seq=1 hlim=64
time=71.139 ms

> ping6 fc00:db8:5::0200:0000:00AE:2F05


PING6(56=40+8+8 bytes) fc00:db8:5::ff -->
fc00:db8:5::200:0:ae:2f05
C-c C-c
--- fc00:db8:5::0200:0000:00AE:2F05 ping6 statistics ---
4 packets transmitted, 0 packets received, 100.0% packet loss

Use IPv6 to send/receive packets:


=================================

Use nc6 (netcat6 package on Ubuntu) to send/receive UDPv6 packets


from a mote.

Start nc6 in UDP-mode and enter data on stdin. nc6 sends a UDP
packet to the specified address on the specified port. It is
received by the tunnel interface and forwarded to the mote. The
response is fed as IPv6 packet back into the tunnel interface.

> nc6 -u fc00:db8:5::0200:0000:00AE:2F01 1024

nc6: using datagram socket


10
10
2020
2020

Compile and run Java client:


============================

Reply.java uses the java host library to query the wireless


network and send UDP v6 packets to the motes.

Compile with: 'make java'

Run with:
java -classpath "../lib/java/v6lowpan.jar:." Reply

Output similar to:

Reply: connected to edge.


Reply: new node: Node: fc00:db8:5::0200:0000:00ae:2f00, 0,
fc00:db8:5::0000:0000:0000:0000

New: Node: fc00:db8:5::0200:0000:00ae:2f00, 0,


fc00:db8:5::0000:0000:0000:0000
UNPACK: 0200000000ae2f01, 256, 0200000000ae2f00

Reply: new node: Node: fc00:db8:5::0200:0000:00ae:2f01, 256,


fc00:db8:5::0200:0000:00ae:2f00

New: Node: fc00:db8:5::0200:0000:00ae:2f01, 256,


fc00:db8:5::0200:0000:00ae:2f00
UNPACK: 0200000000ae2f02, 512, 0200000000ae2f00

Reply: new node: Node: fc00:db8:5::0200:0000:00ae:2f02, 512,


fc00:db8:5::0200:0000:00ae:2f00

New: Node: fc00:db8:5::0200:0000:00ae:2f02, 512,


fc00:db8:5::0200:0000:00ae:2f00
UNPACK: 0200000000ae2f03, 768, 0200000000ae2f01

Reply: new node: Node: fc00:db8:5::0200:0000:00ae:2f03, 768,


fc00:db8:5::0200:0000:00ae:2f01
New: Node: fc00:db8:5::0200:0000:00ae:2f03, 768,
fc00:db8:5::0200:0000:00ae:2f01
Worker: send to /fc00:db8:5:0:200:0:ae:2f03: 123400
Worker: received from /fc00:db8:5:0:200:0:ae:2f03%0: 123400
Worker: send to /fc00:db8:5:0:200:0:ae:2f02: 123401

1.2 DIRECTORIO SENSE-1


readme.txt

Files:
======

- sense.cs: Source of the assembly downloaded on a mote.


It binds a socket to port 1024 and sends periodically
sensor data to the edge.

- sense.js: Scripts to generate a number of simulated motes,


install assemblies and start the network

Getting started:
================

Use "make clean; make" to build 'sense'.

Start the network with:

sonoran -i ./sense.mrsh 3 2

where 3 is the total number of motes and 2 the number of motes a


parent mote may accept.

Output similar to:

Packet from mote to mote:


02-00-00-00-00-AE-2F-01 -> 02-00-00-00-00-AE-2F-00

UDP: source:0|0|1|0:012FAE0000000002 dest:0|0|1|0:002FAE0000000002


srcport:1023 dstport:1023 payload:0041

Packet from mote to mote:


02-00-00-00-00-AE-2F-01 -> 02-00-00-00-00-AE-2F-00

UDP: source:0|0|1|0:012FAE0000000002 dest:0|0|1|0:002FAE0000000002


srcport:1023 dstport:1023 payload:0046

To change the value for the ADC readings in the simulation for
particular mote (e.g., a1) use a command like:
a1 feed-start ADC0 [125,"freeze"]

And to read/check the current state of the ADC in the simulation


use:

a1 device-get-state ADC0

1.3 DIRECTORIO SENSE-2

readme.txt

Files:
======

- sense.cs: Source of the assembly downloaded on a mote.


It binds a socket to port 1024 and sends periodically
sensor data to the edge.

- sense.js: Scripts to generate a number of simulated motes,


install assemblies and start the network

Getting started:
================

Use "make clean; make" to build 'sense'.

Start the network with:

sonoran -i ./sense.mrsh 3 2

where 3 is the total number of motes and 2 the number of motes a


parent mote may accept.

The sensor values are written to the file "output.txt":


Packet from mote 02-00-00-00-00-AE-2F-01: Thu Jan 01 1970 01:17:47
GMT+0100 (CET)
0002: adc1 ff03, adc2 ff03
0001: adc1 ff03, adc2 ff03

Packet merging:
===============

sense.cs uses Mac.addPacketHandler() to install a packet handler.


When packets from children are received, their data is copied to
an internal buffer and the packet dropped. When a node has sensed
a value, it sends off a packet including the child packets.

Control flow command:


=====================

'v6-conn --suspend' delivers a broadcast message to all motes. the


event handler then stops sensing and sending packets.

'v6-conn --resume' resumes the network.

2. DIRECTORIO JAVA
Readme.txt

Files:
======

- com: the package com/ibm/bluez/v6lowpan contains the basic Java


library files to connect to the edge mote and query the network

- sample: sample/Reply uses the Java library to send and receive


packets from a network running the Reply sample

Getting started:
================

Run "make clean; make" to build the class files.

Start the reply network as described in "../reply/readme.txt"


for the simulation.

The network and edge address is

Run the Java sample with: java -classpath . sample.Reply

Output is as follows:

Reply: connected to edge.


Reply: new node: Node: fc00:db8:5::0200:0000:00ae:2f00, 0,
fc00:db8:5::0000:0000:0000:0000
New: Node: fc00:db8:5::0200:0000:00ae:2f00, 0,
fc00:db8:5::0000:0000:0000:0000
Reply: new node: Node: fc00:db8:5::0200:0000:00ae:2f01, 256,
fc00:db8:5::0200:0000:00ae:2f00
New: Node: fc00:db8:5::0200:0000:00ae:2f01, 256,
fc00:db8:5::0200:0000:00ae:2f00
Reply: new node: Node: fc00:db8:5::0200:0000:00ae:2f02, 512,
fc00:db8:5::0200:0000:00ae:2f00
New: Node: fc00:db8:5::0200:0000:00ae:2f02, 512,
fc00:db8:5::0200:0000:00ae:2f00

Worker: send to /fc00:db8:5:0:200:0:ae:2f02: 123400


Worker: received from /fc00:db8:5:0:200:0:ae:2f02%0: 123400
Worker: send to /fc00:db8:5:0:200:0:ae:2f01: 123401
Worker: received from /fc00:db8:5:0:200:0:ae:2f01%0: 123401
Worker: send to /fc00:db8:5:0:200:0:ae:2f02: 123402
Worker: received from /fc00:db8:5:0:200:0:ae:2f02%0: 123402

Subdirectorio dentro de directorio java

com.ibm.bluez.mrv6

binutils.java

connection.java

defs.java

eventhandler.java

info.java

node.java

3. DIRECTORIO BIN

Archivo mrv6.js
Basic MRv6 module functionality

4. DIRECTORIO SCRIPTS
5. DIRECTORIO SRC
Readme.txt

Contents:

0) Files
1) Building
2) Getting started with the simulation
3) Sonoran-Commands
4) Configuration Parameters
5) MOMA-Commands
6) Virtual IP interface
7) Sample session with hardware motes
8) Protocol details
9) Implementation details

0) Files:
=========

- util.cs: Utility functions

- buffers.cs: Library to allocate and release buffers backed by


one big byte array.

- v6lowpan.cs: Definitions for a subset of v6lowpan compatible


packet formats and v6lowpan address formats

- gen-defs.js, defs.h, defs.js, defs.cs:

gen-defs.js defines protocol constants and generates files for


the different programming languages

- config.cs: persistent configuration settings for the protocol

- mac.cs, slot.cs, packet.cs, child.cs, parent.cs: Network


protocol implementation, lower layers

- node.cs: Manager of the mac and network protocol on wireless


motes

- edge.cs: Manager of the network protocol on the edge mote, keeps


track of the whole network tree, manages
associations/disassociations and status messages

- socket.cs: Mote Runner assemblies use Socket instances to


receive v6lowpan UDP messages
1) Building:
============

> make clean; make

builds and installs the library 'mrv6-lib' in the GAC. The


assembly 'mrv6-edge' for the edge is also built.

> make doc

generates API doc in gac/mrv6-lib-1.0.htm.

2) Getting started with the simulation


======================================

Start the shell, create 3 motes and upload the assembly.

> mote-create 3
> a0 moma-load mrv6-edge
> a1 a2 moma-load mrv6-lib

Include the v6lowpan Javascript functionality in Sonoran:


> source ../scripts/init.mrsh

Switch on logic-time, a lot faster on the simulation:

> sag-cont -m logic-time

Attach, configure and start the edge mote, specify the number of
motes in the network:

> a0 v6-setup --MAX_MOTES=4

This prints out the current configuration of the edge mote


and a number of log events.

Now start the 6lowpan protocol on the other, non-edge motes:


> a1 a2 v6-start

Log messages appear which show new motes attaching to the edge.

When issuing a MOMA-command 'mrv6' is used as transport


protocol.

> w1 moma-list

Shows the assembly listing.

> v6-conn
Mote Parent Address Hops
-----------------------------------------------------------------
02-00-00-00-00-6B-64-00 Gateway 0 0
02-00-00-00-00-6B-64-01 02-00-00-00-00-6B-64-00 1 1
02-00-00-00-00-6B-64-02 02-00-00-00-00-6B-64-00 2 1

Shows the 6lowpan network.

3) Sonoran-Commands
===================

a0 v6-setup

Downloads edge assembly, starts the network protocol and connects


to the virtual IP tunnel (see ../reply/readme.txt).

v6-connect

Prints the network with child-parent-relationships.

a0 v6-connect -a

Attaches to an edge mote (with the edge assembly installed and


possibly already running).

a0 v6-connect -d
Detaches from an edge mote (with the edge assembly installed and
possibly already running).

a0 v6-start

Start the procotol on a mote.

a1 v6-start

'v6-start' on any mote not being the edge mote leads to the
protocol started on that mote as wireless mote (even if connected
by LIP). This is useful in case of the simulation.

a0 v6-config

Read or set persistent configuration. The persistent configuration


should be set before the protocol is started. Also, certain
configuration parameters are inherited from the edge mote from
each wireless mote.

... v6-info

Prints the info packet sent by the mote periodically to deliver


statistics.

4) Configuration Parameters
===========================

Most important configuration parameters are:


--MAX_MOTES: Maximum number of motes supported by edge mote;
this is the number of motes you want to use in the network.
--BUFFERS_SIZE: Size of byte array preallocated for Buffer
instances
--MAX_CHILDREN: Size of children table a mote preallocates
--NUM_CHILDREN: Number of children a mote accepts (inherited from
edge mote)
--MAX_DEPTH: Maximum tree depth

5) MOMA-Commands
================

MOMA-packets are routed through the mrv6 protocol. However,


the following mrsh variables shuld be adjusted to allow for larger
timeouts in case of MOMA commands.

option Sonoran.MOMA.DFLT_LOADER_CHUNKSIZE 60
option Sonoran.MOMA.DFLT_TIMEOUT 30000

Then, you can use the moma commands such as 'moma-list' or 'moma-
info'.

6) Virtual IP interface
=======================

The command "v6-tunnel" connects to the 'tunnel' program and uses


the
virtual IPv6 tunnel to route IPv6 packets between motes and the
kernel.
See "../tunnel/readme.txt" for how to start the tunnel process.

When the tunnel is active, Sonoran receives the IPv6 packets from
the kernel and forwards them to the edge mote. The edge mote
forwards the packets and the packet receives at some point the
destination mote with the full IPv6 address.
Any packet from wireless motes with a full IPv6 destintaion
address ends up at the edge mote and is forwarded to the tunnel
interface.
7) Sample session with hardware motes
=====================================

sonoran -i ../scripts/init.mrsh
> mo-cr -p /dev/tty.usbserial-XBSEJ8WNB
02-00-00-00-48-81-60-35
> moma-load mrv6-edge

Download edge assembly and start the protocol:


> l0 v6-set
mrv6.Connection:02-00-00-00-48-81-60-35

> v6-conn
Mote Parent Address Hops
-------------------------------------------------
02-00-00-00-48-81-60-35 Gateway 0 0

> v6-conf
R24_SLOT_RCV_MILLIS 10
R24_SLOT_GAP_MILLIS 15
R24_BEACON_GAP_MILLIS 20
R868_SLOT_RCV_MILLIS 100
R868_SLOT_GAP_MILLIS 40
R868_BEACON_GAP_MILLIS 40
RADIO_SELECT 0
BEACON_INTERVAL_MILLIS 0
PANID 12816
CHANNEL 1
LIFESIGN_INTERVAL_CNT 10
SYNC_INTERVAL_CNT 0
INFO_INTERVAL_CNT 0
BEACON_SCAN_RANGE_SECS 120
BEACON_SCAN_RESTART_SECS 60
BUFFERS_SIZE 384
BUFFERS_CNT 8
MAX_MOTES 8
MAX_DEPTH 4
MAX_CHILDREN 16
NUM_CHILDREN 4
CHILD_MISSED_BEACON_LIMIT 10
SLOT_TRANSMISSION_TRIES 5
RECV_SAFETY_MILLIS 3
PACKET_BUFFER_MIN_SIZE 100
EDGE_SCAN_EXISTING 0
EDGE_AUTOSTART 0

If you change settings, you have to restart the protocol on the


edge mote, i.e. reset the mote and restart the protocol.
If you have downloaded the assembly on a wireless mote before,
switch it on. It searches for WLIP and if not found, 'mrv6' gets
active. Thus, make sure that no WLIP gateway is active close by.

After a wireless mote has attached to the edge mote, a log


message appears on the sonoran shell:

Node added: 02-00-00-00-B9-21-D7-19 : 4 : 02-00-00-00-48-81-60-35

'v6-conn' now shows two motes:


> v6-conn
Mote Parent Address Hops
-----------------------------------------------------------------
02-00-00-00-48-81-60-35 Gateway 0 0
02-00-00-00-B9-21-D7-19 02-00-00-00-48-81-60-35 4 1

8) Protocol details
===================

The network protocol builds up a TDMA multi-hop tree of wireless


motes where the whole tree is known to and managed by the edge
mote. Motes use short 802.15.4 addresses for the exchange of radio
messages. Parent and child motes use a superframe for
contentionless communication. Parent motes send a beacon message
to synchronize time with their children and announce the slots in
which the exclusive message exchange with a child takes place. An
additional single shared slot is available for association
messages of new motes and broadcast messages.

When a mote starts the protocol, it listens for a parent mote and
its beacon.
If found, it uses the associaton slot (announced in the beacon) to
request an association. The parent forwards the request to the
edge which may add the mote to the tree and a response is sent to
the mote. If accepted, the mote receives its short address and the
point in time for its super frame (with slots for beacon, shared
and children messages).

When the edge receives a packet for a mote from the host, it
computes its route through the tree which is added to the radio
message to be sent off.

When a packet is received by a mote, is either forwarded according


to the route or delegated to the user application by a socket.
When an user applications sends a packet, it is forwarded to the
edge where the final destination is determined (external ip
address or another internal mote).

Packets are always sent and received in the assigned slots between
parents and children. The time slots are computed and updated from
the beacons sent by parents and received by children. Packets to
be sent off are buffered in a queue at a Slot instance until the
time is due where they are dequeued and sent off. If no
ackknowledgement is received, the
packet is enqued again and the transmission is retried at a later
time.
When a packet is received and it has to be forwarded, it is queued
with the parent Slot instance for a later transmission. If the
packet
is for the mote itself, it is queuedd as well. It is forwarded to
the
application at a later time when the Mac is not busy (with acting
as
child or parent).

Only one packet is exchanged between parent and child in a time


slot.
A parent signals in the beacon if it is about to send a pcket. If
not,
it listens for a message from a child in that slot.

The following details the layout of a superframe:

==================================================================
==========
Beacon Message:
- created in Mac.fillPduBeacon()
- sent time stored in Mac.beaconParentTS
- reception time stored in Mac.beaconChildTS
- time assigned to beacon handling: slotRcvMillis plus
slotGapMillis

u1: Radio.FCF_BEACON
u1: Radio.FCA_SRC_SADDR
u1: Mac.beaconSeqno, sequence number
u2: PAN id
u2: 16-bit source address

After the header, tree and timing information follows. A mote


which wants to
associate but does not know the network yet can compute the
necessary timings
from this information like superframe length, next beacon etc. It
also contains
some basic configuration information for the protocol such as max
number of
children per mote:

u1: Depth of this mote in tree (new mote tries to associate with
a parent
mote which is closest to tree)
4u: Beacon interval, i.e. when next beacon is sent
u2: slotRcvMillis, number of milliseconds to switch on receiver
in a slot
u2: slotGapMillis, number of milliseconds of gap after reception
to handle message
u2: beaconGapMillis, number of milliseconds of gap after last
slot before super frame ends
u1: childSlotLen, number of children slots supported by each
mote in the network

u1: flags:
bit-0: set if parent accepts new motes
bit-1: set if message is sent in shared slot

for each associated child mote:


u1: flags: bit-0: set if message is sent for this mote in this
slot
u2: short address of this mote

==================================================================
==========
Shared slot:
- time assigned to shared slot: slotRcvMillis plus slotGapMillis
- messages exchanged are ASSOCIATION_REQUEST and
ASSOCIATION_RESPONSE
- ASSOCIATION_REQUEST:
u1: Radio.FCF_DATA|Radio.FCF_NSPID|Radio.FCF_ACKRQ;
u1: Radio.FCA_DST_SADDR|Radio.FCA_SRC_XADDR
u2: sequence number 0
u2: PAN id
u2: short address of parent
u8: extended address of mote (its EUI-64)
- ASSOCIATION_RESPONSE:
u1: Radio.FCF_DATA|Radio.FCF_NSPID|Radio.FCF_ACKRQ
u1: Radio.FCA_DST_XADDR|Radio.FCA_SRC_SADDR
u2: sequencenumber
u2: PAN id
u8: extended address of associating mote (its EUI-64)
u2: short address of parent
u1: flags: bit-0: set if association has succeeded
u2: assigned short address
u4: offset in milliseconds of superframe of new mote relative to
parent,
0 if leaf mote without parent role
==================================================================
==========
Child slot 0:
- time assigned to shared slot: slotRcvMillis plus slotGapMillis

==================================================================
==========
...

==================================================================
==========
Child slot N:
- time assigned to shared slot: slotRcvMillis plus slotGapMillis

==================================================================
==========
- Final gap: beaconGapMillis

==================================================================
==========

9) Implementation details
=========================

- buffers.cs:
The class Buffers manages the Buffer instances used by the Mac
to store and
queue messages. The data of Buffer instances is backed by one
big byte array
Buffers.memory, so care has to be taken that the bounds of
Buffer instances are
obeyed. Buffer instances can be queued in BufferQ list
instances.

- config.cs, defs.cs:
Protocol constants, can be modified in gen-defs.js.

- node.cs:
node.cs implements the functionality required for a wireless
mote in the network,
edge.cs for an edge mote.
The Node class is called by the Mac if:
- the protocol is started and stopped
- an association request has been received in the shared slot
- a child has not reacted to a ping message and got lost
- LIP data over the wire has been received
- a v6lowpan message has to be created, i.e. a Packet instance
specifies
source/dest address/port and payload length and the Node
instance must create
a buffer with all necessary headers and room for the payload
which is later
filled in by the application
The Node class also creates 2 v6lowpan Sockets. One to handle
N2E messages
(i.e. messages from the edge mote). When a association response
is received by
the edge, it is forwarded to the Mac. If a status message is
received, the
current child slots are synchronized with the edge mote. A
seconds slot handles
MOMA packets (messages targeting port 0) which are forwarded to
the system.

- edge.cs:
edge.cs also implements a Node class, but for the wired edge
mote. It also uses
a Socket to handle N2E messages from the wireless motes. It
handles TYPE_ASS_REQ
messages (association requests) and TYPE_NODE_LOST (a mote
reports a child loss).
The edge stores the network tree in a list of Knot instances
where
each Knot represents a mote and the first Knot represents the
edge mote.
Among the information per Knot is short address, extended
address, parent etc.

- n2e.cs: constants for the management message exchange between


node and edge

- v6lowpan.cs:
Contains code to handle the v6lowpan message format headers such
as IPHC,
NHC and MHC. It also contains code to handle v6lowpan addresses
where for
now only short address, extended address and full ipv6 address
based v6lowpan
address formats are used.

- socket.cs:
A very simple socket implementation to send and retrieve UDP
messages.
The protocol supports up to 16 sockets on a mote. Messages are
received in
onPacket in a Packet instance which keeps payload and
address/port
information.

- packet.cs:
The Mac provides one Packet instance. It is received in the
Socket.onPacket
call or using Mac.getPacket. As the header length of a v6lowpan
packet is
variable, an application is expected to fill in address and port
information
and call swap() or create() to let the Mac create a backing
Buffer instance
for the message. The payload can then be filled in by the
application.

- slots.cs:
A Slot instance represents the time slot between child and
parent, parent and
child and the shared time slot. The child Slot instances are
kept in a table
by the Mac and the flags field specify whether a slot currently
in use.
Sequence numbers keep the last sequence number received or used
in a send operation.
A timestamp records the last time a message was received n this
slot. The Mac
sends a ping message when nothing was heard from a child since a
long time.
A BufferQ instance keeps the messages to be pending for a send
on this slot.

- mac.cs, Mac:
The Mac class implements a state machine and moves from handling
the child state
(enterChildInterval(), leaveChildInterval()) to the parent state
(enterParentInterval(),
leaveParentInterval()). The parent states are:
- STATE_PARENT_INACTIVE: mote is not associated yet or no
superframe slot has been
allocated by this edge to the mote
- STATE_PARENT_SEND_BEACON: parent has sent beacon, onParentTx()
will be called
when the transmit has been done and the state will move to
STATE_PARENT_HANDLE_SLOT.
- STATE_PARENT_HANDLE_SLOT: the parent will call repeatedly
nextParentSlot() to
send or receive a message in the shared or one of the children
time slots. After the
last slot is handled, state will move to
STATE_PARENT_SEND_BEACON.
- STATE_CHILD_INACTIVE: not attached or edge mote (which does
not have a parent).
- STATE_CHILD_SCAN_BEACON: child scans for a suitable parent. If
a mote has seen
a beacon a second time and no beacon with a smaller depth has
been found in between,
an association request is sent. State moves to
STATE_CHILD_RECEIVE_BEACON and the field
associationChildRound is set to wait for a number of times for
an association response.
- STATE_CHILD_RECEIVE_BEACON: wait for the parent beacon and
update the timestamp
beaconChildTS. In case of (associationChildRound > 0), move
state to
STATE_CHILD_RECEIVE_ASSOC, otherwise STATE_CHILD_HANDLE_SLOT.
- STATE_CHILD_RECEIVE_ASSOC: check for asociation response and
move to
STATE_CHILD_RECEIVE_BEACON again
- STATE_CHILD_HANDLE_SLOT: handle message exchange between
parent and child.
Packet forwarding/handling:
Incoming radio pdus are parsed, kept in Buffer instances and
queued (either at
a Slot or in pktBufferQ). When the Mac signals an event
Mac.EV_SILENT (i.e.
a child or parent superframe has been handled), the packets are
forwarded to
the Socket interface.
Event forwarding:
The Mac forwards Mac.EV_CHILD_ATTACHED, Mac.EV_PARENT_LOST and
EV_SILENT
to registered event handlers when a mote is attached, the beacon
of the parent
got lost and the time betwwen parent and child role has been
reached.
Ping messages:
The Mac periodically checks the last time a child has sent a
message. It eventually
sends a ping message for the child to ack in its time slot.
Packet filter:
An application can install a packet filter whic receives packets
on their way from a
child to the tree root.

Directorio mrv6.lib.js

Archivo mrv6.js
Basic MRv6 module functionality
// (C) COPYRIGHT INTERNATIONAL BUSINESS MACHINES CORPORATION
2006-2009
// ALL RIGHTS RESERVED
// IBM Research Division, Zurich Research Laboratory
//
//
------------------------------------------------------------------
--

Runtime.include("../../src/defs.js");

//
------------------------------------------------------------------
--
//
// Basic MRv6 module functionality
//
//
------------------------------------------------------------------
--

/**
*
*/
MRv6 = {
/**
* Delay in ms between two sent commands.
* @type Number
* @private
*/
cmdno2str: {},

/**
* Delay in ms between two sent commands.
* @type Number
* @private
*/
COMMAND_DELAY: 500,

/**
* @type String
*/
LOGGER_NAME: "MRv6",
/**
* @type Number
*/
EDGE_ASM_IDENTITY : "mrv6-edge-#.#",

/**
* @type Number
*/
EV_CAT_MRV6: "MRv6",

/**
* Return mote attribute which is eventually created.
* @param mote
* @returns {Object}
*/
getMoteAttr: function(/** Sonoran.Mote */mote) {
var attr = mote.getAttribute("6lowpan");
if (!attr) {
attr = new MRv6.MoteAttr();
mote.setAttribute("6lowpan", attr);
}
return attr;
},

/**
* Return mote attribute or null.
* @param mote
* @returns {Object}
*/
lookupMoteAttr: function(/** Sonoran.Mote */mote) {
return mote.getAttribute("6lowpan");
},

/**
* Delete mote attribute.
* @param mote
* @returns {Object}
*/
deleteMoteAttr: function(/** Sonoran.Mote */mote) {
return mote.delAttribute("6lowpan");
},

/**
* @param addr
* @returns {Sonoran.Mote}
*/
findMoteBy6lowpanAddr: function(/** MRv6.Address */addr) {
var bits = addr.getAddressBits();
if (addr.getAddressMode() ===
MRv6.Address.ADDRESS_MODE_SADDR) {
var saddr = Formatter.unpack("2u", bits)[0];
return this.findMoteBySaddr(saddr);
}
if (addr.getAddressMode() ===
MRv6.Address.ADDRESS_MODE_XADDR) {
var hex = Formatter.unpack("8xL", bits)[0];
var uniqueid = Util.UUID.hex2eui64(hex);
return Sonoran.Registry.lookupMoteByUniqueid(uniqueid);
}
if (addr.getAddressMode() === MRv6.Address.ADDRESS_MODE_FULL)
{
assert(bits.length===16);
return this.findMoteByIpAddr(bits);
}
assert(false);
return null;
},

/**
* @param ipaddr
* @returns {Sonoran.Mote}
* @private
*/
findMoteByIpAddr: function(/** String */ipv6addr) {
assert(ipv6addr.length===16);
var prefix = ipv6addr.substr(0, 8);
var ifid = ipv6addr.substr(8, 8);
//QUACK(0, sprintf("findMoteByIpAddr: %H %H", prefix, ifid));
var uniqueid = Util.UUID.hex2eui64(Formatter.binToHex(ifid));
//QUACK(0, sprintf("findMoteByIpAddr: %s", uniqueid));
var mote = Sonoran.Registry.lookupMoteByUniqueid(uniqueid);
return mote;
},

/**
* @param saddr
* @returns {Sonoran.Mote}
*/
findMoteBySaddr: function(/** Number */saddr) {
var motes = Sonoran.Registry.getMotes();
for (var i = 0; i < motes.length; i++) {
var mote = motes[i];
var attr = MRv6.lookupMoteAttr(mote);
if (attr && attr.saddr === saddr) {
return mote;
}
}
return null;
},

/**
* @returns {Sonoran.Mote[]}
*/
getAllMotes: function() {
return Sonoran.Registry.filterMotes(function(mote) { return
MRv6.lookupMoteAttr(mote) != null; });
},

/**
* Returns existing connection or throws Exception instance.
So far,
* only one MRv6 connection is allowed at one time.
* @returns {MRv6.Connection}
* @private
*/
getConnection: function() {
var mote = MRv6.findMoteBySaddr(DEFS.EDGE_SADDR);
if (!mote) {
throw new Exception("No connection to 6lowpan edge mote
found!");
}
var attr = MRv6.lookupMoteAttr(mote);
var conn = attr.conn;
return conn;
},

/**
* Returns existing connection or null.
* @returns {MRv6.Connection}
* @private
*/
lookupConnection: function() {
var mote = MRv6.findMoteBySaddr(DEFS.EDGE_SADDR);
if (!mote) {
return null;
}
var attr = MRv6.lookupMoteAttr(mote);
var conn = attr.conn;
return conn;
},

/**
* Setup mote as edge, connect, configure and stat protocol.
* @param mote
* @param opts Null or object with numChildren, maxDepth,
maxMotes
* @param noTunnel If true, do not setup tunnel
* @param noMSetup If true, do not upgrade 6lowpan assembly
* @returns Object[] With MRv6.Connection and MRv6.Tunnel
*/
setupConnection: function(/** Sonoran.Mote */mote, /** Object
*/opts, /** Boolean */noTunnel, /** Boolean */noMSetup) {
if (!noMSetup) {
//QUACK(0, "SETUP...");
var msetup = new Sonoran.MSETUP();
msetup.allowExcessAssemblies = true;
msetup.setMotes([ mote ]);
msetup.findAndAddAssembly(new
Sonoran.AsmName(MRv6.EDGE_ASM_IDENTITY), false);
msetup.performActions();
}

var conn = new MRv6.Connection(mote);


conn.attach();

var config = conn.configure(undefined);


if (opts) {
var modified = false;
for (var key in opts) {
if (config[key] != opts[key]) {
config[key] = opts[key];
modified = true;
}
}
config = conn.configure(config);
}
println("Edge mote configuration:\n" + config);

var info = conn.info();


assert(info.RADIO_ACTIVE!==undefined);
if (!info.RADIO_ACTIVE) {
println("Starting mrv6 which is not yet active.");
conn.start();
}

conn.query();

var tunif;
if (!noTunnel) {
tunif = MRv6.Tunnel.createTunnel();
}

return [ conn, tunif];


},

/**
* @returns {String[]}
*/
getNodeInfoKeys: function() {
return DEFS.INFO.map(function(obj) { return obj.name; });
},
/**
* @returns {Util.FormattedData}
*/
unpackNodeInfo: function(/** String */data) {
var obj = new Util.FormattedData(DEFS.INFO);
return obj.unpack(data);
},

/**
* @returns {Util.FormattedData}
*/
unpackConnConf: function(/** String */data) {
var obj = new Util.FormattedData(DEFS.CONFIG);
return obj.unpack(data);
},

/**
* @returns {String[]}
*/
getConnConfKeys: function() {
return DEFS.CONFIG.map(function(obj) { return obj.name; });
}
};

MRv6.cmdno2str = {};
MRv6.cmdno2str[DEFS.LIP_CMD_ATTACH] = "LIP_CMD_ATTACH";
MRv6.cmdno2str[DEFS.LIP_CMD_DETACH] = "LIP_CMD_DETACH";
MRv6.cmdno2str[DEFS.LIP_CMD_START] = "LIP_CMD_START";
MRv6.cmdno2str[DEFS.LIP_CMD_STOP] = "LIP_CMD_STOP";
MRv6.cmdno2str[DEFS.LIP_CMD_CONFIG] = "LIP_CMD_CONFIG";
MRv6.cmdno2str[DEFS.LIP_CMD_INFO] = "LIP_CMD_INFO";
MRv6.cmdno2str[DEFS.LIP_CMD_FORWARD] = "LIP_CMD_FORWARD";
MRv6.cmdno2str[DEFS.LIP_CMD_CTRLFLOW] = "LIP_CMD_CTRLFLOW";
MRv6.cmdno2str[DEFS.LIP_CMD_QUERY] = "LIP_CMD_QUERY";

Logger.defineModule(MRv6.LOGGER_NAME, Logger.DEBUG);
Logger.setImmediateLogFilter(MRv6.LOGGER_NAME, Logger.NOTICE);

//
------------------------------------------------------------------
--
//
// MRv6 Mote attribute
//
//
------------------------------------------------------------------
--

Class.define(
"MRv6.MoteAttr",
/**
* @lends MRv6.MoteAttr
*/
{
/**
* Attribute for a mote being part of a MRv6 network, either
edge or wireless mote.
* @constructs
*/
__constr__: function() {},
/**
* @type Boolean
*/
edge: false,
/**
* @type MRv6.Connection
*/
conn: null,
/**
* @type Number
*/
saddr: -1,
/**
* @type Sonoran.Mote
*/
parentMote: null,
/**
* @type Util.FormattedData
*/
info: null,

/**
* @param conn
* @param isEdge
*/
setConn: function(/** MRv6.Connection */conn, /** Boolean
*/isEdge) {
this.conn = conn;
this.edge = isEdge;
},
/**
* @param conn
* @param saddr
* @param parentSaddr
*/
setAddr: function(/** MRv6.Connection */conn, /** Number
*/saddr, /** Number */parentSaddr) {
this.conn = conn;
this.saddr = saddr;
this.parentSaddr = parentSaddr;
},
/**
* @returns {Boolean}
*/
isEdge: function() {
return this.edge;
},
/**
* @returns {MRv6.Connection}
*/
getConnection: function() {
return this.conn;
},
/**
* @returns {Number} Hops to edge
*/
getHops: function() {
var hops = 0;
var addr = this.parentSaddr;
while(addr != 0xffff) {
hops += 1;
var mote = MRv6.findMoteBySaddr(addr);
if (!mote) {
break;
}
var attr = MRv6.lookupMoteAttr(mote);
assert(attr);
addr = attr.parentSaddr;
}
return hops;
},

/**
* @returns {Sonoran.Mote} mote
*/
lookupParentMote: function() {
if (this.parentSaddr == 0xffff) {
return null;
}
return MRv6.findMoteBySaddr(this.parentSaddr);
},

/**
* @param info
*/
onMoteInfo: function(/** Util.FormattedData */info) {
this.info = info;
},

/**
* @private
*/
toString: function() {
return "6LowpanMoteAttr: " + this.saddr + ", " +
this.edge;
}
}
);

//
------------------------------------------------------------------
--
//
// Wrapper to UDP packets received on MRv6 connection
//
//
------------------------------------------------------------------
--

Class.define(
"MRv6.UDPPacket",
/**
* @lends MRv6.UDPPacket.prototype
*/
{
/**
* MRv6 UDP packet.
* @constructs
* @param srcaddr
* @param dstaddr
* @param srcport
* @param dstport
* @param payload
*/
__constr__: function(/** MRv6.Address */srcaddr, /**
MRv6.Address */dstaddr, /** Number */srcport, /** Number
*/dstport, /** String */payload) {
this.srcaddr = srcaddr;
this.dstaddr = dstaddr;
this.srcport = srcport;
this.dstport = dstport;
this.payload = payload;
},
/**
* @return {String} human-readable representation
*/
toString: function() {
return sprintf("UDP: source:%s dest:%s srcport:%d
dstport:%d payload:%H", this.srcaddr, this.dstaddr, this.srcport,
this.dstport, this.payload);
},

/**
* @return {String} binary representation
*/
toBinary: function() {
return MRv6.IPHC.generate1(this.srcaddr, this.dstaddr,
true) + MRv6.NHC.generateUdp(this.srcPort, this.destPort) +
payload;
}
}
);

//
------------------------------------------------------------------
--
//
// MRv6 Address
//
//
------------------------------------------------------------------
--

Class.define(
"MRv6.Address",
/**
* @lends MRv6.Address.prototype
*/
{
/**
* An encoded 6lowpan address. For now, only short, extended
and full address modes are used.
* No use of address compression or contect identifier yet.
* @constructs
* @param contextExtension
* @param contextIdentifier
* @param addressMode
* @param addressCompression
* @param bits
*/
__constr__: function(/** Number */contextExtension, /**
Number */contextIdentifier, /** Number */addressMode, /** Number
*/addressCompression, /** String */bits) {
assert(contextExtension === 0x0);
assert(contextIdentifier === 0x0);
assert(MRv6.Address.isValidAddressMode(addressMode));
assert(addressCompression === 0x0);
this.contextExtension = contextExtension;
this.contextIdentifier = contextIdentifier;
this.addressMode = addressMode;
this.addressCompression = addressCompression;
var hdr =
(contextIdentifier<<MRv6.Address.CONTEXT_ID_OFF) |
(contextExtension<<MRv6.Address.CONTEXT_EXTENSION_OFF) |
(addressMode<<MRv6.Address.ADDRESS_MODE_OFF) |
(addressCompression<<MRv6.Address.ADDRESS_COMPRESSION_OFF);
// XXX: bits could be size 0, 2, 8 or 16
this.bits = bits;
this.bytes = Formatter.pack("1u", hdr) + bits;
},

/**
* @returns {String}
*/
toString: function() {
return sprintf("%x|%x|%x|%x:%H", this.contextExtension,
this.contextIdentifier, this.addressMode, this.addressCompression,
this.bits);
},

/**
* @returns {Number}
*/
getAddressMode: function() {
return this.addressMode;
},

/**
* @returns {Number}
*/
getAddressCompression: function() {
return this.addressCompression;
},

/**
* @returns {Number}
*/
getContextExtension: function() {
return this.contextExtension;
},
/**
* @returns {Number}
*/
getContextIdentifier: function() {
return this.contextIdentifier;
},

/**
* @returns {String}
*/
getAddressBits: function() {
return this.bits;
},

/**
* @returns {Number}
*/
getLength: function() {
return this.bytes.length;
},

/**
* @returns {String}
*/
toBinary: function() {
return this.bytes;
}
},

/**
* Static properties.
* @lends MRv6.Address
*/
{
/**
* @type Number
*/
CONTEXT_ID_MASK: 0xf0,
/**
* @type Number
*/
CONTEXT_ID_OFF: 4,
/**
* @type Number
*/
ADDRESS_MODE_MASK: 0x0c,
/**
* @type Number
*/
ADDRESS_MODE_OFF: 2,
/**
* @type Number
*/
ADDRESS_COMPRESSION_MASK: 0x02,
/**
* @type Number
*/
ADDRESS_COMPRESSION_OFF: 1,
/**
* @type Number
*/
CONTEXT_EXTENSION_MASK: 0x01,
/**
* @type Number
*/
CONTEXT_EXTENSION_OFF: 0,
/**
* @type Number
*/
ADDRESS_MODE_SADDR: 0x2,
/**
* @type Number
*/
ADDRESS_MODE_XADDR: 0x1,
/**
* @type Number
*/
ADDRESS_MODE_FULL: 0x0,

/**
* @param s Binary representation
* @param off
* @returns {MRv6.Address}
*/
parse: function(/** String */s, /** Number */off) {
if (off == null) { off = 0; }
var b = s.charCodeAt(off);
var ci = ((b&this.CONTEXT_ID_MASK) >>
this.CONTEXT_ID_OFF);
assert(ci===0);
var ce = ((b&this.CONTEXT_EXTENSION_MASK) >>
this.CONTEXT_EXTENSION_OFF);
assert(ce===0);
var ac = ((b&this.ADDRESS_COMPRESSION_MASK) >>
this.ADDRESS_COMPRESSION_OFF);
assert(ac===0);
var am = ((b&this.ADDRESS_MODE_MASK) >>
this.ADDRESS_MODE_OFF);
assert(this.isValidAddressMode(am));
var cnt = this.addressMode2BitsLength(am);
var bits = s.substr(off+1, cnt);
var addr = new MRv6.Address(ce, ci, am, ac, bits);
return addr;
},
/**
* @param am
* @returns {Number}
*/
addressMode2BitsLength: function(/** Number */am) {
switch(am) {
case this.ADDRESS_MODE_SADDR:
return 2;
case this.ADDRESS_MODE_XADDR:
return 8;
case this.ADDRESS_MODE_FULL:
return 16;
}
assert(false);
return -1;
},

/**
* @param s Unique mote id
* @returns {MRv6.Address}
*/
uniqueid2address: function(/** String */uniqueid) {
var eui = Util.UUID.eui642hex(uniqueid);
var bits = Formatter.pack("8xL", eui);
return new MRv6.Address(0, 0, this.ADDRESS_MODE_XADDR, 0,
bits);
},

/**
* @param saddr
* @returns {MRv6.Address}
*/
saddr2address: function(/** Number */saddr) {
var bits = Formatter.pack("2u", saddr);
return new MRv6.Address(0, 0, this.ADDRESS_MODE_SADDR, 0,
bits);
},

/**
* @param ipv6addr
* @returns {MRv6.Address}
*/
ipv62address: function(/** String */ipv6addr) {
assert(ipv6addr.length===16);
return new MRv6.Address(0, 0, this.ADDRESS_MODE_FULL, 0,
ipv6addr);
},

/**
* @returns {Boolean}
*/
isValidAddressMode: function(/**Number */am) {
return (am==this.ADDRESS_MODE_SADDR||
am==this.ADDRESS_MODE_XADDR||am==this.ADDRESS_MODE_FULL);
}
}
);

Event.extend(
"MRv6.Event",
/**
* @lends MRv6.Event.prototype
*/
{
/**
* @constructs
* @augments Event
* @param evname 'info' etc
* @param uniqueid
* @param info
*/
__constr__: function(/** String */evname, /** String
*/uniqueid, /** Util.FormattedData */info) {
Event.call(this, MRv6.EV_CAT_MRV6, evname);
this.uniqueid = uniqueid;
this.info = info;
//Runtime.blockAccess(this, "type");
},

/**
* @type String
*/
uniqueid: null,

/**
* @type Util.FormattedData
*/
info: null,

/**
* @return {Sonoran.Mote}
*/
getMote: function() {
return
Sonoran.Registry.lookupMoteByUniqueid(this.uniqueid);
}
}
);

//
------------------------------------------------------------------
--
//
// MRv6 Connection
//
//
------------------------------------------------------------------
--

Class.define(
"MRv6.Connection",
/**
* @lends MRv6.Connection.prototype
*/
{
/**
* LIP based connection to send routes, to send packets using
the edge mote and to receive
* packets from the edge mote.
* @class
* @augments Generic.Connection
* @constructs
* @param mote
*/
__constr__: function(/** Sonoran.Mote */mote) {
var attr = MRv6.lookupMoteAttr(mote);
if (attr) {
throw new Exception("Mote is already setup as 6lowpan
mote!");
}
this.mote = mote;
this.srvport = DEFS.LIP_ASM_MR_PORT;
var clsock = this.clsock = new Sonoran.Socket();
clsock.setName("MRv6.Connection:" + mote);
clsock.onData = this.onData.bind(this);
var _this = this;
clsock.onClose = function(status) {
_this.clsock = null;
_this.close(status);
_this.onClose(status);
};
clsock.open(DEFS.TUNNEL_UDP_PORT, BLCK);
this.commands = new MRv6.Connection.CommandQueue(this);
},

/**
* @type Sonoran.Mote
*/
mote: null,
/**
* @type Sonoran.Socket
*/
clsock: null,
/**
* @type Number
*/
srvport: null,
/**
* @type Object
*/
commands: null,

//----------------------------------------------------------------
------------------------------------------------
//
// Gateway support start: Methods required to set this connection
as gateway in edge mote
//
//
------------------------------------------------------------------
----------------------------------------------
/**
* @returns {Boolean} true if specified mote is the gateway
mote
*/
isGatewayMote: function(/** Sonoran.Mote */mote) {
return mote===this.mote;
},

/**
* Generate an address string for a wireless mote attached to
this gateway.
* Used in createMote to create a new mote.
* @param mote
* @returns {String} address string for a mote
* @private
*/
genAddr: function(/** Sonoran.Mote */mote) {
return sprintf("%s://%s", "v6", (typeof(mote) ===
'string') ? mote : mote.getUniqueid());
},

/**
* Return address family implemented by this gateway (default
is 'rf').
* @returns {String} address family
*/
getAddressFamiliy: function() {
return "v6";
},

/**
* @see Sonoran.Gateway.send
* @param dstmote Wireless target mote
* @param dstport Port on wirless target mote
* @param data Binary string with data
* @param timer Optional
* @private
*/
send: function(/** Sonoran.Mote */dstmote, /** Number
*/dstport, /** Number */srcport, /** String */data, /**
Timer.Timer|Timer.Timer[] */timer) {
//QUACK(0, sprintf("MRv6.Connection.send: %s %d %d %H\n",
dstmote, dstport, srcport, data));
var address1 =
MRv6.Address.uniqueid2address(dstmote.getUniqueid());
var address2 =
MRv6.Address.saddr2address(DEFS.EDGE_SADDR);

var pdu = String.fromCharCode(DEFS.LIP_CMD_FORWARD) +


address1.toBinary() + address2.toBinary() +
Formatter.pack("2uB2uB", dstport, srcport) + data;

var _this = this;


return this.commands.exchangeCommand(pdu, timer,
function(status) {
if (status.code !== 0) {
var msg = sprintf("%s: send data to mote '%s'
failed: %s", _this, dstmote, status);
Logger.err(msg);
}
});
},

/**
* Gateway mote disappears. Calls shutdown on gateway.
* @param status
* @param callback
*/
shutdown: function(/** AOP.Result */status, callback) {
this.close(status);
callback(new AOP.OK());
},

//----------------------------------------------------------------
------------------------------------------------
//
// Gateway support end
//
//
------------------------------------------------------------------
----------------------------------------------

/**
* @ignore
*/
toString: function() {
return "MRv6";
},

/**
* @returns {Boolean}
*/
isConnected: function() {
return (this.clsock !== null);
},

/**
* @returns {Sonoran.Mote}
*/
getMote: function() {
return this.mote;
},

/**
* Override to get notified when connection is closed.
*/
onClose: function(/** AOP.Result */result) {
QUACK(0, sprintf("%s: closing: %s", this, result));
},

/**
* @param status
*/
close: function(/** AOP.Result */status) {
var motes = MRv6.getAllMotes();
for (var i = 0; i < motes.length; i++) {
var mote = motes[i];
var attr = MRv6.lookupMoteAttr(mote);
if (!attr.isEdge()) {
this.onMoteRemoval(mote);
}
}
this.mote.setGateway(null);
MRv6.deleteMoteAttr(this.mote);

Sonoran.Registry.signalEvent(new
Sonoran.Event.Gateway(Sonoran.EV_NAME_DEREGISTER, this.mote));

if (this.clsock != null) {
this.clsock.close(status);
this.clsock = null;
}
this.commands.clear();
},

/**
* @returns {Sonoran.Mote[]}
*/
getMotes: function() {
var _this = this;
return Sonoran.Registry.filterMotes(function(mote) {
var attr = MRv6.lookupMoteAttr(mote);
return !attr ? false : (attr.conn === _this);
});
},

/**
* Attach to edge mote to send/receive packets.
* @throws {Exception}
*/
attach: function() {
var result =
this.commands.exchangeCommand(Formatter.pack("1u",
DEFS.LIP_CMD_ATTACH), undefined, SCB);
if (result.code !== 0) {
throw new Exception("Cannot attach to mote: " + result);
}

this.mote.setGateway(this);
Sonoran.Registry.signalEvent(new
Sonoran.Event.Gateway(Sonoran.EV_NAME_REGISTER, this.mote));

var attr = MRv6.getMoteAttr(this.mote);


attr.setConn(this, true);
attr.setAddr(this, DEFS.EDGE_SADDR, null);
},

/**
* Disconnect from edge mote.
*/
detach: function() {
var result =
this.commands.exchangeCommand(Formatter.pack("1u",
DEFS.LIP_CMD_DETACH), undefined, SCB);
this.close(result);
},

/**
* @param mode
* @throws {Exception}
*/
ctrlflow: function(/** Number */mode) {
var result =
this.commands.exchangeCommand(Formatter.pack("1u1u",
DEFS.LIP_CMD_CTRLFLOW, mode), undefined, SCB);
if (result.code !== 0) {
throw new Exception("Cannot send control-flow message to
mote: " + result);
}
},

/**
* Override to get notified when connection is closed.
*/
onClose: function(/** AOP.Result */result) {
var _this = this;

(Sonoran.Registry.filterMotes(function(m) { return
m.getSink() === _this; })).forEach(function(mote) {
_this.onMoteRemoval(mote);
});

this.mote.setGateway(null);
MRv6.deleteMoteAttr(this.mote);
Sonoran.Registry.signalEvent(new
Sonoran.Event.Gateway(Sonoran.EV_NAME_DEREGISTER, this.mote));
},

/**
* Start radio.
* @throws {Exception}
*/
start: function(/** Sonoran.Mote */mote) {
var data = Formatter.pack("1u", DEFS.LIP_CMD_START);
var result = this.commands.exchangeCommand(data,
undefined, SCB);
if (result.code !== 0) {
throw new Exception("Cannot start radio: " + result);
}
},

/**
* Stop radio.
* @throws {Exception}
*/
stop: function() {
var result =
this.commands.exchangeCommand(Formatter.pack("1u",
DEFS.LIP_CMD_STOP), undefined, SCB);
if (result.code !== 0) {
throw new Exception("Cannot stop radio: " + result);
}
},

/**
* Get/Set configuration data.
* @param config
* @throws {Exception}
* @returns {Util.FormattedData}
*/
configure: function(/** Util.FormattedData */config) {
var bytes = Formatter.pack("1u", DEFS.LIP_CMD_CONFIG);
if (config) {
bytes += config.pack();
}
var result = this.commands.exchangeCommand(bytes,
undefined, SCB);
if (result.code !== 0) {
throw new Exception("'configure'-Command failed: " +
result);
}
return MRv6.unpackConnConf(result.getData());
},

/**
* Send status-command to mote.
* @throws {Exception}
* @returns {Number}
*/
info: function() {
var bytes = Formatter.pack("1u", DEFS.LIP_CMD_INFO);
var result = this.commands.exchangeCommand(bytes,
undefined, SCB);
if (result.code !== 0) {
throw new Exception("'info'-Command failed: " + result);
}
var data = result.getData();
var info = MRv6.unpackNodeInfo(data);
return info;
},

/**
* Send query-command to mote.
* @throws {Exception}
*/
query: function() {
var slot = 0;
while(true) {
//QUACK(0, sprintf("QUERY: slot %d", slot));
var bytes = Formatter.pack("1u1u", DEFS.LIP_CMD_QUERY,
slot);
var result = this.commands.exchangeCommand(bytes,
undefined, SCB);
if (result.code !== 0) {
throw new Exception("'status'-Command failed: " +
result);
}
var data = result.getData();
if (data.length===0) {
break;
}
while(data.length>1) {
var arr = Formatter.unpack("8xL2u2u*d", data);
var nodeXaddr = arr[0];
var saddr = arr[1];
var parentSaddr = arr[2];
data = arr[3];
//var uniqueid = Util.UUID.hex2eui64(nodeXaddr);
//QUACK(0, sprintf("QUERY: %d %s %d %d %H", slot,
uniqueid, saddr, parentSaddr, data));
this.onMoteAdd(nodeXaddr, saddr, parentSaddr);
}
slot = data.charCodeAt(0);
}
},

/**
* Evaluate a incoming packet.
* @param blob
* @private
*/
onData: function(/** Sonoran.Event.Media */blob) {
//QUACK(0, sprintf("Connection.onData: incoming data:
%s", blob));
var message = blob.data;
assert(message.length>0);

var tunif = MRv6.Tunnel.getTunnel();


if (tunif) {
try {
tunif.send2Remote(blob);
} catch(ex) {
Logger.warn("Could not send packet to MRv6 tunnel,
tunnel inactive?");
}
}

var cmd = message.charCodeAt(0);


if ((cmd&DEFS.LIP_CMD_REPLY_MASK) !== 0) {
this.commands.onData(blob);
return;
}

if (cmd === DEFS.LIP_EV_ERROR) {


this.onError(cmd, message);
return;
}

if (cmd === DEFS.LIP_EV_STARTED) {


return;
}

if (cmd !== DEFS.LIP_EV_RADIO) {


Logger.err(sprintf("Unexpected data from 6lowpan
gateway: %H", message));
return;
}

var srcaddr = MRv6.Address.parse(message, 1);


var data = message.substr(1 + srcaddr.getLength());

var dstaddr = MRv6.Address.parse(data);


data = data.substr(dstaddr.getLength());

var arr = Formatter.unpack("2uB2uB*d", data);


var srcport = arr[0];
var dstport = arr[1];
data = arr[2];

var srcmote = MRv6.findMoteBy6lowpanAddr(srcaddr);

if (srcport === DEFS.H2E_UDP_PORT) {


arr = Formatter.unpack("1u*d", data);
var _cmd = arr[0];
var _data = arr[1];
switch(_cmd) {
case DEFS.H2E_EV_NODE_ADD: {
arr = Formatter.unpack("8xL2u2u", _data);
var nodeXaddr = arr[0];
var saddr = arr[1];
var parentSaddr = arr[2];
var mote = this.onMoteAdd(nodeXaddr, saddr,
parentSaddr);
//Logger.log(Logger.NOTICE, MRv6.LOGGER_NAME,
sprintf("Node added: %s, short address %d, parent short address
%d", mote.getUniqueid(), saddr, parentSaddr),
blob.getTimeInMillis());
break;
}
case DEFS.H2E_EV_NODE_DEL: {
arr = Formatter.unpack("8xL", _data);
var xaddr = arr[0];
var uniqueid = Util.UUID.hex2eui64(xaddr);
var mote =
Sonoran.Registry.lookupMoteByUniqueid(uniqueid);
if (!mote) {
printf("%s: lost unregistered node: %d\n", this,
saddr);
} else {
this.onMoteRemoval(mote, blob);
}
break;
}
case DEFS.H2E_EV_NODE_INFO: {
var info = MRv6.unpackNodeInfo(_data);
if (srcmote == null) {
Logger.log(Logger.WARN, MRv6.LOGGER_NAME,
"Received info from mote having been lost: " + srcaddr);
} else {
var attr = MRv6.lookupMoteAttr(srcmote);
if (attr) {
attr.onMoteInfo(info);
}
this.onMoteInfo(srcmote, info, blob);
}
break;
}
default:
Logger.log(Logger.WARN, MRv6.LOGGER_NAME, "Packet
for N2E.UDP_PORT: " + srcaddr + " -> " + dstaddr + ", " + _cmd + "
" + Formatter.binToHex(_data));
}
//return;
}

var packet = new MRv6.UDPPacket(srcaddr, dstaddr,


srcport, dstport, data);
if (!srcmote || !MRv6.lookupMoteAttr(srcmote)) {
//println("Received packet from mote having been
lost:\n" + packet.toString());
Logger.log(Logger.WARN, MRv6.LOGGER_NAME,
sprintf("Received packet from mote %s having been lost:\n%s",
srcaddr, packet.toString()));
return;
}

var dstmote = MRv6.findMoteBy6lowpanAddr(dstaddr);


if (dstmote) {
assert(dstmote===this.mote);
var dstattr = MRv6.lookupMoteAttr(dstmote);
if (!dstattr) {
//println("Received packet for mote having been
lost:\n" + packet.toString());
Logger.log(Logger.WARN, MRv6.LOGGER_NAME, "Received
packet for mote having been lost:\n" + packet.toString());
return;
}

Event.Registry.signalEvent(new
Sonoran.Event.Media(dstport, srcmote, srcport, data, blob.time));

try {
this.onPacket4Mote(packet, dstmote, srcmote,
message);
} catch (x) {
//QUACK(0, "onPacket4Mote invocation failed: " +
Runtime.dumpException(x));
Logger.log(Logger.ERR, MRv6.LOGGER_NAME,
"onPacket4Mote invocation failed: " + Runtime.dumpException(x));
}

} else {

try {
this.onPacket4Addr(packet, srcmote, message);
} catch (x) {
QUACK(0, "onPacket4Addr invocation failed: " +
Runtime.dumpException(x));
Logger.log(Logger.ERR, MRv6.LOGGER_NAME,
"onPacket4Addr invocation failed: " + Runtime.dumpException(x));
}
}
},

/**
* On mote info.
*/
onMoteInfo: function(/** Sonoran.Mote */mote, /**
Util.FormattedData */info) {
var uniqueid = mote.getUniqueid();
Logger.log(Logger.INFO, MRv6.LOGGER_NAME, "Node-Info: " +
uniqueid + "\n" + info);
//Event.Registry.signalEvent(new MRv6.Event("info",
uniqueid, info));
},

/**
* Received NODE_ADD, NODE_STATUS event.
* @param xaddr
* @param saddr
* @param parentSaddr
* @private
*/
onMoteAdd: function(/** String */xaddr, /** Number
*/saddr, /** Number */parentSaddr) {
var uniqueid = Util.UUID.hex2eui64(xaddr);
var mote =
Sonoran.Registry.lookupMoteByUniqueid(uniqueid);
var attr;

if (!mote) {
assert(!this.mote.isSimulated());
var moteImpl = new Sonoran.HW.MoteImpl(uniqueid, "rf://"
+ uniqueid);
mote = moteImpl.mote;
}

attr = MRv6.lookupMoteAttr(mote);
var existedBefore = (attr != null);
if (!attr) {
attr = MRv6.getMoteAttr(mote);
}

attr.setAddr(this, saddr, parentSaddr);

if (!existedBefore || mote.getState() !==


Sonoran.Mote.ON) {
Logger.log(Logger.NOTICE, MRv6.LOGGER_NAME,
sprintf("Node added: %s, short address %d, parent short address
%d", mote.getUniqueid(), saddr, parentSaddr));
}

mote.setGateway(this);
mote.updateState(Sonoran.Mote.ON);

if (saddr == 0) {
// gateway mote itself
return mote;
}

var ev = new Sonoran.Event.Gateway(Sonoran.EV_NAME_HELLO,


this.mote, mote, existedBefore);
Sonoran.Registry.signalEvent(ev);
return mote;
},

/**
* A mote got lost. This is also called from
Sonoran.Mote.setGateway and
* Sonoran.Mote.onDestroy
* @param mote
* @private
*/
onMoteRemoval: function(/** Sonoran.Mote */mote) {
var attr = MRv6.lookupMoteAttr(mote);
if (attr) {
MRv6.deleteMoteAttr(mote);
assert(!MRv6.lookupMoteAttr(mote));
}
if (mote.getState() !== Sonoran.Mote.OFF) {
Logger.log(Logger.NOTICE, MRv6.LOGGER_NAME, "Node lost:
" + mote.getUniqueid());
}
mote.updateState(Sonoran.Mote.OFF);
mote.setGateway(null);
var ev = new Sonoran.Event.Gateway(Sonoran.EV_NAME_BYE,
this.mote, mote);
Sonoran.Registry.signalEvent(ev);
//Sonoran.Registry.signalEvent(new
Sonoran.Event.Gateway(Sonoran.Event.Gateway.BYE, this.mote,
mote));
},

/**
* A UDP packet has been received with destination being edge
mote.
* @param packet
* @param dstmote
* @param srcmote
*/
onPacket4Mote: function(/** MRv6.UDPPacket */packet, /**
Sonoran.Mote */dstmote, /** Sonoran.Mote */srcmote) {
//println("Packet from mote to mote: " + srcmote + " -> "
+ dstmote + "\n" + packet.toString());
Logger.log(Logger.INFO, MRv6.LOGGER_NAME, "Packet from
mote to mote: " + srcmote + " -> " + dstmote + "\n" +
packet.toString());
},

/**
* A UDP packet has been received with destination being not
the edge.
* @param packet
* @param srcmote
*/
onPacket4Addr: function(/** MRv6.UDPPacket */packet, /**
Sonoran.Mote */srcmote) {
Logger.log(Logger.INFO, MRv6.LOGGER_NAME, "Packet from
mote for external address: " + srcmote + "\n" +
packet.toString());
//println("Packet from mote for external address: " +
srcmote + "\n" + packet.toString());
},

/**
* An error message was received from the edge.
* @param bytes
*/
onError: function(/** Number */err, /** String */bytes) {
var s;
if (err === DEFS.ERROR_EDGE_EXISTS) {
s = sprintf("Error received from 6lowpan gateway:
ERROR_EDGE_EXISTS");
} else {
s = sprintf("Error received from 6lowpan gateway: %d
%H", err, bytes);
}
Logger.err(s);
}
}
);

//
------------------------------------------------------------------
--
//
// MRv6 connection command queue and command
//
//
------------------------------------------------------------------
--

Class.define(
"MRv6.Connection.CommandQueue",
/**
* @lends MRv6.Connection.CommandQueue.prototype
*/
{
/**
* Manage queue of commands to send to edge mote.
* @constructs
* @param connection
* @private
*/
__constr__: function(/** Generic.GatewayConnection
*/connection) {
this.connection = connection;
this.commandQ = [];
this.commandLimit = 4;
this.commandTimestamp = 0;
assert(DEFS.LIP_CMD_REPLY_MASK!==undefined);
assert(DEFS.LIP_CMD_CMD_MASK!==undefined);
assert(DEFS.LIP_CMD_REPLY_OK!==undefined);
assert(DEFS.LIP_CMD_REPLY_ERR!==undefined);
},

/**
* Array of outsnading commands.
* @type Array
*/
commandQ: null,
/**
* Maximum mnumbers of commands to queue.
* @type Number
*/
commandLimit: 4,
/**
* Timer to feed next command.
* @type Timer.Timer
*/
commandTimer: null,
/**
* Timestamp of last command sent.
* @type Number
*/
commandTimestamp: 0,

/**
* @returns {Sonoran.Mote}
*/
getMote: function() {
return this.connection.mote;
},
/**
* @private
*/
clear: function() {
if (this.commandTimer) {
this.commandTimer.cancel();
this.commandTimer = null;
}
this.commandQ = [];
},

/**
* @param blob
* @private
*/
onData: function(/** Sonoran.Event.Media */blob) {
var cmdbyte = blob.data.charCodeAt(0);
assert((cmdbyte&DEFS.LIP_CMD_REPLY_MASK) !== 0);
var cmdno = (cmdbyte&DEFS.LIP_CMD_CMD_MASK);

//QUACK(0, sprintf("%s: command reply: %d", this,


cmdno));

if (!MRv6.cmdno2str[cmdno]) {
Logger.log(Logger.DEBUG, MRv6.LOGGER_NAME, sprintf("%s:
received response for non-existing command: %H", this,
blob.data));
return;
}

var data = blob.data.substr(1);


var cmdreplyok = (cmdbyte&DEFS.LIP_CMD_REPLY_OK);
var cmdreplyerr = (cmdbyte&DEFS.LIP_CMD_REPLY_ERR);
var cmd = this.commandQ[0];
//QUACK(0, sprintf("%s: command reply: %d %d %d", this,
cmdno, cmdreplyok, cmdreplyerr));
if (!cmd) {
var msg = sprintf("%s: received unexpected %s reply for
a command '0x%x' (data=%H)", this, cmdreplyerr ? "error " : "",
cmdno, data);
Logger.log(Logger.WARN, Sonoran.Logger.GATEWAY, msg,
undefined, this.getMote());
return;
}

var status;
if (cmdreplyerr != 0) {
var msg = sprintf("%s: command '%s' failed: data=%H",
this, MRv6.cmdno2str[cmdno], data);
Logger.log(Logger.ERR, Sonoran.Logger.GATEWAY, msg,
undefined, this.getMote());
status = new AOP.ERR(msg);
} else if (cmdno != cmd.cmdno) {
var msg = sprintf("%s: received reply for command '0x%x'
instead of for command '%s'", this, cmdno,
MRv6.cmdno2str[cmd.cmdno]);
Logger.log(Logger.ERR, Sonoran.Logger.GATEWAY, msg,
undefined, this.getMote());
status = new AOP.ERR(msg);
} else {
status = new AOP.OK(data);
}
cmd.close(status);
},

/**
* Send a simple command expecting a response.
* @param bytes binary string
* @param timer forwarded to socket, timers associated with
target motes
* @param callback
* @private
*/
exchangeCommand: function(/** String */bytes , /**
Timer.Timer|Timer.Timer[] */timer, /** DFLT_ASYNC_CB */callback) {
if (BC.is(callback)) { return BC.exec(arguments.callee,
this, arguments); }
assert(typeof(bytes) === 'string');
if (!this.connection.isConnected()) {
callback(new AOP.ERR("Not connected!"));
return;
}
var cmdno = bytes.charCodeAt(0);
if (!MRv6.cmdno2str[cmdno]) {
callback(new AOP.ERR(sprintf("%s: unconfigured command
byte: %d", this, cmdno)));
return;
}
if (this.commandQ.length >= this.commandLimit) {
callback(new AOP.ERR(sprintf("%s: maximum number of
commands reached", this)));
return;
}
var cmd = new MRv6.Connection.Command(this, bytes, timer,
callback);
this.commandQ.push(cmd);
if (this.commandQ.length === 1) {
this.nextCommand();
}
},
/**
* @private
*/
onCommand: function(/** MRv6.Connection.Command */cmd, /**
AOP.Result */result) {
if (this.commandQ && this.commandQ.length>0 &&
this.commandQ[0] === cmd) {
this.commandQ.splice(0, 1);
this.nextCommand();
}
try {
cmd.callback(result);
} catch (x) {
QUACK(0, Runtime.dumpException(x, "MRv6.onCommand
callback failed: " + x));
}
},

/**
* @private
*/
nextCommand: function() {
var _this = this;
if (this.commandQ.length===0) {
return;
}
var now = Clock.get();
var diff = now - this.commandTimestamp;
if (diff >= MRv6.COMMAND_DELAY) {
this.commandTimestamp = now;
this.commandQ[0].send();
} else {
this.commandTimer = new Timer.Timer(MRv6.COMMAND_DELAY -
diff, undefined, function() {
_this.commandTimer = null;
_this.commandTimestamp = Clock.get();
_this.commandQ[0].send();
});
this.commandTimer.start();
}
},

/**
* @returns {String}
*/
toString: function() {
return this.connection.toString();
}
}
);

Class.define(
"MRv6.Connection.Command",
/**
* @lends MRv6.Connection.Command.prototype
*/
{
/**
* @constructs
* @param queue
* @param bytes
* @param timers
* @param callback
* @private
*/
__constr__: function(/** MRv6.Connection.CommandQueue
*/queue,/** String */bytes, /** Timer.Timer|Timer.Timer[]
*/timers, /** function */callback) {
this.queue = queue;
this.bytes = bytes;
this.cmdno = bytes.charCodeAt(0);
this.timer = null;
this.timers = timers;
this.callback = callback;
},

/**
* Close command execution
* @param result
* @private
*/
close: function(/** AOP.OK */result) {
if (this.timer) {
this.timer.cancel();
this.timer = null;
}
this.queue.onCommand(this, result);
},

/**
* Send this command
* @private
*/
send: function() {
var _this = this;
var connection = this.queue.connection;
var mote = connection.mote;
var srvport = connection.srvport;
this.timer = new Timer.Timer(10000, mote,
function(status) {
_this.timer = null;
var msg = sprintf("Connection %s: TIMEOUT for command
'%s'", mote, MRv6.cmdno2str[_this.cmdno]);
_this.close(new AOP.ERR(ERR_TIMEOUT, msg));
});
try {
connection.clsock.send(this.bytes, mote, srvport,
this.timer);
} catch (ex) {
var msg = sprintf("Connection %s: cannot send '%s'-
message", mote, MRv6.cmdno2str[_this.cmdno]);
this.close(AOP.Ex2ERR(ERR_GENERIC, ex, msg));
}
}
}
);

/**
* @class
* @static
*/
MRv6.Tunnel = {
/**
* @type Sonoran.UDP.Bridge
* @private
*/
tunnel: null,

/**
* @returns {Sonoran.UDP.Bridge}
*/
createTunnel: function() {
var mote = MRv6.findMoteBySaddr(DEFS.EDGE_SADDR);
if (!mote) {
throw new Exception("Missing edge mote!");
}
if (this.tunnel) {
this.tunnel.close();
}
this.tunnel = new Sonoran.UDP.Bridge(mote);
this.tunnel.open(undefined, BLCK);
this.tunnel.setRemote("127.0.0.1", DEFS.TUNNEL_UDP_PORT);

var ev = new Sonoran.Event.Media(0xffff, mote,


DEFS.LIP_ASM_MR_PORT, Formatter.pack("1u", DEFS.LIP_EV_STARTED));
try {
this.tunnel.send2Remote(ev);
} catch(ex) {
Logger.warn("Could not send packet to MRv6 tunnel, tunnel
inactive?");
}
return this.tunnel;
},

/**
* @returns {Sonoran.UDP.Bridge}
*/
getTunnel: function() {
return this.tunnel;
}
};

/**
* @namespace MRv6.ShellUtils
* @private
*/
MRv6.ShellUtils = {
/**
* Utility function for mrsh script. Create number of wireless
motes, one after the other and
* wait for them to associate with the wlip gateway. This also
works for a high number of
* motes. A 'mote-create -w cnt' creates them in parallel and
they wont attach all
* if too many are started.
*/
createMotes: function(motes, /** String */num, /** String
*/uniqueid, /** String */dll, /** String */packetErrorRate) {
num = parseInt(num);
var uuid = Util.UUID.eui642hex(uniqueid);
for(var i = 0; i < num; i++) {
var settings = new Sonoran.MoteSettings();
//QUACK(0, "Wireless mote: " + uuid);
settings.eui64 = Util.UUID.hex2eui64(uuid);
settings.dll = dll;
settings.wireless = true;
if (packetErrorRate) {
settings.packetErrorRate = parseInt(packetErrorRate);
}
Sonoran.createMote2(settings, 1, 3000, BLCK);
var idx = uuid.length - 2;
var c = uuid.substr(idx);
var n = parseInt(c, 16) + 1;
uuid = uuid.substr(0, idx) + Formatter.numToHex(n, 2);
}
}
};

/**
* Exchange LIP commands with wired edge or other MRv6 nodes.
* @private
*/
MRv6.LIP = {
/**
* @param mote
* @returns {String}
* @private
*/
start: function(/** Sonoran.Mote */mote) {
return this.exec(mote, DEFS.LIP_CMD_START, undefined);
},

/**
* @param mote
* @returns {String}
* @private
*/
stop: function(/** Sonoran.Mote */mote) {
return this.exec(mote, DEFS.LIP_CMD_STOP, undefined);
},

/**
* @param mote
* @returns {String}
* @private
*/
info: function(/** Sonoran.Mote */mote) {
var data = this.exec(mote, DEFS.LIP_CMD_INFO, undefined);
var obj = new Util.FormattedData(DEFS.INFO);
return obj.unpack(data);

},

/**
* @param mote
* @returns {String}
* @private
*/
configure: function(/** Sonoran.Mote */mote, /**
Util.FormattedData */config) {
var bytes;
if (config) {
bytes = config.pack();
}
bytes = this.exec(mote, DEFS.LIP_CMD_CONFIG, bytes);
return MRv6.unpackConnConf(bytes);
},

/**
* @param mote
* @param cmdno
* @param payload
* @returns {String}
* @private
*/
exec: function(/** Sonoran.Mote */mote, /* Number */cmdno, /**
String */payload) {
var cmdstr = MRv6.cmdno2str[cmdno];
var data = Formatter.pack("1u", cmdno);
if (payload) {
data += payload;
}
var ret = mote.exchange(DEFS.LIP_ASM_MR_PORT, data, 3000,
undefined, SCB);
if (ret.code !== 0) {
throw new Exception(sprintf("Command exchange failed for
mote '%s': %s", mote, data));
}
var blob = ret.getData();
//QUACK(0, "BLOB: " + blob);

var cmdbyte = blob.data.charCodeAt(0);


assert((cmdbyte&DEFS.LIP_CMD_REPLY_MASK) !== 0);

if (cmdno !== (cmdbyte&DEFS.LIP_CMD_CMD_MASK)) {


var msg = sprintf("%s: received reply for command '%x'
instead of for command '%s'", this, cmdbyte, cmdstr);
Logger.log(Logger.ERR, Sonoran.Logger.GATEWAY, msg,
undefined, mote);
throw new Exception(msg);
}

data = blob.data.substr(1);
var cmdreplyok = (cmdbyte&DEFS.LIP_CMD_REPLY_OK);
var cmdreplyerr = (cmdbyte&DEFS.LIP_CMD_REPLY_ERR);
if (cmdreplyerr != 0) {
var msg = sprintf("%s: command '%s' failed: data=%H",
mote, cmdstr, data);
Logger.log(Logger.ERR, Sonoran.Logger.GATEWAY, msg,
undefined, mote);
throw new Exception(msg);
}

return data;
}
};

//
------------------------------------------------------------------
--
//
// Shell comands
//
//
------------------------------------------------------------------
--

/**
* @class
* @private
*/
MRv6.Commands = {
/**
* @class
* @private
*/
MRv6: {}
};

/**
* @class
* @private
*/
MRv6.Commands.MRv6.SetupCommand = function(shell, name) {
this.description =
"Setup edge mote. Download assembly, connect, configure and
start network protocol.";
var keys = MRv6.getConnConfKeys();
var key2opt = {};
var key2spec = {};
var opts = [];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var spec = new GetOpt.Number(key, key);
var so = null;
if (key === 'maxDepth') { so = "d"; }
if (key === 'numChildren') { so = "c"; }
if (key === 'maxMotes') { so = "m"; }
var opt = new GetOpt.Option(so, "--"+key, 0, null, null,
spec);
key2opt[key] = opt;
key2spec[key] = spec;
opts.push(opt);
}
this.key2opt = key2opt;
this.key2spec = key2spec;
this.noTunnelOpt = new GetOpt.Option("t", "--no-tunnel", 0,
null, "Do not start IPv6 tunnel");
this.noMSetupOpt = new GetOpt.Option("n", "--no-msetup", 0,
null, "Do not upgrade 6lowpan assembly");
opts.push(this.noTunnelOpt, this.noMSetupOpt);
var optSet = new GetOpt.OptionSet(opts);
var cmdSpec = new Sonoran.CLI.Spec(name,
Sonoran.CLI.DST_ONE_MOTE);
CLI.Command.call(this, shell, cmdSpec, [ optSet ]);
};

/** @private */
MRv6.Commands.MRv6.SetupCommand.prototype = extend(
CLI.Command.prototype,
{
/** @private */
exec: function(callback) {
var mote = this.cmdSpec.getMote();
var attr = MRv6.lookupMoteAttr(mote);
if (attr) {
callback(new AOP.ERR("Mote is already
connected/edge!"));
return;
}

var opts = {};


for (var key in this.key2opt) {
var opt = this.key2opt[key];
var spec = this.key2spec[key];
if (opt.isSet()) {
opts[key] = spec.getNumber();
}
}

var arr = MRv6.setupConnection(mote, opts,


this.noTunnelOpt.isSet(), this.noMSetupOpt.isSet());
var conn = arr[0];
callback(new AOP.OK(conn.toString()));
}
}
);

/**
* @class
* @private
*/
MRv6.Commands.MRv6.ConnectCommand = function(shell, name) {
this.description =
"Without parameters, list current 6lowpan network.\n" +
"If '-a' and mote is specified, attach to this edge mote.\n"
+
"If '-d' and mote is specified, detach from this edge
mote.\n" +
"Use '--suspend' to broadcast 'suspend' event to all
motes.\n" +
"Use '--resume' to broadcast 'resume' event to all motes.\n";
this.attachOpt = new GetOpt.Option("a", "--attach", 0, null,
null);
this.detachOpt = new GetOpt.Option("d", "--detach", 0, null,
null);
this.resumeOpt = new GetOpt.Option(null, "--resume", 0, null,
null);
this.suspendOpt = new GetOpt.Option(null, "--suspend", 0,
null, null);
var optSet = new GetOpt.OptionSet([ this.attachOpt,
this.detachOpt, this.suspendOpt, this.resumeOpt ]);
var cmdSpec = new Sonoran.CLI.Spec(name,
Sonoran.CLI.DST_ANY_MOTE);
CLI.Command.call(this, shell, cmdSpec, [ optSet ]);
};

/** @private */
MRv6.Commands.MRv6.ConnectCommand.prototype = extend(
CLI.Command.prototype,
{
/** @private */
exec: function(callback) {
if (this.suspendOpt.isSet() || this.resumeOpt.isSet()) {
var conn = MRv6.getConnection();
if (!conn) { throw new Exception("No active
connection!"); }
conn.ctrlflow(this.suspendOpt.isSet() ? 1 : 0);
callback(new AOP.OK());
return;
}
if (this.detachOpt.isSet()) {
var conn = MRv6.getConnection();
if (!conn) { throw new Exception("No active
connection!"); }
conn.detach();
callback(new AOP.OK());
return;
}
if (this.attachOpt.isSet()) {
var motes = this.cmdSpec.getMotes();
if (!motes || motes.length===0) {
callback(new AOP.ERR("Missing mote
specification!"));
return;
}
var mote = motes[0];
var conn = new MRv6.Connection(mote);
conn.attach();
conn.query();
callback(new AOP.OK());
return;
}

var t = new Util.Formatter.Table2(4);


t.setTitle("Mote", "Parent", "Address", "Hops");
var y = 0;
//var motes =
Sonoran.Registry.getMotes().sort(function(m1, m2) { return
m1.getUid() - m2.getUid(); });
var motes = Sonoran.Registry.getMotes();
for (var i = 0; i < motes.length; i++) {
var mote = motes[i];
var attr = MRv6.lookupMoteAttr(mote);
if (attr && attr.isEdge()) {
t.setValue(0, y, mote.getUniqueid());
t.setValue(1, y, "Gateway");
t.setValue(2, y, attr.saddr.toString(16));
t.setValue(3, y, attr.getHops());
y += 1;
} else if (attr) {
t.setValue(0, y, mote.getUniqueid());
var pm = attr.lookupParentMote();
t.setValue(1, y, pm ? pm.getUniqueid() : "---");
t.setValue(2, y, attr.saddr.toString(16));
t.setValue(3, y, attr.getHops());
y += 1;
}
}
callback(new AOP.OK(t.render().join("\n")));
}
}
);

/**
* @class
* @private
*/
MRv6.Commands.MRv6.StartCommand = function(shell, name) {
this.description = "Start the radio protocol on a mote using
LIP.";
var optSet = new GetOpt.OptionSet([]);
var cmdSpec = new Sonoran.CLI.Spec(name,
Sonoran.CLI.DST_MANY_MOTES);
CLI.Command.call(this, shell, cmdSpec, [ optSet ]);
};

/** @private */
MRv6.Commands.MRv6.StartCommand.prototype = extend(
CLI.Command.prototype,
{
/** @private */
exec: function(callback) {
var motes = this.cmdSpec.getMotes();
for (var i = 0; i < motes.length; i++) {
MRv6.LIP.start(motes[i]);
}
callback(new AOP.OK());
}
}
);

/**
* @class
* @private
*/
MRv6.Commands.MRv6.StopCommand = function(shell, name) {
this.description = "Stop the radio protocol on a mote using
LIP.";
var optSet = new GetOpt.OptionSet([]);
var cmdSpec = new Sonoran.CLI.Spec(name,
Sonoran.CLI.DST_MANY_MOTES);
CLI.Command.call(this, shell, cmdSpec, [ optSet ]);
};
/** @private */
MRv6.Commands.MRv6.StopCommand.prototype = extend(
CLI.Command.prototype,
{
/** @private */
exec: function(callback) {
var motes = this.cmdSpec.getMotes();
for (var i = 0; i < motes.length; i++) {
MRv6.LIP.stop(motes[i]);
}
callback(new AOP.OK());
}
}
);

/**
* @class
* @private
*/
MRv6.Commands.MRv6.InfoCommand = function(shell, name) {
this.description =
"Print information messages of a mote having reached the
edge." +
"Use '-l' to ask for an info message using a LIP command.";
this.lipOpt = new GetOpt.Option("l", "--lip", 0, null, null);
var optSet = new GetOpt.OptionSet([ this.lipOpt ]);
var cmdSpec = new Sonoran.CLI.Spec(name,
Sonoran.CLI.DST_MANY_MOTES);
CLI.Command.call(this, shell, cmdSpec, [ optSet ]);
};

/** @private */
MRv6.Commands.MRv6.InfoCommand.prototype = extend(
CLI.Command.prototype,
{
/** @private */
exec: function(callback) {
var ret = [];
var motes = this.cmdSpec.getMotes();
for (var i = 0; i < motes.length; i++) {
var mote = motes[i];
if (this.lipOpt.isSet()) {
var info = MRv6.LIP.info(mote);
ret.push(mote.getUniqueid() + ":\n" + info);
} else {
var conn = MRv6.getConnection();
var attr = MRv6.lookupMoteAttr(mote);
if (attr) {
var info = attr.info;
if (info) {
ret.push(mote.getUniqueid() + ":\n" + info);
}
}
}
}
callback(new AOP.OK(ret));
}
}
);

/**
* @class
* @private
*/
MRv6.Commands.MRv6.ConfigCommand = function(shell, name) {
this.description = "Configure mrv6.";
var keys = MRv6.getConnConfKeys();
var key2opt = {};
var key2spec = {};
var opts = [];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var spec = new GetOpt.Number(key, key);
var opt = new GetOpt.Option(null, "--"+key, 0, null, null,
spec);
key2opt[key] = opt;
key2spec[key] = spec;
opts.push(opt);
}
this.key2opt = key2opt;
this.key2spec = key2spec;
var optSet = new GetOpt.OptionSet(opts);
var cmdSpec = new Sonoran.CLI.Spec(name,
Sonoran.CLI.DST_MANY_MOTES);
CLI.Command.call(this, shell, cmdSpec, [ optSet ]);
};

/** @private */
MRv6.Commands.MRv6.ConfigCommand.prototype = extend(
CLI.Command.prototype,
{
/** @private */
exec: function(callback) {
var ret = [];
var motes = this.cmdSpec.getMotes();
for (var i = 0; i < motes.length; i++) {
var mote = motes[i];
var conf = MRv6.LIP.configure(mote, undefined);
var modified = false;
for (var key in this.key2opt) {
var opt = this.key2opt[key];
var spec = this.key2spec[key];
if (opt.isSet()) {
var num = spec.getNumber();
conf[key] = num;
modified = true;
}
}
if (modified) {
conf = MRv6.LIP.configure(mote, conf);
}
ret.push(conf);
}
callback(new AOP.OK(ret));
}
}
);

/**
* @class
* @private
*/
MRv6.Commands.MRv6.TunnelCommand = function(shell, name) {
this.description =
"Attach to a tunnel.";
CLI.Command.call(this, shell, name);
};

/** @private */
MRv6.Commands.MRv6.TunnelCommand.prototype = extend(
CLI.Command.prototype,
{
/** @private */
exec: function(callback) {
var tunif = MRv6.Tunnel.createTunnel();
callback(new AOP.OK(tunif));
}
}
);

CLI.commandFactory.addModule("MRv6.Commands.MRv6");

MRv6.Commands.v6 = MRv6.Commands.MRv6;
CLI.commandFactory.addModule("MRv6.Commands.v6");
Directorio mrv6.src.tmp

Archivo v6lowpan-lib.cs
Definitions and functions for the handling of the
IPHC header of a v6lowpan message
//

// (C) COPYRIGHT INTERNATIONAL BUSINESS MACHINES CORPORATION


2006-2009
// ALL RIGHTS RESERVED
// IBM Research Division, Zurich Research Laboratory
//
//
------------------------------------------------------------------
--

namespace com.ibm.saguaro.mrv6 {
using com.ibm.saguaro.system;
using com.ibm.saguaro.util;
using com.ibm.saguaro.buffers;

/// <summary>
/// Definitions and functions for the handling of the IPHC
header of a v6lowpan message.
/// </summary>
internal class IPHC {
/// <summary>IPHC constant.</summary>
internal const uint DISPATCH_VAL = 0x6; // bits: 0:0 1:1
2:1
/// <summary>IPHC constant.</summary>
internal const uint DISPATCH_MASK = 0x7;
/// <summary>IPHC constant.</summary>
internal const int DISPATCH_OFF = 0;

/// <summary>IPHC constant.</summary>


internal const uint TF_MASK = 0x18;
/// <summary>IPHC constant.</summary>
internal const int TF_OFF = 3;

/// <summary>IPHC constant.</summary>


internal const uint NH_MASK = 0x20;
/// <summary>IPHC constant.</summary>
internal const int NH_OFF = 5;

/// <summary>IPHC constant.</summary>


internal const uint HLIM_MASK = 0xc0;
/// <summary>IPHC constant.</summary>
internal const int HLIM_OFF = 6;

/// <summary>IPHC constant.</summary>


internal const uint CID_MASK = 0x1;
/// <summary>IPHC constant.</summary>
internal const int CID_OFF = 0;

/// <summary>IPHC constant.</summary>


internal const uint SAC_MASK = 0x2;
/// <summary>IPHC constant.</summary>
internal const int SAC_OFF = 1;

/// <summary>IPHC constant.</summary>


internal const uint SAM_MASK = 0xc;
/// <summary>IPHC constant.</summary>
internal const int SAM_OFF = 2;

/// <summary>IPHC constant.</summary>


internal const uint M_MASK = 0x10;
/// <summary>IPHC constant.</summary>
internal const int M_OFF = 4;

/// <summary>IPHC constant.</summary>


internal const uint DAC_MASK = 0x20;
/// <summary>IPHC constant.</summary>
internal const int DAC_OFF = 5;

/// <summary>IPHC constant.</summary>


internal const uint DAM_MASK = 0xc0;
/// <summary>IPHC constant.</summary>
internal const int DAM_OFF = 6;

/// <summary>
/// Parse IPHC structure into specified Packet p. Returns
offset into buf after IPHC.
/// </summary>
/// <param name="buf"> Packet buffer </param>
/// <param name="off"> Offset into buffer </param>
/// <param name="end"> End of buffer</param>
/// <param name="p"> Packet </param>
/// <returns>Offset or 0xffxx in case of parse
error</returns>
internal static uint parse(byte[] buf, uint off, uint end,
Packet p) {
if (off + 2 >= end) {
return 0xff00;
}
byte b1 = buf[off++];
if (((b1 & DISPATCH_MASK) >> DISPATCH_OFF) !=
DISPATCH_VAL) {
return 0xff01;
}
if (((b1 & TF_MASK) >> TF_OFF) != 0x3) {
return 0xff02;
}
if (((b1 & NH_MASK) >> NH_OFF) != 0x1) {
return 0xff03;
}
if (((b1 & HLIM_MASK) >> HLIM_OFF) != 0x2) {
return 0xff04;
}
byte b2 = buf[off++];
uint cid = ((b2 & CID_MASK) >> CID_OFF);
if (cid != 0x0) {
return 0xff05;
}
uint sac = ((b2 & SAC_MASK) >> SAC_OFF);
if (sac != 0x0) {
return 0xff06;
}
uint sam = ((b2 & SAM_MASK) >> SAM_OFF);
if (!Address.isValidAddressMode(sam)) {
return 0xff07;
}
uint m = ((b2 & M_MASK) >> M_OFF);
if (m != 0x0) {
return 0xff08;
}
uint dac = ((b2 & DAC_MASK) >> DAC_OFF);
if (dac != 0x0) {
return 0xff09;
}
uint dam = ((b2 & DAM_MASK) >> DAM_OFF);
if (!Address.isValidAddressMode(dam)) {
return 0xff07;
}

// context identifier follows if specified


//uint contextIdentifier = 0;

// source address
uint cnt = Address.mode2addressBitsLength(sam);
if (off + cnt >= end) {
return 0xff00;
}
Address.setAddress(p.srcaddr, 0, sam, buf, off);
off += cnt;
// destination address
cnt = Address.mode2addressBitsLength(dam);
if (off + cnt >= end) {
return 0xff00;
}
Address.setAddress(p.dstaddr, 0, dam, buf, off);
off += cnt;

return off;
}

/// <summary>
/// Packet addresses specified destination short address?
/// </summary>
/// <returns>0 if short address is matched</returns>
internal static bool addressesDstSaddr(byte[] buf, uint
off, uint end, uint saddr) {
//byte b1 = buf[off++];
byte b2 = buf[off + 1];
off += 2;
uint sam = ((b2 & SAM_MASK) >> SAM_OFF);
if (!Address.isValidAddressMode(sam)) {
return false;
}
uint dam = ((b2 & DAM_MASK) >> DAM_OFF);
if (!Address.isValidAddressMode(dam)) {
return false;
}
if (dam != Address.ADDRESS_MODE_SADDR) {
return false;
}
// source address
uint cnt = Address.mode2addressBitsLength(sam);
if (off + cnt >= end) {
return false;
}
return Util.get16le(buf, off + cnt) == saddr;
}

/// <summary>
/// Calculate required size for IPHC. Source address is
expected to be short
/// address based, i.e. 16bits wide.
/// </summary>
internal static uint calcSize(byte[] dstaddressBuf, uint
dstaddressOff) {
uint dam = Address.getAddressMode(dstaddressBuf,
dstaddressOff);
return 2 + Address.mode2addressBitsLength(dam) + 2;
}

/// <summary>
/// Calculate required size for IPHC.
/// </summary>
internal static uint calcSize(byte[] dstaddressBuf, uint
dstaddressOff, byte[] srcaddressBuf, uint srcaddressOff) {
uint dam = Address.getAddressMode(dstaddressBuf,
dstaddressOff);
uint sam = Address.getAddressMode(srcaddressBuf,
srcaddressOff);
return 2 + Address.mode2addressBitsLength(dam) +
Address.mode2addressBitsLength(sam);
}

/// <summary>
/// Marshal IPHC into buffer for specified destination
address.
/// </summary>
/// <param name="buf"> Buffer </param>
/// <param name="off"> Offset </param>
/// <param name="dstaddr"> Address </param>
/// <param name="dstoff"> Offset </param>
/// <param name="saddr"> Source address (16-bit short)
</param>
/// <param name="nextHeader"> Next header, 0 or 1 </param>
/// <returns>size</returns>
internal static uint generate(byte[] buf, uint off, byte[]
dstaddr, uint dstoff, uint saddr, uint nextHeader) {
byte b1 = (byte)((0x6<<DISPATCH_OFF) | (0x3<<TF_OFF) |
(nextHeader<<NH_OFF) | (0x2<<HLIM_OFF));

uint sac = 0x0;


uint sam = Address.ADDRESS_MODE_SADDR;
uint dac = Address.getAddressCompression(dstaddr,
dstoff);
uint dam = Address.getAddressMode(dstaddr, dstoff);

byte b2 = (byte)((0<<CID_OFF) | (sac<<SAC_OFF) |


(sam<<SAM_OFF) | (0 << M_OFF) | (dac<<DAC_OFF) | (dam<<DAM_OFF));
buf[off++] = b1;
buf[off++] = b2;

Util.set16le(buf, off, saddr);


off += 2;

off += Address.copyBits(dstaddr, dstoff, buf, off);


return off;
}

/// <summary>
/// Marshal IPHC into buffer for specified source and
destnation address.
/// </summary>
/// <param name="buf"> Buffer </param>
/// <param name="off"> Offset </param>
/// <param name="dstaddr"> Address </param>
/// <param name="dstoff"> Offset </param>
/// <param name="srcaddr"> Address </param>
/// <param name="srcoff"> Offset </param>
/// <param name="nextHeader"> Next header, 0 or 1 </param>
/// <returns>size</returns>
internal static uint generate(byte[] buf, uint off, byte[]
dstaddr, uint dstoff, byte[] srcaddr, uint srcoff, uint
nextHeader) {
byte b1 = (byte)((0x6<<DISPATCH_OFF) | (0x3<<TF_OFF) |
(nextHeader<<NH_OFF) | (0x2<<HLIM_OFF));
uint sac = Address.getAddressCompression(srcaddr,
srcoff);
uint sam = Address.getAddressMode(srcaddr, srcoff);
uint dac = Address.getAddressCompression(dstaddr,
dstoff);
uint dam = Address.getAddressMode(dstaddr, dstoff);

byte b2 = (byte)((0<<CID_OFF) | (sac<<SAC_OFF) |


(sam<<SAM_OFF) | (0 << M_OFF) | (dac<<DAC_OFF) | (dam<<DAM_OFF));
buf[off++] = b1;
buf[off++] = b2;

off += Address.copyBits(srcaddr, srcoff, buf, off);


off += Address.copyBits(dstaddr, dstoff, buf, off);
return off;
}

/// <summary>
/// Definitions and functions for the handling of the next
header format of a v6lowpan message.
/// So far only UDP messages are supported.
/// NHC handling.
/// </summary>
internal class NHC {
/// <summary>NHC constant.</summary>
internal const uint DISPATCH_VAL = 0xf;
/// <summary>NHC constant.</summary>
internal const uint DISPATCH_MASK = 0xf;
/// <summary>NHC constant.</summary>
internal const int DISPATCH_OFF = 0;

/// <summary>NHC constant.</summary>


internal const uint UDP_MASK = 0x10;
/// <summary>NHC constant.</summary>
internal const int UDP_OFF = 4;

/// <summary>NHC constant.</summary>


internal const uint CHECKSUM_MASK = 0x20;
/// <summary>NHC constant.</summary>
internal const int CHECKSUM_OFF = 5;

/// <summary>NHC constant.</summary>


internal const uint PORTS_MASK = 0xc0;
/// <summary>NHC constant.</summary>
internal const int PORTS_OFF = 6;

/// <summary>NHC constant.</summary>


internal const byte HDR_BYTE = (byte)((DISPATCH_VAL <<
DISPATCH_OFF) | (0x0 << UDP_OFF) | (0x1 << CHECKSUM_OFF) | (0x0 <<
PORTS_OFF));

/// <summary>
/// Parse UDP next header structure into specified
InPacket.
/// Returns offset into buf after NHC.
/// </summary>
/// <param name="buf"> Packet buffer </param>
/// <param name="off"> Offset </param>
/// <param name="p"> Packet </param>
/// <returns>Offset or 0xffxx in case of parse
error</returns>
internal static uint parseUdp(byte[] buf, uint off, Packet
p) {
if (((buf[off] & UDP_MASK) >> UDP_OFF) != 0x0) {// ->
its a UDP NHC format
return 0xff00;
}
if (((buf[off] & DISPATCH_MASK) >> DISPATCH_OFF) !=
DISPATCH_VAL) {
return 0xff01;
}
if (((buf[off] & CHECKSUM_MASK) >> CHECKSUM_OFF) !=
0x1) {
return 0xff02;
}
if (((buf[off] & PORTS_MASK) >> PORTS_OFF) != 0x0) {
return 0xff03;
}
off += 1;
uint srcport = Util.get16le(buf, off);
p.srcport = srcport;
off += 2;
uint dstport = Util.get16le(buf, off);
p.dstport = dstport;
off += 2;
return off;
}

/// <summary>
/// Calculate required size for UDP NHC.
/// </summary>
/// <param name="dstport"> Port </param>
/// <param name="srcport"> Port </param>
/// <returns>size</returns>
internal static uint calcUdpSize(uint dstport, uint
srcport) {
return 5;
}

/// <summary>
/// Marshal ports and payload into UDP NHC format.
/// </summary>
/// <param name="buf">buffer</param>
/// <param name="off">offset</param>
/// <param name="srcport">source port</param>
/// <param name="dstport">dest port</param>
/// <param name="payloadLen">payload length</param>
/// <returns>offset into buffer after UDP NHC</returns>
internal static uint generateUdp(byte[] buf, uint off,
uint srcport, uint dstport, uint payloadLen) {
buf[off++] = HDR_BYTE;
Util.set16le(buf, off, srcport);
off += 2;
Util.set16le(buf, off, dstport);
off += 2;
return off;
}
}

/// <summary>
/// Mesh header declarations and functions.
/// </summary>
internal class MHC {
/// <summary>MHC constant.</summary>
internal const uint TYPE_TRANSMISSION_PATH = 1;
/// <summary>MHC constant.</summary>
internal const uint TYPE_LIFESIGN_REQUEST = 2;
/// <summary>MHC constant.</summary>
internal const uint TYPE_SUSPEND_REQUEST = 3;
/// <summary>MHC constant.</summary>
internal const uint TYPE_RESUME_REQUEST = 4;

/// <summary>MHC constant.</summary>


internal const uint TRANSMISSION_SLOTCNT_OFF = 1;
internal const uint TRANSMISSION_SLOTS_OFF = 2;

/// <summary>MHC constant.</summary>


internal const uint DISPATCH_VAL = 0x1;
/// <summary>MHC constant.</summary>
internal const uint DISPATCH_MASK = 0x3;
/// <summary>MHC constant.</summary>
internal const int DISPATCH_OFF = 0;

/// <summary>MHC constant.</summary>


internal const uint TYPE_MASK = 0xfc;
/// <summary>MHC constant.</summary>
internal const int TYPE_OFF = 2;

/// <summary>returns true if specified PDU has an MHC


header.</summary>
internal static bool isMHC(byte[] pdu, uint off){
return (((pdu[off]&DISPATCH_MASK) >> DISPATCH_OFF) ==
DISPATCH_VAL);
}

/// <summary>Returns offset into PDU after MHC header.


</summary>
internal static uint skip(byte[] pdu, uint off){
byte b = pdu[off];
if (((b&DISPATCH_MASK) >> DISPATCH_OFF) !=
DISPATCH_VAL) {
return off;
}
ASSERT((getType(pdu,off)>=TYPE_TRANSMISSION_PATH)||
(getType(pdu,off)<=TYPE_RESUME_REQUEST));
return (uint)(off + TRANSMISSION_SLOTS_OFF + 2 *
pdu[off + TRANSMISSION_SLOTCNT_OFF]);
}

/// <summary>Returns type of MHC header.</summary>


internal static uint getType(byte[] pdu, uint off){
byte b = pdu[off];
uint type = ((b&TYPE_MASK) >> TYPE_OFF);
ASSERT((type>=TYPE_TRANSMISSION_PATH)||
(type<=TYPE_RESUME_REQUEST));
return type;
}

/// <summary>Returns header byte for specified MHC header


type.</summary>
internal static byte genHeaderByte(uint type) {
ASSERT((type>=TYPE_TRANSMISSION_PATH)||
(type<=TYPE_RESUME_REQUEST));
return (byte)((DISPATCH_VAL<<DISPATCH_OFF) |
(type<<TYPE_OFF));
}
}

/// <summary>
/// 6lowpan addresses are encoded in a byte array. First byte
contains
/// address mode etc, then a variable length of address bits
follow.
/// byte 0:
/// bit 0: CID Context Identifier Extension
/// bit 1: Address Compression
/// bit 2-4: Address Mode
/// bit 5-8: Context Identifier
/// byte 1-3, 1-8, 1-16: Address information, Address bits
/// So far, short address, extended eui-64 based addresses and
full
/// ipv6 addresses are supported for being stored in the
address bits.
/// </summary>
public class Address {
/// <summary>Maximum number of bytes necessary to store
any kind of address.</summary>
public const uint MAX_LEN = 17;

/// <summary> Address bits in first header byte.</summary>


public const uint CONTEXT_ID_MASK = 0xf0;
/// <summary> Address bits in first header byte.</summary>
public const int CONTEXT_ID_OFF = 4;

/// <summary> Address bits in first header byte.</summary>


public const uint ADDRESS_MODE_MASK = 0x0c;
/// <summary> Address bits in first header byte.</summary>
public const int ADDRESS_MODE_OFF = 2;
/// <summary> Address bits in first header byte.</summary>
public const uint ADDRESS_COMPRESSION_MASK = 0x02;
/// <summary> Address bits in first header byte.</summary>
public const int ADDRESS_COMPRESSION_OFF = 1;

/// <summary> Address bits in first header byte.</summary>


public const uint CONTEXT_EXTENSION_MASK = 0x01;
/// <summary> Address bits in first header byte.</summary>
public const int CONTEXT_EXTENSION_OFF = 0;

/// <summary> Address mode: addrss bits contain a short


address.</summary>
public const byte ADDRESS_MODE_SADDR = 0x2;
/// <summary> Address mode: address bits contain an eui-64
address.</summary>
public const byte ADDRESS_MODE_XADDR = 0x1;
/// <summary> Address mode: address bits contain a full
ipv6 address.</summary>
public const byte ADDRESS_MODE_FULL = 0x0;

/// <summary>Return whether specified value is a valid


address mode.</summary>
internal static bool isValidAddressMode(uint am) {
return (am==ADDRESS_MODE_SADDR||
am==ADDRESS_MODE_XADDR||am==ADDRESS_MODE_FULL);
}

/// <summary>
/// Create address. No address compression, no context
identifier used.
/// Address mode might be ADDRESS_MODE_SADDR,
ADDRESS_MODE_XADDR. ADDRESS_MODE_FULL.
/// </summary>
/// <param name="addressBuf"> Address buffer</param>
/// <param name="addressOff"> Offset into address
buffer</param>
/// <param name="am"> Address mode</param>
/// <param name="bdat"> Buffer where addresss data
resides</param>
/// <param name="boff"> Offset into buffer where addresss
data resides</param>
/// <returns>Length in bytes of address</returns>
public static uint setAddress(byte[] addressBuf, uint
addressOff, uint am, byte[] bdat, uint boff) {
uint ci = 0x0;
uint ce = 0x0;
uint ac = 0x0;
addressBuf[addressOff + 0] = (byte)
((ci<<Address.CONTEXT_ID_OFF) |
(ce<<Address.CONTEXT_EXTENSION_OFF) |
(am<<Address.ADDRESS_MODE_OFF) |
(ac<<Address.ADDRESS_COMPRESSION_OFF));
uint cnt = Address.mode2addressBitsLength(am);
Util.copyData(bdat, boff, addressBuf, addressOff+1,
cnt);
return 1+cnt;
}

/// <summary>
/// Create address.
/// </summary>
/// <param name="addressBuf"> Address buffer</param>
/// <param name="addressOff"> Offset into address
buffer</param>
/// <param name="ci"> Context identifier</param>
/// <param name="ce"> Context extension</param>
/// <param name="am"> Address mode</param>
/// <param name="ac"> Address compression</param>
/// <param name="bdat"> Buffer where addresss data
resides</param>
/// <param name="boff"> Offset into buffer where addresss
data resides</param>
/// <returns>Length in bytes of address</returns>
internal static uint setAddress(byte[] addressBuf, uint
addressOff, uint ci, uint ce, uint am, uint ac, byte[] bdat, uint
boff) {
addressBuf[addressOff + 0] = (byte)
((ci<<Address.CONTEXT_ID_OFF) |
(ce<<Address.CONTEXT_EXTENSION_OFF) |
(am<<Address.ADDRESS_MODE_OFF) |
(ac<<Address.ADDRESS_COMPRESSION_OFF));
uint cnt = Address.mode2addressBitsLength(am);
Util.copyData(bdat, boff, addressBuf, addressOff+1,
cnt);
return 1+cnt;
}

/// <summary>
/// Copy address bits from an address from a source buffer
to a destination buffer.
/// </summary>
/// <param name="addressBuf"> Address buffer </param>
/// <param name="addressOff"> Offset into address
buffer</param>
/// <param name="destBuf"> Destination for address bits
</param>
/// <param name="destOff">Offset into destination buffer
</param>
/// <returns>Number of copied bytes</returns>
internal static uint copyBits(byte[] addressBuf, uint
addressOff, byte[] destBuf, uint destOff) {
uint cnt = getLength(addressBuf, addressOff) - 1;
if (cnt > 0) {
Util.copyData(addressBuf, addressOff+1, destBuf,
destOff, cnt);
}
return cnt;
}

/// <summary>
/// Copy address from source to destination buffer.
/// </summary>
/// <param name="addressBuf">Address buffer</param>
/// <param name="addressOff">Offset into address buffer
</param>
/// <param name="destBuf"> Destination for address</param>
/// <param name="destOff">Offset into destination buffer
</param>
/// <returns>Number of copied bytes</returns>
public static uint copyAddress(byte[] addressBuf, uint
addressOff, byte[] destBuf, uint destOff) {
uint cnt = getLength(addressBuf, addressOff);
Util.copyData(addressBuf, addressOff, destBuf,
destOff, cnt);
return cnt;
}

/// <summary>
/// Compare two addresses stored in byte arrays.
/// </summary>
/// <param name="address1"> First address</param>
/// <param name="address2"> Second address</param>
/// <returns>0xffff if equal</returns>
public static uint compare(byte[] address1, byte[]
address2) {
return compare(address1, 0, address2, 0);
}

/// <summary>
/// Compare two addresses stored in byte arrays.
/// </summary>
/// <param name="address1"> Address buffer</param>
/// <param name="off1"> Offset into address buffer</param>
/// <param name="address2"> Address buffer</param>
/// <param name="off2"> Offset into address buffer</param>
/// <returns>0xffff if equal</returns>
public static uint compare(byte[] address1, uint off1,
byte[] address2, uint off2) {
byte hdr1 = address1[off1];
byte hdr2 = address2[off2];
ASSERT(getContextExtension(hdr1) == 0);
ASSERT(getContextExtension(hdr2) == 0);
ASSERT(getAddressCompression(hdr1) == 0);
ASSERT(getAddressCompression(hdr2) == 0);
uint am1 = getAddressMode(hdr1);
ASSERT(am1==ADDRESS_MODE_SADDR||
am1==ADDRESS_MODE_XADDR||am1==ADDRESS_MODE_FULL);
uint am2 = getAddressMode(hdr2);
ASSERT(am2==ADDRESS_MODE_SADDR||
am2==ADDRESS_MODE_XADDR||am2==ADDRESS_MODE_FULL);
if (am1 != am2) {
return 0;
}
uint cnt = getLength(address1, off1);
return Util.compareData(address1, off1, address2,
off2, cnt);
}

/// <summary>
/// Return total length of address in bytes.
/// </summary>
/// <param name="address"> Address buffer</param>
/// <param name="off"> Offset to address</param>
/// <returns>length in bytes</returns>
public static uint getLength(byte[] address, uint off) {
byte hdr = address[off];
ASSERT(getContextExtension(hdr) == 0);
ASSERT(getAddressCompression(hdr) == 0);
uint am = getAddressMode(hdr);
uint cnt = mode2addressBufLength(am);
ASSERT(off + cnt <= address.Length);
return cnt;
}

/// <summary>
/// Return total length of address in bytes.
/// </summary>
/// <param name="address"> Address buffer</param>
/// <returns>length in bytes</returns>
public static uint getLength(byte[] address) {
return getLength(address, 0);
}

/// <summary>
/// Return context extension.
/// </summary>
/// <param name="b">header byte</param>
/// <returns>bits</returns>
internal static uint getContextExtension(byte b) {
return ((b&CONTEXT_EXTENSION_MASK) >>
CONTEXT_EXTENSION_OFF);
}
/// <summary>
/// Return context identifier.
/// </summary>
/// <param name="b">header byte</param>
/// <returns>bits</returns>
internal static uint getContextIdentifier(byte b) {
return ((b&CONTEXT_ID_MASK) >> CONTEXT_ID_OFF);
}

/// <summary>
/// Return address compression.
/// </summary>
/// <param name="b">header byte</param>
/// <returns>bits</returns>
internal static uint getAddressCompression(byte b) {
return ((b&ADDRESS_COMPRESSION_MASK) >>
ADDRESS_COMPRESSION_OFF);
}

/// <summary>
/// Return address mode.
/// </summary>
/// <param name="b">header byte</param>
/// <returns>bits</returns>
public static uint getAddressMode(byte b) {
return ((b&ADDRESS_MODE_MASK) >> ADDRESS_MODE_OFF);
}

/// <summary>
/// Return context extension.
/// </summary>
/// <param name="address">address</param>
/// <returns>bits</returns>
internal static uint getContextExtension(byte[] address) {
return ((address[0]&CONTEXT_EXTENSION_MASK) >>
CONTEXT_EXTENSION_OFF);
}

/// <summary>
/// Return context identifier.
/// </summary>
/// <param name="address">address</param>
/// <returns>bits</returns>
internal static uint getContextIdentifier(byte[] address)
{
return ((address[0]&CONTEXT_ID_MASK) >>
CONTEXT_ID_OFF);
}

/// <summary>
/// Return address compression.
/// </summary>
/// <param name="address">address</param>
/// <returns>bits</returns>
internal static uint getAddressCompression(byte[] address)
{
return ((address[0]&ADDRESS_COMPRESSION_MASK) >>
ADDRESS_COMPRESSION_OFF);
}

/// <summary>
/// Return address mode.
/// </summary>
/// <param name="address">address</param>
/// <returns>bits</returns>
public static uint getAddressMode(byte[] address) {
return ((address[0]&ADDRESS_MODE_MASK) >>
ADDRESS_MODE_OFF);
}

/// <summary>
/// Return context extension.
/// </summary>
/// <param name="address">address</param>
/// <param name="off">offset</param>
/// <returns>bits</returns>
internal static uint getContextExtension(byte[] address,
uint off) {
return ((address[off]&CONTEXT_EXTENSION_MASK) >>
CONTEXT_EXTENSION_OFF);
}

/// <summary>
/// Return context identifier.
/// </summary>
/// <param name="address">address</param>
/// <param name="off">offset</param>
/// <returns>bits</returns>
internal static uint getContextIdentifier(byte[] address,
uint off) {
return ((address[off]&CONTEXT_ID_MASK) >>
CONTEXT_ID_OFF);
}

/// <summary>
/// Return address compression.
/// </summary>
/// <param name="address">address</param>
/// <param name="off">offset</param>
/// <returns>bits</returns>
internal static uint getAddressCompression(byte[] address,
uint off) {
return ((address[off]&ADDRESS_COMPRESSION_MASK) >>
ADDRESS_COMPRESSION_OFF);
}

/// <summary>
/// Return address mode.
/// </summary>
/// <param name="address">address</param>
/// <param name="off">offset</param>
/// <returns>bits</returns>
public static uint getAddressMode(byte[] address, uint
off) {
return ((address[off]&ADDRESS_MODE_MASK) >>
ADDRESS_MODE_OFF);
}

/// <summary>
/// Create 16-bit short address based v6lowpan address in
specified destination buffer.
/// </summary>
/// <param name="buf">address</param>
/// <param name="off">offset</param>
/// <param name="saddr">Short address</param>
/// <returns></returns>
public static uint fromSaddr(byte[] buf, uint off, uint
saddr) {
buf[off++] = (ADDRESS_MODE_SADDR <<
Address.ADDRESS_MODE_OFF);
Util.set16le(buf, off, saddr);
off += 2;
return off;
}

/// <summary>
/// Return whether 6lowpan address at specified buffer
location matches specified 16-bit short address.
/// </summary>
/// <param name="buf">address</param>
/// <param name="off">offset</param>
/// <param name="saddr">Short address</param>
/// <returns></returns>
public static bool isSaddr(byte[] buf, uint off, uint
saddr) {
return ((Address.getLength(buf, off) == 3) &&
(Util.get16le(buf, off+1) == saddr));
}

/// <summary>
/// Create 64-bit extended address based v6lowpan address
in specified destination buffer.
/// </summary>
/// <param name="addressBuf">address</param>
/// <param name="addressOff">offset</param>
/// <param name="xaddrBuf">xaddr buf</param>
/// <param name="xaddrOff">xaddr off</param>
/// <returns></returns>
public static uint fromXaddr(byte[] addressBuf, uint
addressOff, byte[] xaddrBuf, uint xaddrOff) {
addressBuf[addressOff++] = (ADDRESS_MODE_XADDR <<
Address.ADDRESS_MODE_OFF);
Util.copyData(xaddrBuf, xaddrOff, addressBuf,
addressOff, 8);
addressOff += 8;
return addressOff;
}

/// <summary>
/// Create 128-bit based v6lowpan address in specified
destination buffer.
/// </summary>
/// <param name="addressBuf">Destination address
buffer</param>
/// <param name="addressOff">Destination address
offset</param>
/// <param name="ipaddrBuf">Buffer with 16 bytes of ipv6
address</param>
/// <param name="ipaddrOff">Offset into buffer of ipv6
address</param>
/// <returns></returns>
public static uint fromIPv6addr(byte[] addressBuf, uint
addressOff, byte[] ipaddrBuf, uint ipaddrOff) {
addressBuf[addressOff++] = (ADDRESS_MODE_FULL <<
Address.ADDRESS_MODE_OFF);
Util.copyData(ipaddrBuf, ipaddrOff, addressBuf,
addressOff, 16);
addressOff += 16;
return addressOff;
}

/// <summary>
/// Return length of v6lowpan address based on address
mode.
/// </summary>
/// <param name="am">address mode</param>
/// <returns></returns>
internal static uint mode2addressBufLength(uint am) {
ASSERT(am==ADDRESS_MODE_SADDR||
am==ADDRESS_MODE_XADDR||am==ADDRESS_MODE_FULL);
uint cnt = 1;
switch(am) {
case ADDRESS_MODE_SADDR:
cnt += 2;
break;
case ADDRESS_MODE_XADDR:
cnt += 8;
break;
case ADDRESS_MODE_FULL:
cnt += 16;
break;
default:
ABORT(7);
}
return cnt;
}

/// <summary>
/// Return length of v6lowpan address information based on
address mode.
/// </summary>
/// <param name="am">address mode</param>
/// <returns></returns>
internal static uint mode2addressBitsLength(uint am) {
ASSERT(am==ADDRESS_MODE_SADDR||
am==ADDRESS_MODE_XADDR||am==ADDRESS_MODE_FULL);
uint cnt = 0;
switch(am) {
case ADDRESS_MODE_SADDR:
cnt += 2;
break;
case ADDRESS_MODE_XADDR:
cnt += 8;
break;
case ADDRESS_MODE_FULL:
cnt += 16;
break;
default:
ABORT(8);
}
return cnt;
}
}

} // namespace

You might also like