mirror of
https://github.com/espressif/openthread.git
synced 2026-06-06 05:24:51 +00:00
ncp: Added sniffer host tool. (#773)
* ncp: Added sniffer host tool.
Split spinel-cli out into a general spinel python library module.
Added sniffer.py tool that leverages the spinel library module.
Added some initial unit tests of spinel.py module using MockStream.
From openthread root:
./tools/spinel-cli/sniffer.py -c 11 -n 1 -u /dev/ttyUSB0 | wireshark -k -i -
This commit is contained in:
committed by
Jonathan Hui
parent
b6ebabf2e4
commit
3ea74a1977
@@ -45,6 +45,14 @@ cd /tmp || die
|
||||
sudo -H pip install pexpect || die
|
||||
pip install pexpect || die
|
||||
|
||||
# Packages used by ncp tools.
|
||||
sudo -H pip install ipaddress || die
|
||||
sudo -H pip install scapy || die
|
||||
sudo -H pip install pyserial || die
|
||||
pip install ipaddress || die
|
||||
pip install scapy || die
|
||||
pip install pyserial || die
|
||||
|
||||
[ $BUILD_TARGET != pretty-check ] || {
|
||||
wget http://jaist.dl.sourceforge.net/project/astyle/astyle/astyle%202.05.1/astyle_2.05.1_linux.tar.gz || die
|
||||
tar xzvf astyle_2.05.1_linux.tar.gz || die
|
||||
@@ -70,12 +78,6 @@ cd /tmp || die
|
||||
[ $BUILD_TARGET != posix-32-bit ] || {
|
||||
sudo apt-get install g++-multilib || die
|
||||
}
|
||||
|
||||
[ $BUILD_TARGET != posix-ncp ] || {
|
||||
pip install ipaddress || die
|
||||
pip install scapy || die
|
||||
pip install pyserial || die
|
||||
}
|
||||
}
|
||||
|
||||
[ $TRAVIS_OS_NAME != osx ] || {
|
||||
|
||||
@@ -790,6 +790,7 @@ tools/harness-automation/Makefile
|
||||
tools/harness-thci/Makefile
|
||||
tools/spi-hdlc-adapter/Makefile
|
||||
tools/spinel-cli/Makefile
|
||||
tools/spinel-cli/spinel/Makefile
|
||||
tests/Makefile
|
||||
tests/scripts/Makefile
|
||||
tests/unit/Makefile
|
||||
|
||||
@@ -40,6 +40,7 @@ DIST_SUBDIRS = \
|
||||
# Always build (e.g. for 'make all') these subdirectories.
|
||||
|
||||
SUBDIRS = \
|
||||
spinel-cli \
|
||||
$(NULL)
|
||||
|
||||
if OPENTHREAD_TARGET_LINUX
|
||||
|
||||
@@ -28,8 +28,37 @@
|
||||
|
||||
include $(abs_top_nlbuild_autotools_dir)/automake/pre.am
|
||||
|
||||
EXTRA_DIST = \
|
||||
spinel-cli.py \
|
||||
EXTRA_DIST = \
|
||||
spinel-cli.py \
|
||||
sniffer.py \
|
||||
test_spinel.py \
|
||||
$(NULL)
|
||||
|
||||
DIST_SUBDIRS = \
|
||||
spinel \
|
||||
$(NULL)
|
||||
|
||||
# Always build (e.g. for 'make all') these subdirectories.
|
||||
|
||||
SUBDIRS = \
|
||||
$(NULL)
|
||||
|
||||
# Always pretty (e.g. for 'make pretty') these subdirectories.
|
||||
|
||||
PRETTY_SUBDIRS = \
|
||||
$(NULL)
|
||||
|
||||
if OPENTHREAD_ENABLE_NCP
|
||||
|
||||
# List all essential script tests that MUST be run.
|
||||
|
||||
TESTS_ENVIRONMENT = \
|
||||
$(NULL)
|
||||
|
||||
TESTS = \
|
||||
test_spinel.py \
|
||||
$(NULL)
|
||||
|
||||
endif # OPENTHREAD_ENABLE_NCP
|
||||
|
||||
include $(abs_top_nlbuild_autotools_dir)/automake/post.am
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
# Spinel Sniffer Reference
|
||||
|
||||
Any Spinel NCP node can be made into a promiscuous packet sniffer, and this
|
||||
tool both intializes a device into this mode and outputs a pcap stream that
|
||||
can be saved or piped directly into Wireshark.
|
||||
|
||||
## System Requirements
|
||||
|
||||
The tool has been tested on the following platforms:
|
||||
|
||||
| Platforms | Version |
|
||||
|-----------|------------------|
|
||||
| Ubuntu | 14.04 Trusty |
|
||||
| Mac OS | 10.11 El Capitan |
|
||||
|
||||
| Language | Version |
|
||||
|-----------|------------------|
|
||||
| Python | 2.7.10 |
|
||||
|
||||
### Package Installation
|
||||
|
||||
```
|
||||
sudo easy_install pip
|
||||
sudo pip install ipaddress
|
||||
sudo pip install scapy
|
||||
sudo pip install pyserial
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### NAME
|
||||
sniffer.py - shell tool for controlling OpenThread NCP instances
|
||||
|
||||
### SYNOPSIS
|
||||
sniffer.py [-hupsnqvdxc]
|
||||
|
||||
### DESCRIPTION
|
||||
|
||||
```
|
||||
-h, --help
|
||||
Show this help message and exit
|
||||
|
||||
-u <UART>, --uart=<UART>
|
||||
Open a serial connection to the OpenThread NCP device
|
||||
where <UART> is a device path such as "/dev/ttyUSB0".
|
||||
|
||||
-p <PIPE>, --pipe=<PIPE>
|
||||
Open a piped process connection to the OpenThread NCP device
|
||||
where <PIPE> is the command to start an emulator, such as
|
||||
"ot-ncp". Spinel-cli will communicate with the child process
|
||||
via stdin/stdout.
|
||||
|
||||
-s <SOCKET>, --socket=<SOCKET>
|
||||
Open a socket connection to the OpenThread NCP device
|
||||
where <SOCKET> is the port to open.
|
||||
This is useful for SPI configurations when used in conjunction
|
||||
with a spinel spi-driver daemon.
|
||||
Note: <SOCKET> will eventually map to hostname:port tuple.
|
||||
|
||||
-n NODEID, --nodeid=<NODEID>
|
||||
The unique nodeid for the HOST and NCP instance.
|
||||
|
||||
-q, --quiet
|
||||
Minimize debug and log output.
|
||||
|
||||
-v, --verbose
|
||||
Maximize debug and log output.
|
||||
|
||||
-d <DEBUG_LEVEL>, --debug=<DEBUG_LEVEL>
|
||||
Set the debug level. Enabling debug output is typically coupled with -x.
|
||||
0: Supress all debug output. Required to stream to Wireshark.
|
||||
1: Show spinel property changes and values.
|
||||
2: Show spinel IPv6 packet bytes.
|
||||
3: Show spinel raw packet bytes (after HDLC decoding).
|
||||
4: Show spinel HDLC bytes.
|
||||
5: Show spinel raw stream bytes: all serial traffic to NCP.
|
||||
|
||||
-x, --hex
|
||||
Output packets as ASCII HEX rather than pcap.
|
||||
|
||||
-c, --channel
|
||||
Set the channel upon which to listen.
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
From openthread root:
|
||||
|
||||
```
|
||||
sudo ./tools/spinel-cli/sniffer.py -c 11 -n 1 -u /dev/ttyUSB0 | wireshark -k -i -
|
||||
```
|
||||
|
||||
This will connect to stock openthread ncp firmware over the given UART,
|
||||
make the node into a promiscuous mode sniffer on the given channel,
|
||||
open up wireshark, and start streaming packets into wireshark.
|
||||
|
||||
Executable
+154
@@ -0,0 +1,154 @@
|
||||
#!/usr/bin/python -u
|
||||
#
|
||||
# Copyright (c) 2016, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
"""
|
||||
Sniffer tool that outputs raw pcap.
|
||||
|
||||
Real-time stream to wireshark:
|
||||
./sniffer.py | wireshark -k -i -
|
||||
|
||||
Save stream to file or pipe:
|
||||
./sniffer.py > trace.pcap
|
||||
"""
|
||||
|
||||
import sys
|
||||
import optparse
|
||||
|
||||
import spinel.util as util
|
||||
import spinel.config as CONFIG
|
||||
from spinel.const import SPINEL
|
||||
from spinel.codec import WpanApi
|
||||
from spinel.stream import StreamOpen
|
||||
from spinel.pcap import PcapCodec
|
||||
|
||||
|
||||
# Nodeid is required to execute ot-ncp for its sim radio socket port.
|
||||
# This is maximum that works for MacOS.
|
||||
DEFAULT_NODEID = 34 # same as WELLKNOWN_NODE_ID
|
||||
DEFAULT_CHANNEL = 11
|
||||
|
||||
def parse_args():
|
||||
""" Parse command line arguments for this applications. """
|
||||
|
||||
args = sys.argv[1:]
|
||||
|
||||
opt_parser = optparse.OptionParser()
|
||||
opt_parser.add_option("-u", "--uart", action="store",
|
||||
dest="uart", type="string")
|
||||
opt_parser.add_option("-p", "--pipe", action="store",
|
||||
dest="pipe", type="string")
|
||||
opt_parser.add_option("-s", "--socket", action="store",
|
||||
dest="socket", type="string")
|
||||
opt_parser.add_option("-n", "--nodeid", action="store",
|
||||
dest="nodeid", type="string", default=str(DEFAULT_NODEID))
|
||||
|
||||
opt_parser.add_option("-q", "--quiet", action="store_true", dest="quiet")
|
||||
opt_parser.add_option("-v", "--verbose", action="store_false", dest="verbose")
|
||||
opt_parser.add_option("-d", "--debug", action="store",
|
||||
dest="debug", type="int", default=CONFIG.DEBUG_ENABLE)
|
||||
opt_parser.add_option("-x", "--hex", action="store_true", dest="hex")
|
||||
|
||||
opt_parser.add_option("-c", "--channel", action="store",
|
||||
dest="channel", type="int", default=DEFAULT_CHANNEL)
|
||||
|
||||
return opt_parser.parse_args(args)
|
||||
|
||||
def sniffer_init(wpan_api, options):
|
||||
"""" Send spinel commands to initialize sniffer node. """
|
||||
wpan_api.queue_register(SPINEL.HEADER_DEFAULT)
|
||||
wpan_api.queue_register(SPINEL.HEADER_ASYNC)
|
||||
|
||||
wpan_api.cmd_send(SPINEL.CMD_RESET)
|
||||
wpan_api.prop_set_value(SPINEL.PROP_PHY_ENABLED, 1)
|
||||
wpan_api.prop_set_value(SPINEL.PROP_MAC_FILTER_MODE, SPINEL.MAC_FILTER_MODE_MONITOR)
|
||||
wpan_api.prop_set_value(SPINEL.PROP_PHY_CHAN, options.channel)
|
||||
wpan_api.prop_set_value(SPINEL.PROP_MAC_15_4_PANID, 0xFFFF, 'H')
|
||||
wpan_api.prop_set_value(SPINEL.PROP_MAC_RAW_STREAM_ENABLED, 1)
|
||||
wpan_api.prop_set_value(SPINEL.PROP_NET_IF_UP, 1)
|
||||
|
||||
def main():
|
||||
""" Top-level main for sniffer host-side tool. """
|
||||
(options, remaining_args) = parse_args()
|
||||
|
||||
if options.debug:
|
||||
CONFIG.debug_set_level(options.debug)
|
||||
|
||||
# Set default stream to pipe
|
||||
stream_type = 'p'
|
||||
stream_descriptor = "../../examples/apps/ncp/ot-ncp "+options.nodeid
|
||||
|
||||
if options.uart:
|
||||
stream_type = 'u'
|
||||
stream_descriptor = options.uart
|
||||
elif options.socket:
|
||||
stream_type = 's'
|
||||
stream_descriptor = options.socket
|
||||
elif options.pipe:
|
||||
stream_type = 'p'
|
||||
stream_descriptor = options.pipe
|
||||
if options.nodeid:
|
||||
stream_descriptor += " "+str(options.nodeid)
|
||||
else:
|
||||
if len(remaining_args) > 0:
|
||||
stream_descriptor = " ".join(remaining_args)
|
||||
|
||||
stream = StreamOpen(stream_type, stream_descriptor, False)
|
||||
if stream is None: exit()
|
||||
wpan_api = WpanApi(stream, options.nodeid)
|
||||
sniffer_init(wpan_api, options)
|
||||
|
||||
pcap = PcapCodec()
|
||||
hdr = pcap.encode_header()
|
||||
if options.hex:
|
||||
hdr = util.hexify_str(hdr)+"\n"
|
||||
sys.stdout.write(hdr)
|
||||
sys.stdout.flush()
|
||||
|
||||
try:
|
||||
tid = SPINEL.HEADER_ASYNC
|
||||
prop_id = SPINEL.PROP_STREAM_RAW
|
||||
while True:
|
||||
result = wpan_api.queue_wait_for_prop(prop_id, tid)
|
||||
if result and result.prop == prop_id:
|
||||
length = wpan_api.parse_S(result.value)
|
||||
pkt = result.value[2:2+length]
|
||||
pkt = pcap.encode_frame(pkt)
|
||||
if options.hex:
|
||||
pkt = util.hexify_str(pkt)+"\n"
|
||||
sys.stdout.write(pkt)
|
||||
sys.stdout.flush()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
if wpan_api:
|
||||
wpan_api.stream.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+512
-1835
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,51 @@
|
||||
#
|
||||
# Copyright (c) 2016, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
include $(abs_top_nlbuild_autotools_dir)/automake/pre.am
|
||||
|
||||
EXTRA_DIST = \
|
||||
__init__.py \
|
||||
codec.py \
|
||||
config.py \
|
||||
const.py \
|
||||
hdlc.py \
|
||||
stream.py \
|
||||
pcap.py \
|
||||
tun.py \
|
||||
util.py \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST += \
|
||||
tests.py \
|
||||
test_codec.py \
|
||||
test_hdlc.py \
|
||||
test_stream.py \
|
||||
test_sniffer.py \
|
||||
$(NULL)
|
||||
|
||||
include $(abs_top_nlbuild_autotools_dir)/automake/post.am
|
||||
@@ -0,0 +1,27 @@
|
||||
#
|
||||
# Copyright (c) 2016, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
@@ -0,0 +1,967 @@
|
||||
#
|
||||
# Copyright (c) 2016, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
"""
|
||||
Module providing a Spienl coder / decoder class.
|
||||
"""
|
||||
|
||||
import os
|
||||
import time
|
||||
import logging
|
||||
import threading
|
||||
import traceback
|
||||
|
||||
import Queue
|
||||
|
||||
from struct import pack
|
||||
from struct import unpack
|
||||
from collections import namedtuple
|
||||
from collections import defaultdict
|
||||
|
||||
import ipaddress
|
||||
|
||||
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
|
||||
from scapy.layers.inet6 import IPv6
|
||||
from scapy.layers.inet6 import ICMPv6EchoReply
|
||||
|
||||
import spinel.util as util
|
||||
import spinel.config as CONFIG
|
||||
from spinel.const import kThread
|
||||
from spinel.const import SPINEL
|
||||
from spinel.const import SPINEL_LAST_STATUS_MAP
|
||||
from spinel.hdlc import Hdlc
|
||||
from spinel.tun import TunInterface
|
||||
|
||||
|
||||
FEATURE_USE_HDLC = 1
|
||||
FEATURE_USE_SLACC = 1
|
||||
|
||||
TIMEOUT_PROP = 2
|
||||
|
||||
#=========================================
|
||||
# SpinelCodec
|
||||
#=========================================
|
||||
|
||||
# 0: DATATYPE_NULL
|
||||
#'.': DATATYPE_VOID: Empty data type. Used internally.
|
||||
#'b': DATATYPE_BOOL: Boolean value. Encoded in 8-bits as either 0x00 or 0x01.
|
||||
# All other values are illegal.
|
||||
#'C': DATATYPE_UINT8: Unsigned 8-bit integer.
|
||||
#'c': DATATYPE_INT8: Signed 8-bit integer.
|
||||
#'S': DATATYPE_UINT16: Unsigned 16-bit integer. (Little-endian)
|
||||
#'s': DATATYPE_INT16: Signed 16-bit integer. (Little-endian)
|
||||
#'L': DATATYPE_UINT32: Unsigned 32-bit integer. (Little-endian)
|
||||
#'l': DATATYPE_INT32: Signed 32-bit integer. (Little-endian)
|
||||
#'i': DATATYPE_UINT_PACKED: Packed Unsigned Integer. (See section 7.2)
|
||||
#'6': DATATYPE_IPv6ADDR: IPv6 Address. (Big-endian)
|
||||
#'E': DATATYPE_EUI64: EUI-64 Address. (Big-endian)
|
||||
#'e': DATATYPE_EUI48: EUI-48 Address. (Big-endian)
|
||||
#'D': DATATYPE_DATA: Arbitrary Data. (See section 7.3)
|
||||
#'U': DATATYPE_UTF8: Zero-terminated UTF8-encoded string.
|
||||
#'T': DATATYPE_STRUCT: Structured datatype. Compound type. (See section 7.4)
|
||||
#'A': DATATYPE_ARRAY: Array of datatypes. Compound type. (See section 7.5)
|
||||
|
||||
|
||||
class SpinelCodec(object):
|
||||
""" A general coder / decoder class for Spinel protocol. """
|
||||
|
||||
@classmethod
|
||||
def parse_b(cls, payload): return unpack("<B", payload[:1])[0]
|
||||
|
||||
@classmethod
|
||||
def parse_c(cls, payload): return unpack("<b", payload[:1])[0]
|
||||
|
||||
@classmethod
|
||||
def parse_C(cls, payload): return unpack("<B", payload[:1])[0]
|
||||
|
||||
@classmethod
|
||||
def parse_s(cls, payload): return unpack("<h", payload[:2])[0]
|
||||
|
||||
@classmethod
|
||||
def parse_S(cls, payload): return unpack("<H", payload[:2])[0]
|
||||
|
||||
@classmethod
|
||||
def parse_l(cls, payload): return unpack("<l", payload[:4])[0]
|
||||
|
||||
@classmethod
|
||||
def parse_L(cls, payload): return unpack("<L", payload[:4])[0]
|
||||
|
||||
@classmethod
|
||||
def parse_6(cls, payload): return payload[:16]
|
||||
|
||||
@classmethod
|
||||
def parse_E(cls, payload): return payload[:8]
|
||||
|
||||
@classmethod
|
||||
def parse_e(cls, payload): return payload[:6]
|
||||
|
||||
@classmethod
|
||||
def parse_U(cls, payload): return payload[:-1] # strip null
|
||||
|
||||
@classmethod
|
||||
def parse_D(cls, payload): return payload
|
||||
|
||||
@classmethod
|
||||
def parse_i(cls, payload):
|
||||
""" Decode EXI integer format. """
|
||||
value = 0
|
||||
value_len = 0
|
||||
value_mul = 1
|
||||
|
||||
while value_len < 4:
|
||||
byte = ord(payload[value_len])
|
||||
value += (byte & 0x7F) * value_mul
|
||||
if byte < 0x80:
|
||||
break
|
||||
value_mul *= 0x80
|
||||
value_len += 1
|
||||
|
||||
return (value, value_len + 1)
|
||||
|
||||
@classmethod
|
||||
def parse_field(cls, payload, spinel_format):
|
||||
map_decode = {
|
||||
'b': cls.parse_b,
|
||||
'c': cls.parse_c,
|
||||
'C': cls.parse_C,
|
||||
's': cls.parse_s,
|
||||
'S': cls.parse_S,
|
||||
'L': cls.parse_L,
|
||||
'l': cls.parse_l,
|
||||
'6': cls.parse_6,
|
||||
'E': cls.parse_E,
|
||||
'e': cls.parse_e,
|
||||
'U': cls.parse_U,
|
||||
'D': cls.parse_D,
|
||||
'i': cls.parse_i,
|
||||
}
|
||||
try:
|
||||
return map_decode[spinel_format[0]](payload)
|
||||
except KeyError, _ex:
|
||||
print traceback.format_exc()
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def encode_i(cls, data):
|
||||
""" Encode EXI integer format. """
|
||||
result = ""
|
||||
while data:
|
||||
value = data & 0x7F
|
||||
data >>= 7
|
||||
if data:
|
||||
value |= 0x80
|
||||
result = result + pack("<B", value)
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def encode_b(cls, value): return pack('B', value)
|
||||
|
||||
@classmethod
|
||||
def encode_c(cls, value): return pack('B', value)
|
||||
|
||||
@classmethod
|
||||
def encode_C(cls, value): return pack('B', value)
|
||||
|
||||
@classmethod
|
||||
def encode_s(cls, value): return pack('<h', value)
|
||||
|
||||
@classmethod
|
||||
def encode_S(cls, value): return pack('<H', value)
|
||||
|
||||
@classmethod
|
||||
def encode_l(cls, value): return pack('<l', value)
|
||||
|
||||
@classmethod
|
||||
def encode_L(cls, value): return pack('<L', value)
|
||||
|
||||
@classmethod
|
||||
def encode_6(cls, value): return value[:16]
|
||||
|
||||
@classmethod
|
||||
def encode_E(cls, value): return value[:8]
|
||||
|
||||
@classmethod
|
||||
def encode_e(cls, value): return value[:6]
|
||||
|
||||
@classmethod
|
||||
def encode_U(cls, value): return value + '\0'
|
||||
|
||||
@classmethod
|
||||
def encode_D(cls, value): return value
|
||||
|
||||
@classmethod
|
||||
def encode_field(cls, code, value):
|
||||
map_encode = {
|
||||
'b': cls.encode_b,
|
||||
'c': cls.encode_c,
|
||||
'C': cls.encode_C,
|
||||
's': cls.encode_s,
|
||||
'S': cls.encode_S,
|
||||
'L': cls.encode_L,
|
||||
'l': cls.encode_l,
|
||||
'6': cls.encode_6,
|
||||
'E': cls.encode_E,
|
||||
'e': cls.encode_e,
|
||||
'U': cls.encode_U,
|
||||
'D': cls.encode_D,
|
||||
'i': cls.encode_i,
|
||||
}
|
||||
try:
|
||||
return map_encode[code](value)
|
||||
except KeyError, _ex:
|
||||
print traceback.format_exc()
|
||||
return None
|
||||
|
||||
def next_code(self, spinel_format):
|
||||
code = spinel_format[0]
|
||||
spinel_format = spinel_format[1:]
|
||||
# TODO: Handle T() and A()
|
||||
return code, spinel_format
|
||||
|
||||
def encode_fields(self, spinel_format, *fields):
|
||||
packed = ""
|
||||
for field in fields:
|
||||
code, spinel_format = self.next_code(spinel_format)
|
||||
if not code:
|
||||
break
|
||||
packed += self.encode_field(code, field)
|
||||
return packed
|
||||
|
||||
def encode_packet(self, command_id, payload=None, tid=SPINEL.HEADER_DEFAULT):
|
||||
""" Encode the given payload as a Spinel frame. """
|
||||
header = pack(">B", tid)
|
||||
cmd = self.encode_i(command_id)
|
||||
pkt = header + cmd + payload
|
||||
return pkt
|
||||
|
||||
|
||||
#=========================================
|
||||
|
||||
class SpinelPropertyHandler(SpinelCodec):
|
||||
|
||||
def LAST_STATUS(self, _, payload): return self.parse_i(payload)[0]
|
||||
|
||||
def PROTOCOL_VERSION(self, _wpan_api, payload): pass
|
||||
|
||||
def NCP_VERSION(self, _, payload): return self.parse_U(payload)
|
||||
|
||||
def INTERFACE_TYPE(self, _, payload): return self.parse_i(payload)[0]
|
||||
|
||||
def VENDOR_ID(self, _, payload): return self.parse_i(payload)[0]
|
||||
|
||||
def CAPS(self, _wpan_api, payload): pass
|
||||
|
||||
def INTERFACE_COUNT(self, _, payload): return self.parse_C(payload)
|
||||
|
||||
def POWER_STATE(self, _, payload): return self.parse_C(payload)
|
||||
|
||||
def HWADDR(self, _, payload): return self.parse_E(payload)
|
||||
|
||||
def LOCK(self, _, payload): return self.parse_b(payload)
|
||||
|
||||
def HBO_MEM_MAX(self, _, payload): return self.parse_L(payload)
|
||||
|
||||
def HBO_BLOCK_MAX(self, _, payload): return self.parse_S(payload)
|
||||
|
||||
def PHY_ENABLED(self, _, payload): return self.parse_b(payload)
|
||||
|
||||
def PHY_CHAN(self, _, payload): return self.parse_C(payload)
|
||||
|
||||
def PHY_CHAN_SUPPORTED(self, _wpan_api, payload): pass
|
||||
|
||||
def PHY_FREQ(self, _, payload): return self.parse_L(payload)
|
||||
|
||||
def PHY_CCA_THRESHOLD(self, _, payload): return self.parse_c(payload)
|
||||
|
||||
def PHY_TX_POWER(self, _, payload): return self.parse_c(payload)
|
||||
|
||||
def PHY_RSSI(self, _, payload): return self.parse_c(payload)
|
||||
|
||||
def MAC_SCAN_STATE(self, _, payload): return self.parse_C(payload)
|
||||
|
||||
def MAC_SCAN_MASK(self, _, payload): return self.parse_U(payload)
|
||||
|
||||
def MAC_SCAN_PERIOD(self, _, payload): return self.parse_S(payload)
|
||||
|
||||
def MAC_SCAN_BEACON(self, _, payload): return self.parse_U(payload)
|
||||
|
||||
def MAC_15_4_LADDR(self, _, payload): return self.parse_E(payload)
|
||||
|
||||
def MAC_15_4_SADDR(self, _, payload): return self.parse_S(payload)
|
||||
|
||||
def MAC_15_4_PANID(self, _, payload): return self.parse_S(payload)
|
||||
|
||||
def MAC_FILTER_MODE(self, _, payload): return self.parse_C(payload)
|
||||
|
||||
def MAC_RAW_STREAM_ENABLED(self, _, payload):
|
||||
return self.parse_b(payload)
|
||||
|
||||
def MAC_WHITELIST(self, _, payload): pass
|
||||
|
||||
def MAC_WHITELIST_ENABLED(self, _, payload):
|
||||
return self.parse_b(payload)
|
||||
|
||||
def NET_SAVED(self, _, payload): return self.parse_b(payload)
|
||||
|
||||
def NET_IF_UP(self, _, payload): return self.parse_b(payload)
|
||||
|
||||
def NET_STACK_UP(self, _, payload): return self.parse_C(payload)
|
||||
|
||||
def NET_ROLE(self, _, payload): return self.parse_C(payload)
|
||||
|
||||
def NET_NETWORK_NAME(self, _, payload): return self.parse_U(payload)
|
||||
|
||||
def NET_XPANID(self, _, payload): return self.parse_D(payload)
|
||||
|
||||
def NET_MASTER_KEY(self, _, payload): return self.parse_D(payload)
|
||||
|
||||
def NET_KEY_SEQUENCE(self, _, payload): return self.parse_L(payload)
|
||||
|
||||
def NET_PARTITION_ID(self, _, payload): return self.parse_L(payload)
|
||||
|
||||
def THREAD_LEADER_ADDR(self, _, payload): return self.parse_6(payload)
|
||||
|
||||
def THREAD_PARENT(self, _wpan_api, payload): pass
|
||||
|
||||
def THREAD_CHILD_TABLE(self, _, payload): return self.parse_D(payload)
|
||||
|
||||
def THREAD_LEADER_RID(self, _, payload): return self.parse_C(payload)
|
||||
|
||||
def THREAD_LEADER_WEIGHT(self, _, payload):
|
||||
return self.parse_C(payload)
|
||||
|
||||
def THREAD_LOCAL_LEADER_WEIGHT(self, _, payload):
|
||||
return self.parse_C(payload)
|
||||
|
||||
def THREAD_NETWORK_DATA(self, _, payload):
|
||||
return self.parse_D(payload)
|
||||
|
||||
def THREAD_NETWORK_DATA_VERSION(self, _wpan_api, payload): pass
|
||||
|
||||
def THREAD_STABLE_NETWORK_DATA(self, _wpan_api, payload): pass
|
||||
|
||||
def THREAD_STABLE_NETWORK_DATA_VERSION(self, _wpan_api, payload): pass
|
||||
|
||||
def __init__(self):
|
||||
self.autoAddresses = set()
|
||||
|
||||
self.wpan_api = None
|
||||
self.__queue_prefix = Queue.Queue()
|
||||
self.prefix_thread = threading.Thread(target=self.__run_prefix_handler)
|
||||
self.prefix_thread.setDaemon(True)
|
||||
self.prefix_thread.start()
|
||||
|
||||
def handle_ipaddr_remove(self, ipaddr):
|
||||
valid = 1
|
||||
preferred = 1
|
||||
flags = 0
|
||||
prefix_len = 64 # always use /64
|
||||
|
||||
arr = self.encode_fields('6CLLC',
|
||||
ipaddr.ip.packed,
|
||||
prefix_len,
|
||||
valid,
|
||||
preferred,
|
||||
flags)
|
||||
|
||||
self.wpan_api.prop_remove_async(SPINEL.PROP_IPV6_ADDRESS_TABLE,
|
||||
arr, str(len(arr)) + 's',
|
||||
SPINEL.HEADER_EVENT_HANDLER)
|
||||
|
||||
def handle_ipaddr_insert(self, prefix, prefix_len, _stable, flags, _is_local):
|
||||
""" Add an ip address for each prefix on prefix change. """
|
||||
|
||||
ipaddr_str = str(ipaddress.IPv6Address(prefix)) + \
|
||||
str(self.wpan_api.nodeid)
|
||||
if CONFIG.DEBUG_LOG_PROP:
|
||||
print "\n>>>> new PREFIX add ipaddr: " + ipaddr_str
|
||||
|
||||
valid = 1
|
||||
preferred = 1
|
||||
flags = 0
|
||||
ipaddr = ipaddress.IPv6Interface(unicode(ipaddr_str))
|
||||
self.autoAddresses.add(ipaddr)
|
||||
|
||||
arr = self.encode_fields('6CLLC',
|
||||
ipaddr.ip.packed,
|
||||
prefix_len,
|
||||
valid,
|
||||
preferred,
|
||||
flags)
|
||||
|
||||
self.wpan_api.prop_insert_async(SPINEL.PROP_IPV6_ADDRESS_TABLE,
|
||||
arr, str(len(arr)) + 's',
|
||||
SPINEL.HEADER_EVENT_HANDLER)
|
||||
|
||||
def handle_prefix_change(self, payload):
|
||||
""" Automatically ipaddr add / remove addresses for each new prefix. """
|
||||
# As done by cli.cpp Interpreter::HandleNetifStateChanged
|
||||
|
||||
# First parse payload and extract slaac prefix information.
|
||||
pay = payload
|
||||
Prefix = namedtuple("Prefix", "prefix prefixlen stable flags is_local")
|
||||
prefixes = []
|
||||
slaacPrefixSet = set()
|
||||
while len(pay) >= 22:
|
||||
(_structlen) = unpack('<H', pay[:2])
|
||||
pay = pay[2:]
|
||||
prefix = Prefix(*unpack('16sBBBB', pay[:20]))
|
||||
if prefix.flags & kThread.PrefixSlaacFlag:
|
||||
net6 = ipaddress.IPv6Network(prefix.prefix)
|
||||
net6 = net6.supernet(new_prefix=prefix.prefixlen)
|
||||
slaacPrefixSet.add(net6)
|
||||
prefixes.append(prefix)
|
||||
pay = pay[20:]
|
||||
|
||||
for prefix in prefixes:
|
||||
self.handle_ipaddr_insert(*prefix)
|
||||
|
||||
if CONFIG.DEBUG_LOG_PROP:
|
||||
print "\n========= PREFIX ============"
|
||||
print "ipaddrs: " + str(self.autoAddresses)
|
||||
print "slaac prefix set: " + str(slaacPrefixSet)
|
||||
print "==============================\n"
|
||||
|
||||
# ==> ipaddrs - query current addresses
|
||||
#
|
||||
# for ipaddr in ipaddrs:
|
||||
# if lifetime > 0 and not in slaac prefixes
|
||||
# ==> remove
|
||||
for ipaddr in self.autoAddresses:
|
||||
if not any(ipaddr in prefix for prefix in slaacPrefixSet):
|
||||
self.handle_ipaddr_remove(ipaddr)
|
||||
|
||||
# for slaac prefix in prefixes:
|
||||
# if no ipaddr with lifetime > 0 in prefix:
|
||||
# ==> add
|
||||
|
||||
def __run_prefix_handler(self):
|
||||
while 1:
|
||||
(wpan_api, payload) = self.__queue_prefix.get(True)
|
||||
self.wpan_api = wpan_api
|
||||
self.handle_prefix_change(payload)
|
||||
self.__queue_prefix.task_done()
|
||||
|
||||
def THREAD_ON_MESH_NETS(self, wpan_api, payload):
|
||||
if FEATURE_USE_SLACC:
|
||||
# Kick prefix handler thread to allow serial rx thread to work.
|
||||
self.__queue_prefix.put_nowait((wpan_api, payload))
|
||||
|
||||
return self.parse_D(payload)
|
||||
|
||||
def THREAD_LOCAL_ROUTES(self, _wpan_api, payload): pass
|
||||
|
||||
def THREAD_ASSISTING_PORTS(self, _wpan_api, payload): pass
|
||||
|
||||
def THREAD_ALLOW_LOCAL_NET_DATA_CHANGE(self, _, payload):
|
||||
return self.parse_b(payload)
|
||||
|
||||
def THREAD_MODE(self, _, payload): return self.parse_C(payload)
|
||||
|
||||
def THREAD_CHILD_TIMEOUT(self, _, payload): return self.parse_L(payload)
|
||||
|
||||
def THREAD_RLOC16(self, _, payload): return self.parse_S(payload)
|
||||
|
||||
def THREAD_ROUTER_UPGRADE_THRESHOLD(self, _, payload):
|
||||
return self.parse_C(payload)
|
||||
|
||||
def THREAD_ROUTER_DOWNGRADE_THRESHOLD(self, _, payload):
|
||||
return self.parse_C(payload)
|
||||
|
||||
def THREAD_ROUTER_SELECTION_JITTER(self, _, payload):
|
||||
return self.parse_C(payload)
|
||||
|
||||
def THREAD_CONTEXT_REUSE_DELAY(self, _, payload):
|
||||
return self.parse_L(payload)
|
||||
|
||||
def THREAD_NETWORK_ID_TIMEOUT(self, _, payload):
|
||||
return self.parse_C(payload)
|
||||
|
||||
def THREAD_ACTIVE_ROUTER_IDS(self, _, payload):
|
||||
return self.parse_D(payload)
|
||||
|
||||
def THREAD_RLOC16_DEBUG_PASSTHRU(self, _, payload):
|
||||
return self.parse_b(payload)
|
||||
|
||||
def MESHCOP_JOINER_ENABLE(self, _, payload):
|
||||
return self.parse_b(payload)
|
||||
|
||||
def MESHCOP_JOINER_CREDENTIAL(self, _, payload):
|
||||
return self.parse_D(payload)
|
||||
|
||||
def MESHCOP_JOINER_URL(self, _, payload):
|
||||
return self.parse_U(payload)
|
||||
|
||||
def MESHCOP_BORDER_AGENT_ENABLE(self, _, payload):
|
||||
return self.parse_b(payload)
|
||||
|
||||
def IPV6_LL_ADDR(self, _, payload): return self.parse_6(payload)
|
||||
|
||||
def IPV6_ML_ADDR(self, _, payload): return self.parse_6(payload)
|
||||
|
||||
def IPV6_ML_PREFIX(self, _, payload): return self.parse_E(payload)
|
||||
|
||||
def IPV6_ADDRESS_TABLE(self, _, payload): return self.parse_D(payload)
|
||||
|
||||
def IPV6_ROUTE_TABLE(self, _, payload): return self.parse_D(payload)
|
||||
|
||||
def IPv6_ICMP_PING_OFFLOAD(self, _, payload):
|
||||
return self.parse_b(payload)
|
||||
|
||||
def STREAM_DEBUG(self, _, payload): return self.parse_U(payload)
|
||||
|
||||
def STREAM_RAW(self, _, payload): return self.parse_D(payload)
|
||||
|
||||
def STREAM_NET(self, _, payload): return self.parse_D(payload)
|
||||
|
||||
def STREAM_NET_INSECURE(self, _, payload): return self.parse_D(payload)
|
||||
|
||||
def PIB_PHY_CHANNELS_SUPPORTED(self, _wpan_api, payload): pass
|
||||
|
||||
def PIB_MAC_PROMISCUOUS_MODE(self, _wpan_api, payload): pass
|
||||
|
||||
def PIB_MAC_SECURITY_ENABLED(self, _wpan_api, payload): pass
|
||||
|
||||
#=========================================
|
||||
|
||||
|
||||
class SpinelCommandHandler(SpinelCodec):
|
||||
|
||||
def handle_prop(self, wpan_api, name, payload, tid):
|
||||
(prop_id, prop_len) = self.parse_i(payload)
|
||||
|
||||
try:
|
||||
handler = SPINEL_PROP_DISPATCH[prop_id]
|
||||
prop_name = handler.__name__
|
||||
prop_value = handler(wpan_api, payload[prop_len:])
|
||||
|
||||
if CONFIG.DEBUG_LOG_PROP:
|
||||
|
||||
# Generic output
|
||||
if isinstance(prop_value, basestring):
|
||||
prop_value_str = util.hexify_str(prop_value)
|
||||
logging.debug("PROP_VALUE_%s [tid=%d]: %s = %s",
|
||||
name, (tid & 0xF), prop_name, prop_value_str)
|
||||
else:
|
||||
prop_value_str = str(prop_value)
|
||||
|
||||
logging.debug("PROP_VALUE_%s [tid=%d]: %s = %s",
|
||||
name, (tid & 0xF), prop_name, prop_value_str)
|
||||
|
||||
# Extend output for certain properties.
|
||||
if prop_id == SPINEL.PROP_LAST_STATUS:
|
||||
logging.debug(SPINEL_LAST_STATUS_MAP[prop_value])
|
||||
|
||||
if CONFIG.DEBUG_LOG_PKT:
|
||||
if ((prop_id == SPINEL.PROP_STREAM_NET) or
|
||||
(prop_id == SPINEL.PROP_STREAM_NET_INSECURE)):
|
||||
logging.debug("PROP_VALUE_" + name + ": " + prop_name)
|
||||
pkt = IPv6(prop_value[2:])
|
||||
pkt.show()
|
||||
|
||||
elif prop_id == SPINEL.PROP_STREAM_DEBUG:
|
||||
logging.debug("DEBUG: " + prop_value)
|
||||
|
||||
if wpan_api:
|
||||
wpan_api.queue_add(prop_id, prop_value, tid)
|
||||
else:
|
||||
print "no wpan_api"
|
||||
|
||||
except Exception as _ex:
|
||||
prop_name = "Property Unknown"
|
||||
logging.info("\n%s (%i): ", prop_name, prop_id)
|
||||
print traceback.format_exc()
|
||||
|
||||
def PROP_VALUE_IS(self, wpan_api, payload, tid):
|
||||
self.handle_prop(wpan_api, "IS", payload, tid)
|
||||
|
||||
def PROP_VALUE_INSERTED(self, wpan_api, payload, tid):
|
||||
self.handle_prop(wpan_api, "INSERTED", payload, tid)
|
||||
|
||||
def PROP_VALUE_REMOVED(self, wpan_api, payload, tid):
|
||||
self.handle_prop(wpan_api, "REMOVED", payload, tid)
|
||||
|
||||
|
||||
WPAN_CMD_HANDLER = SpinelCommandHandler()
|
||||
|
||||
SPINEL_COMMAND_DISPATCH = {
|
||||
SPINEL.RSP_PROP_VALUE_IS: WPAN_CMD_HANDLER.PROP_VALUE_IS,
|
||||
SPINEL.RSP_PROP_VALUE_INSERTED: WPAN_CMD_HANDLER.PROP_VALUE_INSERTED,
|
||||
SPINEL.RSP_PROP_VALUE_REMOVED: WPAN_CMD_HANDLER.PROP_VALUE_REMOVED,
|
||||
}
|
||||
|
||||
WPAN_PROP_HANDLER = SpinelPropertyHandler()
|
||||
|
||||
SPINEL_PROP_DISPATCH = {
|
||||
SPINEL.PROP_LAST_STATUS: WPAN_PROP_HANDLER.LAST_STATUS,
|
||||
SPINEL.PROP_PROTOCOL_VERSION: WPAN_PROP_HANDLER.PROTOCOL_VERSION,
|
||||
SPINEL.PROP_NCP_VERSION: WPAN_PROP_HANDLER.NCP_VERSION,
|
||||
SPINEL.PROP_INTERFACE_TYPE: WPAN_PROP_HANDLER.INTERFACE_TYPE,
|
||||
SPINEL.PROP_VENDOR_ID: WPAN_PROP_HANDLER.VENDOR_ID,
|
||||
SPINEL.PROP_CAPS: WPAN_PROP_HANDLER.CAPS,
|
||||
SPINEL.PROP_INTERFACE_COUNT: WPAN_PROP_HANDLER.INTERFACE_COUNT,
|
||||
SPINEL.PROP_POWER_STATE: WPAN_PROP_HANDLER.POWER_STATE,
|
||||
SPINEL.PROP_HWADDR: WPAN_PROP_HANDLER.HWADDR,
|
||||
SPINEL.PROP_LOCK: WPAN_PROP_HANDLER.LOCK,
|
||||
SPINEL.PROP_HBO_MEM_MAX: WPAN_PROP_HANDLER.HBO_MEM_MAX,
|
||||
SPINEL.PROP_HBO_BLOCK_MAX: WPAN_PROP_HANDLER.HBO_BLOCK_MAX,
|
||||
|
||||
SPINEL.PROP_PHY_ENABLED: WPAN_PROP_HANDLER.PHY_ENABLED,
|
||||
SPINEL.PROP_PHY_CHAN: WPAN_PROP_HANDLER.PHY_CHAN,
|
||||
SPINEL.PROP_PHY_CHAN_SUPPORTED: WPAN_PROP_HANDLER.PHY_CHAN_SUPPORTED,
|
||||
SPINEL.PROP_PHY_FREQ: WPAN_PROP_HANDLER.PHY_FREQ,
|
||||
SPINEL.PROP_PHY_CCA_THRESHOLD: WPAN_PROP_HANDLER.PHY_CCA_THRESHOLD,
|
||||
SPINEL.PROP_PHY_TX_POWER: WPAN_PROP_HANDLER.PHY_TX_POWER,
|
||||
SPINEL.PROP_PHY_RSSI: WPAN_PROP_HANDLER.PHY_RSSI,
|
||||
|
||||
SPINEL.PROP_MAC_SCAN_STATE: WPAN_PROP_HANDLER.MAC_SCAN_STATE,
|
||||
SPINEL.PROP_MAC_SCAN_MASK: WPAN_PROP_HANDLER.MAC_SCAN_MASK,
|
||||
SPINEL.PROP_MAC_SCAN_PERIOD: WPAN_PROP_HANDLER.MAC_SCAN_PERIOD,
|
||||
SPINEL.PROP_MAC_SCAN_BEACON: WPAN_PROP_HANDLER.MAC_SCAN_BEACON,
|
||||
SPINEL.PROP_MAC_15_4_LADDR: WPAN_PROP_HANDLER.MAC_15_4_LADDR,
|
||||
SPINEL.PROP_MAC_15_4_SADDR: WPAN_PROP_HANDLER.MAC_15_4_SADDR,
|
||||
SPINEL.PROP_MAC_15_4_PANID: WPAN_PROP_HANDLER.MAC_15_4_PANID,
|
||||
SPINEL.PROP_MAC_RAW_STREAM_ENABLED: WPAN_PROP_HANDLER.MAC_RAW_STREAM_ENABLED,
|
||||
SPINEL.PROP_MAC_FILTER_MODE: WPAN_PROP_HANDLER.MAC_FILTER_MODE,
|
||||
|
||||
SPINEL.PROP_MAC_WHITELIST: WPAN_PROP_HANDLER.MAC_WHITELIST,
|
||||
SPINEL.PROP_MAC_WHITELIST_ENABLED: WPAN_PROP_HANDLER.MAC_WHITELIST_ENABLED,
|
||||
|
||||
SPINEL.PROP_NET_SAVED: WPAN_PROP_HANDLER.NET_SAVED,
|
||||
SPINEL.PROP_NET_IF_UP: WPAN_PROP_HANDLER.NET_IF_UP,
|
||||
SPINEL.PROP_NET_STACK_UP: WPAN_PROP_HANDLER.NET_STACK_UP,
|
||||
SPINEL.PROP_NET_ROLE: WPAN_PROP_HANDLER.NET_ROLE,
|
||||
SPINEL.PROP_NET_NETWORK_NAME: WPAN_PROP_HANDLER.NET_NETWORK_NAME,
|
||||
SPINEL.PROP_NET_XPANID: WPAN_PROP_HANDLER.NET_XPANID,
|
||||
SPINEL.PROP_NET_MASTER_KEY: WPAN_PROP_HANDLER.NET_MASTER_KEY,
|
||||
SPINEL.PROP_NET_KEY_SEQUENCE: WPAN_PROP_HANDLER.NET_KEY_SEQUENCE,
|
||||
SPINEL.PROP_NET_PARTITION_ID: WPAN_PROP_HANDLER.NET_PARTITION_ID,
|
||||
|
||||
SPINEL.PROP_THREAD_LEADER_ADDR: WPAN_PROP_HANDLER.THREAD_LEADER_ADDR,
|
||||
SPINEL.PROP_THREAD_PARENT: WPAN_PROP_HANDLER.THREAD_PARENT,
|
||||
SPINEL.PROP_THREAD_CHILD_TABLE: WPAN_PROP_HANDLER.THREAD_CHILD_TABLE,
|
||||
SPINEL.PROP_THREAD_LEADER_RID: WPAN_PROP_HANDLER.THREAD_LEADER_RID,
|
||||
SPINEL.PROP_THREAD_LEADER_WEIGHT: WPAN_PROP_HANDLER.THREAD_LEADER_WEIGHT,
|
||||
SPINEL.PROP_THREAD_LOCAL_LEADER_WEIGHT: WPAN_PROP_HANDLER.THREAD_LOCAL_LEADER_WEIGHT,
|
||||
SPINEL.PROP_THREAD_NETWORK_DATA: WPAN_PROP_HANDLER.THREAD_NETWORK_DATA,
|
||||
SPINEL.PROP_THREAD_NETWORK_DATA_VERSION: WPAN_PROP_HANDLER.THREAD_NETWORK_DATA_VERSION,
|
||||
SPINEL.PROP_THREAD_STABLE_NETWORK_DATA: WPAN_PROP_HANDLER.THREAD_STABLE_NETWORK_DATA,
|
||||
SPINEL.PROP_THREAD_STABLE_NETWORK_DATA_VERSION:
|
||||
WPAN_PROP_HANDLER.THREAD_STABLE_NETWORK_DATA_VERSION,
|
||||
SPINEL.PROP_THREAD_ON_MESH_NETS: WPAN_PROP_HANDLER.THREAD_ON_MESH_NETS,
|
||||
SPINEL.PROP_THREAD_LOCAL_ROUTES: WPAN_PROP_HANDLER.THREAD_LOCAL_ROUTES,
|
||||
SPINEL.PROP_THREAD_ASSISTING_PORTS: WPAN_PROP_HANDLER.THREAD_ASSISTING_PORTS,
|
||||
SPINEL.PROP_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE:
|
||||
WPAN_PROP_HANDLER.THREAD_ALLOW_LOCAL_NET_DATA_CHANGE,
|
||||
SPINEL.PROP_THREAD_MODE: WPAN_PROP_HANDLER.THREAD_MODE,
|
||||
SPINEL.PROP_THREAD_CHILD_TIMEOUT: WPAN_PROP_HANDLER.THREAD_CHILD_TIMEOUT,
|
||||
SPINEL.PROP_THREAD_RLOC16: WPAN_PROP_HANDLER.THREAD_RLOC16,
|
||||
SPINEL.PROP_THREAD_ROUTER_UPGRADE_THRESHOLD: WPAN_PROP_HANDLER.THREAD_ROUTER_UPGRADE_THRESHOLD,
|
||||
SPINEL.PROP_THREAD_ROUTER_DOWNGRADE_THRESHOLD:
|
||||
WPAN_PROP_HANDLER.THREAD_ROUTER_DOWNGRADE_THRESHOLD,
|
||||
SPINEL.PROP_THREAD_ROUTER_SELECTION_JITTER: WPAN_PROP_HANDLER.THREAD_ROUTER_SELECTION_JITTER,
|
||||
SPINEL.PROP_THREAD_CONTEXT_REUSE_DELAY: WPAN_PROP_HANDLER.THREAD_CONTEXT_REUSE_DELAY,
|
||||
SPINEL.PROP_THREAD_NETWORK_ID_TIMEOUT: WPAN_PROP_HANDLER.THREAD_NETWORK_ID_TIMEOUT,
|
||||
SPINEL.PROP_THREAD_ACTIVE_ROUTER_IDS: WPAN_PROP_HANDLER.THREAD_ACTIVE_ROUTER_IDS,
|
||||
SPINEL.PROP_THREAD_RLOC16_DEBUG_PASSTHRU: WPAN_PROP_HANDLER.THREAD_RLOC16_DEBUG_PASSTHRU,
|
||||
|
||||
SPINEL.PROP_MESHCOP_JOINER_ENABLE: WPAN_PROP_HANDLER.MESHCOP_JOINER_ENABLE,
|
||||
SPINEL.PROP_MESHCOP_JOINER_CREDENTIAL: WPAN_PROP_HANDLER.MESHCOP_JOINER_CREDENTIAL,
|
||||
SPINEL.PROP_MESHCOP_JOINER_URL: WPAN_PROP_HANDLER.MESHCOP_JOINER_URL,
|
||||
SPINEL.PROP_MESHCOP_BORDER_AGENT_ENABLE: WPAN_PROP_HANDLER.MESHCOP_BORDER_AGENT_ENABLE,
|
||||
|
||||
|
||||
SPINEL.PROP_IPV6_LL_ADDR: WPAN_PROP_HANDLER.IPV6_LL_ADDR,
|
||||
SPINEL.PROP_IPV6_ML_ADDR: WPAN_PROP_HANDLER.IPV6_ML_ADDR,
|
||||
SPINEL.PROP_IPV6_ML_PREFIX: WPAN_PROP_HANDLER.IPV6_ML_PREFIX,
|
||||
SPINEL.PROP_IPV6_ADDRESS_TABLE: WPAN_PROP_HANDLER.IPV6_ADDRESS_TABLE,
|
||||
SPINEL.PROP_IPV6_ROUTE_TABLE: WPAN_PROP_HANDLER.IPV6_ROUTE_TABLE,
|
||||
SPINEL.PROP_IPv6_ICMP_PING_OFFLOAD: WPAN_PROP_HANDLER.IPv6_ICMP_PING_OFFLOAD,
|
||||
|
||||
SPINEL.PROP_STREAM_DEBUG: WPAN_PROP_HANDLER.STREAM_DEBUG,
|
||||
SPINEL.PROP_STREAM_RAW: WPAN_PROP_HANDLER.STREAM_RAW,
|
||||
SPINEL.PROP_STREAM_NET: WPAN_PROP_HANDLER.STREAM_NET,
|
||||
SPINEL.PROP_STREAM_NET_INSECURE: WPAN_PROP_HANDLER.STREAM_NET_INSECURE,
|
||||
|
||||
SPINEL.PROP_PIB_15_4_PHY_CHANNELS_SUPPORTED: WPAN_PROP_HANDLER.PIB_PHY_CHANNELS_SUPPORTED,
|
||||
SPINEL.PROP_PIB_15_4_MAC_PROMISCUOUS_MODE: WPAN_PROP_HANDLER.PIB_MAC_PROMISCUOUS_MODE,
|
||||
SPINEL.PROP_PIB_15_4_MAC_SECURITY_ENABLED: WPAN_PROP_HANDLER.PIB_MAC_SECURITY_ENABLED,
|
||||
}
|
||||
|
||||
|
||||
class WpanApi(SpinelCodec):
|
||||
""" Helper class to format wpan command packets """
|
||||
|
||||
def __init__(self, stream, nodeid, use_hdlc=FEATURE_USE_HDLC):
|
||||
|
||||
self.tun_if = None
|
||||
self.stream = stream
|
||||
self.nodeid = nodeid
|
||||
|
||||
self.use_hdlc = use_hdlc
|
||||
if self.use_hdlc:
|
||||
self.hdlc = Hdlc(self.stream)
|
||||
|
||||
# PARSER state
|
||||
self.rx_pkt = []
|
||||
|
||||
# Fire up threads
|
||||
self._reader_alive = True
|
||||
self.tid_filter = set()
|
||||
self.__queue_prop = defaultdict(Queue.Queue)
|
||||
self.queue_register()
|
||||
self.__start_reader()
|
||||
|
||||
def __del__(self):
|
||||
self._reader_alive = False
|
||||
|
||||
def __start_reader(self):
|
||||
"""Start reader thread"""
|
||||
self._reader_alive = True
|
||||
# start serial->console thread
|
||||
self.receiver_thread = threading.Thread(target=self.stream_rx)
|
||||
self.receiver_thread.setDaemon(True)
|
||||
self.receiver_thread.start()
|
||||
|
||||
def transact(self, command_id, payload="", tid=SPINEL.HEADER_DEFAULT):
|
||||
pkt = self.encode_packet(command_id, payload, tid)
|
||||
if CONFIG.DEBUG_LOG_SERIAL:
|
||||
msg = "TX Pay: (%i) %s " % (len(pkt), util.hexify_bytes(pkt))
|
||||
logging.debug(msg)
|
||||
|
||||
if self.use_hdlc:
|
||||
pkt = self.hdlc.encode(pkt)
|
||||
self.stream_tx(pkt)
|
||||
|
||||
def parse_rx(self, pkt):
|
||||
if not pkt:
|
||||
return
|
||||
|
||||
if CONFIG.DEBUG_LOG_SERIAL:
|
||||
msg = "RX Pay: (%i) %s " % (
|
||||
len(pkt), str(map(util.hexify_int, pkt)))
|
||||
logging.debug(msg)
|
||||
|
||||
length = len(pkt) - 2
|
||||
if length < 0:
|
||||
return
|
||||
|
||||
spkt = "".join(map(chr, pkt))
|
||||
|
||||
tid = self.parse_C(spkt[:1])
|
||||
(cmd_id, cmd_length) = self.parse_i(spkt[1:])
|
||||
pay_start = cmd_length + 1
|
||||
payload = spkt[pay_start:]
|
||||
|
||||
try:
|
||||
handler = SPINEL_COMMAND_DISPATCH[cmd_id]
|
||||
cmd_name = handler.__name__
|
||||
handler(self, payload, tid)
|
||||
|
||||
except Exception as _ex:
|
||||
print traceback.format_exc()
|
||||
cmd_name = "CB_Unknown"
|
||||
logging.info("\n%s (%i): ", cmd_name, cmd_id)
|
||||
|
||||
if CONFIG.DEBUG_CMD_RESPONSE:
|
||||
logging.info("\n%s (%i): ", cmd_name, cmd_id)
|
||||
logging.info("===> %s", util.hexify_str(payload))
|
||||
|
||||
def stream_tx(self, pkt):
|
||||
# Encapsulate lagging and Framer support in self.stream class.
|
||||
self.stream.write(pkt)
|
||||
|
||||
def stream_rx(self):
|
||||
""" Recieve thread and parser. """
|
||||
while self._reader_alive:
|
||||
if self.use_hdlc:
|
||||
self.rx_pkt = self.hdlc.collect()
|
||||
else:
|
||||
# size=None: Assume stream will always deliver packets
|
||||
pkt = self.stream.read(None)
|
||||
self.rx_pkt = util.packed_to_array(pkt)
|
||||
|
||||
self.parse_rx(self.rx_pkt)
|
||||
|
||||
class PropertyItem(object):
|
||||
""" Queue item for NCP response to property commands. """
|
||||
|
||||
def __init__(self, prop, value, tid):
|
||||
self.prop = prop
|
||||
self.value = value
|
||||
self.tid = tid
|
||||
|
||||
def queue_register(self, tid=SPINEL.HEADER_DEFAULT):
|
||||
self.tid_filter.add(tid)
|
||||
return self.__queue_prop[tid]
|
||||
|
||||
def queue_wait_prepare(self, _prop_id, tid=SPINEL.HEADER_DEFAULT):
|
||||
self.queue_clear(tid)
|
||||
|
||||
def queue_add(self, prop, value, tid):
|
||||
# Asynchronous handlers don't actually add to queue.
|
||||
if prop == SPINEL.PROP_STREAM_NET:
|
||||
pkt = IPv6(value[2:])
|
||||
if ICMPv6EchoReply in pkt:
|
||||
timenow = int(round(time.time() * 1000)) & 0xFFFFFFFF
|
||||
timedelta = (timenow - unpack('>I', pkt.data)[0])
|
||||
print "\n%d bytes from %s: icmp_seq=%d hlim=%d time=%dms" % (
|
||||
pkt.plen, pkt.src, pkt.seq, pkt.hlim, timedelta)
|
||||
return
|
||||
|
||||
if tid not in self.tid_filter:
|
||||
return
|
||||
item = self.PropertyItem(prop, value, tid)
|
||||
self.__queue_prop[tid].put_nowait(item)
|
||||
|
||||
def queue_clear(self, tid):
|
||||
with self.__queue_prop[tid].mutex:
|
||||
self.__queue_prop[tid].queue.clear()
|
||||
|
||||
def queue_wait_for_prop(self, _prop, tid=SPINEL.HEADER_DEFAULT, timeout=TIMEOUT_PROP):
|
||||
try:
|
||||
item = self.__queue_prop[tid].get(True, timeout)
|
||||
# self.__queue_prop[tid].task_done()
|
||||
except Queue.Empty:
|
||||
item = None
|
||||
|
||||
return item
|
||||
|
||||
def if_up(self, nodeid='1'):
|
||||
if os.geteuid() == 0:
|
||||
self.tun_if = TunInterface(nodeid)
|
||||
else:
|
||||
print "Warning: superuser required to start tun interface."
|
||||
|
||||
def if_down(self):
|
||||
if self.tun_if:
|
||||
self.tun_if.close()
|
||||
self.tun_if = None
|
||||
|
||||
def ip_send(self, pkt):
|
||||
pay = self.encode_i(SPINEL.PROP_STREAM_NET)
|
||||
|
||||
pkt_len = len(pkt)
|
||||
pay += pack("<H", pkt_len) # Start with length of IPv6 packet
|
||||
|
||||
pkt_len += 2 # Increment to include length word
|
||||
pay += pack("%ds" % pkt_len, pkt) # Append packet after length
|
||||
|
||||
self.transact(SPINEL.CMD_PROP_VALUE_SET, pay)
|
||||
|
||||
def cmd_send(self, command_id, payload="", tid=SPINEL.HEADER_DEFAULT):
|
||||
self.queue_wait_prepare(None, tid)
|
||||
self.transact(command_id, payload, tid)
|
||||
self.queue_wait_for_prop(None, tid)
|
||||
|
||||
def prop_change_async(self, cmd, prop_id, value, py_format='B',
|
||||
tid=SPINEL.HEADER_DEFAULT):
|
||||
pay = self.encode_i(prop_id)
|
||||
if py_format != None:
|
||||
pay += pack(py_format, value)
|
||||
self.transact(cmd, pay, tid)
|
||||
|
||||
def prop_insert_async(self, prop_id, value, py_format='B',
|
||||
tid=SPINEL.HEADER_DEFAULT):
|
||||
self.prop_change_async(SPINEL.CMD_PROP_VALUE_INSERT, prop_id,
|
||||
value, py_format, tid)
|
||||
|
||||
def prop_remove_async(self, prop_id, value, py_format='B',
|
||||
tid=SPINEL.HEADER_DEFAULT):
|
||||
self.prop_change_async(SPINEL.CMD_PROP_VALUE_REMOVE, prop_id,
|
||||
value, py_format, tid)
|
||||
|
||||
def __prop_change_value(self, cmd, prop_id, value, py_format='B',
|
||||
tid=SPINEL.HEADER_DEFAULT):
|
||||
""" Utility routine to change a property value over SPINEL. """
|
||||
self.queue_wait_prepare(prop_id, tid)
|
||||
|
||||
pay = self.encode_i(prop_id)
|
||||
if py_format != None:
|
||||
pay += pack(py_format, value)
|
||||
self.transact(cmd, pay, tid)
|
||||
|
||||
result = self.queue_wait_for_prop(prop_id, tid)
|
||||
if result:
|
||||
return result.value
|
||||
else:
|
||||
return None
|
||||
|
||||
def prop_get_value(self, prop_id, tid=SPINEL.HEADER_DEFAULT):
|
||||
""" Blocking routine to get a property value over SPINEL. """
|
||||
if CONFIG.DEBUG_LOG_PROP:
|
||||
handler = SPINEL_PROP_DISPATCH[prop_id]
|
||||
prop_name = handler.__name__
|
||||
print "PROP_VALUE_GET [tid=%d]: %s" % (tid & 0xF, prop_name)
|
||||
return self.__prop_change_value(SPINEL.CMD_PROP_VALUE_GET, prop_id,
|
||||
None, None, tid)
|
||||
|
||||
def prop_set_value(self, prop_id, value, py_format='B',
|
||||
tid=SPINEL.HEADER_DEFAULT):
|
||||
""" Blocking routine to set a property value over SPINEL. """
|
||||
if CONFIG.DEBUG_LOG_PROP:
|
||||
handler = SPINEL_PROP_DISPATCH[prop_id]
|
||||
prop_name = handler.__name__
|
||||
print "PROP_VALUE_SET [tid=%d]: %s" % (tid & 0xF, prop_name)
|
||||
return self.__prop_change_value(SPINEL.CMD_PROP_VALUE_SET, prop_id,
|
||||
value, py_format, tid)
|
||||
|
||||
def prop_insert_value(self, prop_id, value, py_format='B',
|
||||
tid=SPINEL.HEADER_DEFAULT):
|
||||
""" Blocking routine to insert a property value over SPINEL. """
|
||||
if CONFIG.DEBUG_LOG_PROP:
|
||||
handler = SPINEL_PROP_DISPATCH[prop_id]
|
||||
prop_name = handler.__name__
|
||||
print "PROP_VALUE_INSERT [tid=%d]: %s" % (tid & 0xF, prop_name)
|
||||
return self.__prop_change_value(SPINEL.CMD_PROP_VALUE_INSERT, prop_id,
|
||||
value, py_format, tid)
|
||||
|
||||
def prop_remove_value(self, prop_id, value, py_format='B',
|
||||
tid=SPINEL.HEADER_DEFAULT):
|
||||
""" Blocking routine to remove a property value over SPINEL. """
|
||||
if CONFIG.DEBUG_LOG_PROP:
|
||||
handler = SPINEL_PROP_DISPATCH[prop_id]
|
||||
prop_name = handler.__name__
|
||||
print "PROP_VALUE_REMOVE [tid=%d]: %s" % (tid & 0xF, prop_name)
|
||||
return self.__prop_change_value(SPINEL.CMD_PROP_VALUE_REMOVE, prop_id,
|
||||
value, py_format, tid)
|
||||
|
||||
def get_ipaddrs(self, tid=SPINEL.HEADER_DEFAULT):
|
||||
"""
|
||||
Return current list of ip addresses for the device.
|
||||
"""
|
||||
value = self.prop_get_value(SPINEL.PROP_IPV6_ADDRESS_TABLE, tid)
|
||||
# TODO: clean up table parsing to be less hard-coded magic.
|
||||
if value is None:
|
||||
return None
|
||||
size = 0x1B
|
||||
addrs = [value[i:i + size] for i in xrange(0, len(value), size)]
|
||||
ipaddrs = []
|
||||
for addr in addrs:
|
||||
addr = addr[2:18]
|
||||
ipaddrs.append(ipaddress.IPv6Address(addr))
|
||||
return ipaddrs
|
||||
@@ -0,0 +1,113 @@
|
||||
#
|
||||
# Copyright (c) 2016, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
""" Module-wide logging configuration for spinel package. """
|
||||
|
||||
import logging
|
||||
import logging.config
|
||||
|
||||
DEBUG_ENABLE = 0
|
||||
|
||||
DEBUG_TUN = 0
|
||||
DEBUG_HDLC = 0
|
||||
|
||||
DEBUG_STREAM_TX = 0
|
||||
DEBUG_STREAM_RX = 0
|
||||
|
||||
DEBUG_LOG_PKT = DEBUG_ENABLE
|
||||
DEBUG_LOG_SERIAL = DEBUG_ENABLE
|
||||
DEBUG_LOG_PROP = DEBUG_ENABLE
|
||||
DEBUG_CMD_RESPONSE = 0
|
||||
DEBUG_EXPERIMENTAL = 1
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
logging.config.dictConfig({
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
|
||||
'formatters': {
|
||||
'minimal': {
|
||||
'format': '%(message)s'
|
||||
},
|
||||
'standard': {
|
||||
'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
|
||||
},
|
||||
},
|
||||
'handlers': {
|
||||
'console': {
|
||||
#'level':'INFO',
|
||||
'level': 'DEBUG',
|
||||
'class': 'logging.StreamHandler',
|
||||
},
|
||||
#'syslog': {
|
||||
# 'level':'DEBUG',
|
||||
# 'class':'logging.handlers.SysLogHandler',
|
||||
# 'address': '/dev/log'
|
||||
#},
|
||||
},
|
||||
'loggers': {
|
||||
'': {
|
||||
'handlers': ['console'], # ,'syslog'],
|
||||
'level': 'DEBUG',
|
||||
'propagate': True
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
def debug_set_level(level):
|
||||
""" Set logging level for spinel module. """
|
||||
global DEBUG_ENABLE, DEBUG_LOG_PROP
|
||||
global DEBUG_LOG_PKT, DEBUG_LOG_SERIAL
|
||||
global DEBUG_STREAM_RX, DEBUG_STREAM_TX, DEBUG_HDLC
|
||||
|
||||
# Defaut to all logging disabled
|
||||
|
||||
DEBUG_ENABLE = 0
|
||||
DEBUG_LOG_PROP = 0
|
||||
DEBUG_LOG_PKT = 0
|
||||
DEBUG_LOG_SERIAL = 0
|
||||
DEBUG_HDLC = 0
|
||||
DEBUG_STREAM_RX = 0
|
||||
DEBUG_STREAM_TX = 0
|
||||
|
||||
if level:
|
||||
DEBUG_ENABLE = level
|
||||
if level >= 1:
|
||||
DEBUG_LOG_PROP = 1
|
||||
if level >= 2:
|
||||
DEBUG_LOG_PKT = 1
|
||||
if level >= 3:
|
||||
DEBUG_LOG_SERIAL = 1
|
||||
if level >= 4:
|
||||
DEBUG_HDLC = 1
|
||||
if level >= 5:
|
||||
DEBUG_STREAM_RX = 1
|
||||
DEBUG_STREAM_TX = 1
|
||||
|
||||
print "DEBUG_ENABLE = " + str(DEBUG_ENABLE)
|
||||
@@ -0,0 +1,393 @@
|
||||
#
|
||||
# Copyright (c) 2016, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
""" Module-wide constants for spinel package. """
|
||||
|
||||
class SPINEL(object):
|
||||
""" Singular class that contains all Spinel constants. """
|
||||
HEADER_ASYNC = 0x80
|
||||
HEADER_DEFAULT = 0x81
|
||||
HEADER_EVENT_HANDLER = 0x82
|
||||
|
||||
#=========================================
|
||||
# Spinel Commands: Host -> NCP
|
||||
#=========================================
|
||||
|
||||
CMD_NOOP = 0
|
||||
CMD_RESET = 1
|
||||
CMD_PROP_VALUE_GET = 2
|
||||
CMD_PROP_VALUE_SET = 3
|
||||
CMD_PROP_VALUE_INSERT = 4
|
||||
CMD_PROP_VALUE_REMOVE = 5
|
||||
|
||||
#=========================================
|
||||
# Spinel Command Responses: NCP -> Host
|
||||
#=========================================
|
||||
RSP_PROP_VALUE_IS = 6
|
||||
RSP_PROP_VALUE_INSERTED = 7
|
||||
RSP_PROP_VALUE_REMOVED = 8
|
||||
|
||||
CMD_NET_SAVE = 9
|
||||
CMD_NET_CLEAR = 10
|
||||
CMD_NET_RECALL = 11
|
||||
|
||||
RSP_HBO_OFFLOAD = 12
|
||||
RSP_HBO_RECLAIM = 13
|
||||
RSP_HBO_DROP = 14
|
||||
|
||||
CMD_HBO_OFFLOADED = 15
|
||||
CMD_HBO_RECLAIMED = 16
|
||||
CMD_HBO_DROPPED = 17
|
||||
|
||||
CMD_NEST__BEGIN = 15296
|
||||
CMD_NEST__END = 15360
|
||||
|
||||
CMD_VENDOR__BEGIN = 15360
|
||||
CMD_VENDOR__END = 16384
|
||||
|
||||
CMD_EXPERIMENTAL__BEGIN = 2000000
|
||||
CMD_EXPERIMENTAL__END = 2097152
|
||||
|
||||
#=========================================
|
||||
# Spinel Properties
|
||||
#=========================================
|
||||
|
||||
PROP_LAST_STATUS = 0 # < status [i]
|
||||
PROP_PROTOCOL_VERSION = 1 # < major, minor [i,i]
|
||||
PROP_NCP_VERSION = 2 # < version string [U]
|
||||
PROP_INTERFACE_TYPE = 3 # < [i]
|
||||
PROP_VENDOR_ID = 4 # < [i]
|
||||
PROP_CAPS = 5 # < capability list [A(i)]
|
||||
PROP_INTERFACE_COUNT = 6 # < Interface count [C]
|
||||
PROP_POWER_STATE = 7 # < PowerState [C]
|
||||
PROP_HWADDR = 8 # < PermEUI64 [E]
|
||||
PROP_LOCK = 9 # < PropLock [b]
|
||||
PROP_HBO_MEM_MAX = 10 # < Max offload mem [S]
|
||||
PROP_HBO_BLOCK_MAX = 11 # < Max offload block [S]
|
||||
|
||||
PROP_PHY__BEGIN = 0x20
|
||||
PROP_PHY_ENABLED = PROP_PHY__BEGIN + 0 # < [b]
|
||||
PROP_PHY_CHAN = PROP_PHY__BEGIN + 1 # < [C]
|
||||
PROP_PHY_CHAN_SUPPORTED = PROP_PHY__BEGIN + 2 # < [A(C)]
|
||||
PROP_PHY_FREQ = PROP_PHY__BEGIN + 3 # < kHz [L]
|
||||
PROP_PHY_CCA_THRESHOLD = PROP_PHY__BEGIN + 4 # < dBm [c]
|
||||
PROP_PHY_TX_POWER = PROP_PHY__BEGIN + 5 # < [c]
|
||||
PROP_PHY_RSSI = PROP_PHY__BEGIN + 6 # < dBm [c]
|
||||
PROP_PHY__END = 0x30
|
||||
|
||||
PROP_MAC__BEGIN = 0x30
|
||||
PROP_MAC_SCAN_STATE = PROP_MAC__BEGIN + 0 # < [C]
|
||||
PROP_MAC_SCAN_MASK = PROP_MAC__BEGIN + 1 # < [A(C)]
|
||||
PROP_MAC_SCAN_PERIOD = PROP_MAC__BEGIN + 2 # < ms-per-channel [S]
|
||||
# < chan,rssi,(laddr,saddr,panid,lqi),(proto,xtra) [CcT(ESSC.)T(i).]
|
||||
PROP_MAC_SCAN_BEACON = PROP_MAC__BEGIN + 3
|
||||
PROP_MAC_15_4_LADDR = PROP_MAC__BEGIN + 4 # < [E]
|
||||
PROP_MAC_15_4_SADDR = PROP_MAC__BEGIN + 5 # < [S]
|
||||
PROP_MAC_15_4_PANID = PROP_MAC__BEGIN + 6 # < [S]
|
||||
PROP_MAC_RAW_STREAM_ENABLED = PROP_MAC__BEGIN + 7 # < [C]
|
||||
PROP_MAC_FILTER_MODE = PROP_MAC__BEGIN + 8 # < [C]
|
||||
PROP_MAC__END = 0x40
|
||||
|
||||
PROP_MAC_EXT__BEGIN = 0x1300
|
||||
# Format: `A(T(Ec))`
|
||||
# * `E`: EUI64 address of node
|
||||
# * `c`: Optional fixed RSSI. -127 means not set.
|
||||
PROP_MAC_WHITELIST = PROP_MAC_EXT__BEGIN + 0
|
||||
PROP_MAC_WHITELIST_ENABLED = PROP_MAC_EXT__BEGIN + 1 # < [b]
|
||||
PROP_MAC_EXT__END = 0x1400
|
||||
|
||||
PROP_NET__BEGIN = 0x40
|
||||
PROP_NET_SAVED = PROP_NET__BEGIN + 0 # < [b]
|
||||
PROP_NET_IF_UP = PROP_NET__BEGIN + 1 # < [b]
|
||||
PROP_NET_STACK_UP = PROP_NET__BEGIN + 2 # < [C]
|
||||
PROP_NET_ROLE = PROP_NET__BEGIN + 3 # < [C]
|
||||
PROP_NET_NETWORK_NAME = PROP_NET__BEGIN + 4 # < [U]
|
||||
PROP_NET_XPANID = PROP_NET__BEGIN + 5 # < [D]
|
||||
PROP_NET_MASTER_KEY = PROP_NET__BEGIN + 6 # < [D]
|
||||
PROP_NET_KEY_SEQUENCE = PROP_NET__BEGIN + 7 # < [L]
|
||||
PROP_NET_PARTITION_ID = PROP_NET__BEGIN + 8 # < [L]
|
||||
PROP_NET__END = 0x50
|
||||
|
||||
PROP_THREAD__BEGIN = 0x50
|
||||
PROP_THREAD_LEADER_ADDR = PROP_THREAD__BEGIN + 0 # < [6]
|
||||
PROP_THREAD_PARENT = PROP_THREAD__BEGIN + 1 # < LADDR, SADDR [ES]
|
||||
PROP_THREAD_CHILD_TABLE = PROP_THREAD__BEGIN + 2 # < [A(T(ES))]
|
||||
PROP_THREAD_LEADER_RID = PROP_THREAD__BEGIN + 3 # < [C]
|
||||
PROP_THREAD_LEADER_WEIGHT = PROP_THREAD__BEGIN + 4 # < [C]
|
||||
PROP_THREAD_LOCAL_LEADER_WEIGHT = PROP_THREAD__BEGIN + 5 # < [C]
|
||||
PROP_THREAD_NETWORK_DATA = PROP_THREAD__BEGIN + 6 # < [D]
|
||||
PROP_THREAD_NETWORK_DATA_VERSION = PROP_THREAD__BEGIN + 7 # < [S]
|
||||
PROP_THREAD_STABLE_NETWORK_DATA = PROP_THREAD__BEGIN + 8 # < [D]
|
||||
PROP_THREAD_STABLE_NETWORK_DATA_VERSION = PROP_THREAD__BEGIN + 9 # < [S]
|
||||
# < array(ipv6prefix,prefixlen,stable,flags) [A(T(6CbC))]
|
||||
PROP_THREAD_ON_MESH_NETS = PROP_THREAD__BEGIN + 10
|
||||
# < array(ipv6prefix,prefixlen,stable,flags) [A(T(6CbC))]
|
||||
PROP_THREAD_LOCAL_ROUTES = PROP_THREAD__BEGIN + 11
|
||||
PROP_THREAD_ASSISTING_PORTS = PROP_THREAD__BEGIN + 12 # < array(portn) [A(S)]
|
||||
PROP_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE = PROP_THREAD__BEGIN + 13 # < [b]
|
||||
PROP_THREAD_MODE = PROP_THREAD__BEGIN + 14
|
||||
PROP_THREAD__END = 0x60
|
||||
|
||||
PROP_THREAD_EXT__BEGIN = 0x1500
|
||||
PROP_THREAD_CHILD_TIMEOUT = PROP_THREAD_EXT__BEGIN + 0 # < [L]
|
||||
PROP_THREAD_RLOC16 = PROP_THREAD_EXT__BEGIN + 1 # < [S]
|
||||
PROP_THREAD_ROUTER_UPGRADE_THRESHOLD = PROP_THREAD_EXT__BEGIN + 2 # < [C]
|
||||
PROP_THREAD_CONTEXT_REUSE_DELAY = PROP_THREAD_EXT__BEGIN + 3 # < [L]
|
||||
PROP_THREAD_NETWORK_ID_TIMEOUT = PROP_THREAD_EXT__BEGIN + 4 # < [b]
|
||||
PROP_THREAD_ACTIVE_ROUTER_IDS = PROP_THREAD_EXT__BEGIN + 5 # < [A(b)]
|
||||
PROP_THREAD_RLOC16_DEBUG_PASSTHRU = PROP_THREAD_EXT__BEGIN + 6 # < [b]
|
||||
PROP_THREAD_ROUTER_ROLE_ENABLED = PROP_THREAD_EXT__BEGIN + 7 # < [b]
|
||||
PROP_THREAD_ROUTER_DOWNGRADE_THRESHOLD = PROP_THREAD_EXT__BEGIN + 8 # < [C]
|
||||
PROP_THREAD_ROUTER_SELECTION_JITTER = PROP_THREAD_EXT__BEGIN + 9 # < [C]
|
||||
|
||||
PROP_THREAD_EXT__END = 0x1600
|
||||
|
||||
PROP_MESHCOP_EXT__BEGIN = 0x1600
|
||||
PROP_MESHCOP_JOINER_ENABLE = PROP_MESHCOP_EXT__BEGIN + 0 # < [b]
|
||||
PROP_MESHCOP_JOINER_CREDENTIAL = PROP_MESHCOP_EXT__BEGIN + 1 # < [D]
|
||||
PROP_MESHCOP_JOINER_URL = PROP_MESHCOP_EXT__BEGIN + 2 # < [U]
|
||||
PROP_MESHCOP_BORDER_AGENT_ENABLE = PROP_MESHCOP_EXT__BEGIN + 3 # < [b]
|
||||
PROP_MESHCOP_EXT__END = 0x1700
|
||||
|
||||
PROP_IPV6__BEGIN = 0x60
|
||||
PROP_IPV6_LL_ADDR = PROP_IPV6__BEGIN + 0 # < [6]
|
||||
PROP_IPV6_ML_ADDR = PROP_IPV6__BEGIN + 1 # < [6C]
|
||||
PROP_IPV6_ML_PREFIX = PROP_IPV6__BEGIN + 2 # < [6C]
|
||||
# < array(ipv6addr,prefixlen,valid,preferred,flags) [A(T(6CLLC))]
|
||||
PROP_IPV6_ADDRESS_TABLE = PROP_IPV6__BEGIN + 3
|
||||
# < array(ipv6prefix,prefixlen,iface,flags) [A(T(6CCC))]
|
||||
PROP_IPV6_ROUTE_TABLE = PROP_IPV6__BEGIN + 4
|
||||
PROP_IPv6_ICMP_PING_OFFLOAD = PROP_IPV6__BEGIN + 5 # < [b]
|
||||
|
||||
PROP_STREAM__BEGIN = 0x70
|
||||
PROP_STREAM_DEBUG = PROP_STREAM__BEGIN + 0 # < [U]
|
||||
PROP_STREAM_RAW = PROP_STREAM__BEGIN + 1 # < [D]
|
||||
PROP_STREAM_NET = PROP_STREAM__BEGIN + 2 # < [D]
|
||||
PROP_STREAM_NET_INSECURE = PROP_STREAM__BEGIN + 3 # < [D]
|
||||
PROP_STREAM__END = 0x80
|
||||
|
||||
# UART Bitrate
|
||||
# Format: `L`
|
||||
PROP_UART_BITRATE = 0x100
|
||||
|
||||
# UART Software Flow Control
|
||||
# Format: `b`
|
||||
PROP_UART_XON_XOFF = 0x101
|
||||
|
||||
PROP_PIB_15_4__BEGIN = 1024
|
||||
PROP_PIB_15_4_PHY_CHANNELS_SUPPORTED = PROP_PIB_15_4__BEGIN + 0x01 # < [A(L)]
|
||||
PROP_PIB_15_4_MAC_PROMISCUOUS_MODE = PROP_PIB_15_4__BEGIN + 0x51 # < [b]
|
||||
PROP_PIB_15_4_MAC_SECURITY_ENABLED = PROP_PIB_15_4__BEGIN + 0x5d # < [b]
|
||||
PROP_PIB_15_4__END = 1280
|
||||
|
||||
PROP_CNTR__BEGIN = 1280
|
||||
|
||||
# Counter reset behavior
|
||||
# Format: `C`
|
||||
PROP_CNTR_RESET = PROP_CNTR__BEGIN + 0
|
||||
|
||||
# The total number of transmissions.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_TX_PKT_TOTAL = PROP_CNTR__BEGIN + 1
|
||||
|
||||
# The number of transmissions with ack request.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_TX_PKT_ACK_REQ = PROP_CNTR__BEGIN + 2
|
||||
|
||||
# The number of transmissions that were acked.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_TX_PKT_ACKED = PROP_CNTR__BEGIN + 3
|
||||
|
||||
# The number of transmissions without ack request.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_TX_PKT_NO_ACK_REQ = PROP_CNTR__BEGIN + 4
|
||||
|
||||
# The number of transmitted data.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_TX_PKT_DATA = PROP_CNTR__BEGIN + 5
|
||||
|
||||
# The number of transmitted data poll.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_TX_PKT_DATA_POLL = PROP_CNTR__BEGIN + 6
|
||||
|
||||
# The number of transmitted beacon.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_TX_PKT_BEACON = PROP_CNTR__BEGIN + 7
|
||||
|
||||
# The number of transmitted beacon request.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_TX_PKT_BEACON_REQ = PROP_CNTR__BEGIN + 8
|
||||
|
||||
# The number of transmitted other types of frames.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_TX_PKT_OTHER = PROP_CNTR__BEGIN + 9
|
||||
|
||||
# The number of retransmission times.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_TX_PKT_RETRY = PROP_CNTR__BEGIN + 10
|
||||
|
||||
# The number of CCA failure times.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_TX_ERR_CCA = PROP_CNTR__BEGIN + 11
|
||||
|
||||
# The total number of received packets.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_RX_PKT_TOTAL = PROP_CNTR__BEGIN + 100
|
||||
|
||||
# The number of received data.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_RX_PKT_DATA = PROP_CNTR__BEGIN + 101
|
||||
|
||||
# The number of received data poll.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_RX_PKT_DATA_POLL = PROP_CNTR__BEGIN + 102
|
||||
|
||||
# The number of received beacon.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_RX_PKT_BEACON = PROP_CNTR__BEGIN + 103
|
||||
|
||||
# The number of received beacon request.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_RX_PKT_BEACON_REQ = PROP_CNTR__BEGIN + 104
|
||||
|
||||
# The number of received other types of frames.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_RX_PKT_OTHER = PROP_CNTR__BEGIN + 105
|
||||
|
||||
# The number of received packets filtered by whitelist.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_RX_PKT_FILT_WL = PROP_CNTR__BEGIN + 106
|
||||
|
||||
# The number of received packets filtered by destination check.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_RX_PKT_FILT_DA = PROP_CNTR__BEGIN + 107
|
||||
|
||||
# The number of received packets that are empty.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_RX_ERR_EMPTY = PROP_CNTR__BEGIN + 108
|
||||
|
||||
# The number of received packets from an unknown neighbor.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_RX_ERR_UKWN_NBR = PROP_CNTR__BEGIN + 109
|
||||
|
||||
# The number of received packets whose source address is invalid.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_RX_ERR_NVLD_SADDR = PROP_CNTR__BEGIN + 110
|
||||
|
||||
# The number of received packets with a security error.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_RX_ERR_SECURITY = PROP_CNTR__BEGIN + 111
|
||||
|
||||
# The number of received packets with a checksum error.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_RX_ERR_BAD_FCS = PROP_CNTR__BEGIN + 112
|
||||
|
||||
# The number of received packets with other errors.
|
||||
# Format: `L` (Read-only) */
|
||||
PROP_CNTR_RX_ERR_OTHER = PROP_CNTR__BEGIN + 113
|
||||
|
||||
#=========================================
|
||||
|
||||
MAC_FILTER_MDOE_NORMAL = 0
|
||||
MAC_FILTER_MODE_PROMISCUOUS = 1
|
||||
MAC_FILTER_MODE_MONITOR = 2
|
||||
|
||||
#=========================================
|
||||
|
||||
RSSI_OVERRIDE = 127
|
||||
|
||||
#=========================================
|
||||
|
||||
|
||||
class kThread(object):
|
||||
""" OpenThread constant class. """
|
||||
PrefixPreferenceOffset = 6
|
||||
PrefixPreferredFlag = 1 << 5
|
||||
PrefixSlaacFlag = 1 << 4
|
||||
PrefixDhcpFlag = 1 << 3
|
||||
PrefixConfigureFlag = 1 << 2
|
||||
PrefixDefaultRouteFlag = 1 << 1
|
||||
PrefixOnMeshFlag = 1 << 0
|
||||
|
||||
#=========================================
|
||||
|
||||
SPINEL_LAST_STATUS_MAP = {
|
||||
0: "STATUS_OK: Operation has completed successfully.",
|
||||
1: "STATUS_FAILURE: Operation has failed for some undefined reason.",
|
||||
2: "STATUS_UNIMPLEMENTED: The given operation has not been implemented.",
|
||||
3: "STATUS_INVALID_ARGUMENT: An argument to the given operation is invalid.",
|
||||
4: "STATUS_INVALID_STATE : The given operation is invalid for the current state of the device.",
|
||||
5: "STATUS_INVALID_COMMAND: The given command is not recognized.",
|
||||
6: "STATUS_INVALID_INTERFACE: The given Spinel interface is not supported.",
|
||||
7: "STATUS_INTERNAL_ERROR: An internal runtime error has occured.",
|
||||
8: "STATUS_SECURITY_ERROR: A security or authentication error has occured.",
|
||||
9: "STATUS_PARSE_ERROR: An error has occured while parsing the command.",
|
||||
10: "STATUS_IN_PROGRESS: The operation is in progress and will be completed asynchronously.",
|
||||
11: "STATUS_NOMEM: The operation has been prevented due to memory pressure.",
|
||||
12: "STATUS_BUSY: The device is currently performing a mutually exclusive operation.",
|
||||
13: "STATUS_PROPERTY_NOT_FOUND: The given property is not recognized.",
|
||||
14: "STATUS_PACKET_DROPPED: The packet was dropped.",
|
||||
15: "STATUS_EMPTY: The result of the operation is empty.",
|
||||
16: "STATUS_CMD_TOO_BIG: The command was too large to fit in the internal buffer.",
|
||||
17: "STATUS_NO_ACK: The packet was not acknowledged.",
|
||||
18: "STATUS_CCA_FAILURE: The packet was not sent due to a CCA failure.",
|
||||
19: "SPINEL_STATUS_ALREADY: The operation is already in progress.",
|
||||
20: "SPINEL_STATUS_ITEM_NOT_FOUND: The given item could not be found.",
|
||||
|
||||
104: "SPINEL_STATUS_JOIN_FAILURE",
|
||||
105: "SPINEL_STATUS_JOIN_SECURITY: The network key has been set incorrectly.",
|
||||
106: "SPINEL_STATUS_JOIN_NO_PEERS: The node was unable to find any other peers on the network.",
|
||||
107: "SPINEL_STATUS_JOIN_INCOMPATIBLE: The only potential peer nodes found are incompatible.",
|
||||
|
||||
112: "STATUS_RESET_POWER_ON",
|
||||
113: "STATUS_RESET_EXTERNAL",
|
||||
114: "STATUS_RESET_SOFTWARE",
|
||||
115: "STATUS_RESET_FAULT",
|
||||
116: "STATUS_RESET_CRASH",
|
||||
117: "STATUS_RESET_ASSERT",
|
||||
118: "STATUS_RESET_OTHER",
|
||||
119: "STATUS_RESET_UNKNOWN",
|
||||
120: "STATUS_RESET_WATCHDOG",
|
||||
|
||||
0x4000: "kThreadError_None",
|
||||
0x4001: "kThreadError_Failed",
|
||||
0x4002: "kThreadError_Drop",
|
||||
0x4003: "kThreadError_NoBufs",
|
||||
0x4004: "kThreadError_NoRoute",
|
||||
0x4005: "kThreadError_Busy",
|
||||
0x4006: "kThreadError_Parse",
|
||||
0x4007: "kThreadError_InvalidArgs",
|
||||
0x4008: "kThreadError_Security",
|
||||
0x4009: "kThreadError_AddressQuery",
|
||||
0x400A: "kThreadError_NoAddress",
|
||||
0x400B: "kThreadError_NotReceiving",
|
||||
0x400C: "kThreadError_Abort",
|
||||
0x400D: "kThreadError_NotImplemented",
|
||||
0x400E: "kThreadError_InvalidState",
|
||||
0x400F: "kThreadError_NoTasklets",
|
||||
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
#
|
||||
# Copyright (c) 2016, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
""" High-Level Data Link Control (HDLC) module. """
|
||||
|
||||
import logging
|
||||
|
||||
from struct import pack
|
||||
|
||||
import spinel.config as CONFIG
|
||||
from spinel.stream import IStream
|
||||
from spinel.util import hexify_int
|
||||
from spinel.util import hexify_bytes
|
||||
|
||||
HDLC_FLAG = 0x7e
|
||||
HDLC_ESCAPE = 0x7d
|
||||
|
||||
# RFC 1662 Appendix C
|
||||
|
||||
HDLC_FCS_INIT = 0xFFFF
|
||||
HDLC_FCS_POLY = 0x8408
|
||||
HDLC_FCS_GOOD = 0xF0B8
|
||||
|
||||
|
||||
class Hdlc(IStream):
|
||||
""" Utility class for HDLC encoding and decoding. """
|
||||
|
||||
def __init__(self, stream):
|
||||
self.stream = stream
|
||||
self.fcstab = self.mkfcstab()
|
||||
|
||||
@classmethod
|
||||
def mkfcstab(cls):
|
||||
""" Make a static lookup table for byte value to FCS16 result. """
|
||||
polynomial = HDLC_FCS_POLY
|
||||
|
||||
def valiter():
|
||||
""" Helper to yield FCS16 table entries for each byte value. """
|
||||
for byte in range(256):
|
||||
fcs = byte
|
||||
i = 8
|
||||
while i:
|
||||
fcs = (fcs >> 1) ^ polynomial if fcs & 1 else fcs >> 1
|
||||
i -= 1
|
||||
|
||||
yield fcs & 0xFFFF
|
||||
|
||||
return tuple(valiter())
|
||||
|
||||
def fcs16(self, byte, fcs):
|
||||
"""
|
||||
Return the next iteration of an fcs16 calculation
|
||||
given the next data byte and current fcs accumulator.
|
||||
"""
|
||||
fcs = (fcs >> 8) ^ self.fcstab[(fcs ^ byte) & 0xff]
|
||||
return fcs
|
||||
|
||||
def collect(self):
|
||||
""" Return the next valid packet to pass HDLC decoding on the stream. """
|
||||
fcs = HDLC_FCS_INIT
|
||||
packet = []
|
||||
raw = []
|
||||
|
||||
# Synchronize
|
||||
while 1:
|
||||
byte = self.stream.read()
|
||||
if CONFIG.DEBUG_HDLC:
|
||||
raw.append(byte)
|
||||
if byte == HDLC_FLAG:
|
||||
break
|
||||
|
||||
# Read packet, updating fcs, and escaping bytes as needed
|
||||
while 1:
|
||||
byte = self.stream.read()
|
||||
if CONFIG.DEBUG_HDLC:
|
||||
raw.append(byte)
|
||||
if byte == HDLC_FLAG:
|
||||
break
|
||||
if byte == HDLC_ESCAPE:
|
||||
byte = self.stream.read()
|
||||
if CONFIG.DEBUG_HDLC:
|
||||
raw.append(byte)
|
||||
byte ^= 0x20
|
||||
packet.append(byte)
|
||||
fcs = self.fcs16(byte, fcs)
|
||||
|
||||
if CONFIG.DEBUG_HDLC:
|
||||
logging.debug("RX Hdlc: " + str(map(hexify_int, raw)))
|
||||
|
||||
if fcs != HDLC_FCS_GOOD:
|
||||
packet = None
|
||||
else:
|
||||
packet = packet[:-2] # remove FCS16 from end
|
||||
|
||||
return packet
|
||||
|
||||
@classmethod
|
||||
def encode_byte(cls, byte, packet=[]):
|
||||
""" HDLC encode and append a single byte to the given packet. """
|
||||
if (byte == HDLC_ESCAPE) or (byte == HDLC_FLAG):
|
||||
packet.append(HDLC_ESCAPE)
|
||||
packet.append(byte ^ 0x20)
|
||||
else:
|
||||
packet.append(byte)
|
||||
return packet
|
||||
|
||||
def encode(self, payload=""):
|
||||
""" Return the HDLC encoding of the given packet. """
|
||||
fcs = HDLC_FCS_INIT
|
||||
packet = []
|
||||
packet.append(HDLC_FLAG)
|
||||
for byte in payload:
|
||||
byte = ord(byte)
|
||||
fcs = self.fcs16(byte, fcs)
|
||||
packet = self.encode_byte(byte, packet)
|
||||
|
||||
fcs ^= 0xffff
|
||||
byte = fcs & 0xFF
|
||||
packet = self.encode_byte(byte, packet)
|
||||
byte = fcs >> 8
|
||||
packet = self.encode_byte(byte, packet)
|
||||
packet.append(HDLC_FLAG)
|
||||
packet = pack("%dB" % len(packet), *packet)
|
||||
|
||||
if CONFIG.DEBUG_HDLC:
|
||||
logging.debug("TX Hdlc: " + hexify_bytes(packet))
|
||||
return packet
|
||||
|
||||
def write(self, data):
|
||||
""" HDLC encode and write the given data to this stream. """
|
||||
pkt = self.encode(data)
|
||||
self.stream.write(pkt)
|
||||
|
||||
def read(self, _size=None):
|
||||
""" Read and HDLC decode the next packet from this stream. """
|
||||
pkt = self.collect()
|
||||
return pkt
|
||||
@@ -0,0 +1,63 @@
|
||||
#
|
||||
# Copyright (c) 2016, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
""" Module to provide codec utilities for .pcap formatters. """
|
||||
|
||||
import struct
|
||||
from datetime import datetime
|
||||
|
||||
DLT_IEEE802_15_4 = 195
|
||||
PCAP_MAGIC_NUMBER = 0xa1b2c3d4
|
||||
PCAP_VERSION_MAJOR = 2
|
||||
PCAP_VERSION_MINOR = 4
|
||||
|
||||
|
||||
class PcapCodec(object):
|
||||
""" Utility class for .pcap formatters. """
|
||||
|
||||
@classmethod
|
||||
def encode_header(cls):
|
||||
""" Returns a pcap file header. """
|
||||
return struct.pack("<LHHLLLL",
|
||||
PCAP_MAGIC_NUMBER,
|
||||
PCAP_VERSION_MAJOR,
|
||||
PCAP_VERSION_MINOR,
|
||||
0, 0, 256,
|
||||
DLT_IEEE802_15_4)
|
||||
|
||||
@classmethod
|
||||
def encode_frame(cls, frame):
|
||||
""" Returns a pcap encapsulation of the given frame. """
|
||||
# write frame pcap header
|
||||
epoch = datetime(1970, 1, 1)
|
||||
d_time = datetime.utcnow() - epoch
|
||||
sec = d_time.days * 24 * 60 * 60 + d_time.seconds
|
||||
usec = d_time.microseconds
|
||||
length = len(frame)
|
||||
pcap_frame = struct.pack("<LLLL", sec, usec, length, length)
|
||||
pcap_frame += frame
|
||||
return pcap_frame
|
||||
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2016, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
"""
|
||||
Module providing a generic stream interface.
|
||||
Also includes adapter implementations for serial, socket, and pipes.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
import subprocess
|
||||
import socket
|
||||
import serial
|
||||
|
||||
import spinel.util
|
||||
import spinel.config as CONFIG
|
||||
|
||||
|
||||
class IStream(object):
|
||||
""" Abstract base class for a generic Stream Interface. """
|
||||
|
||||
def read(self, size):
|
||||
""" Read an array of byte integers of the given size from the stream. """
|
||||
pass
|
||||
|
||||
def write(self, data):
|
||||
""" Write the given packed data to the stream. """
|
||||
pass
|
||||
|
||||
def close(self):
|
||||
""" Close the stream cleanly as needed. """
|
||||
pass
|
||||
|
||||
|
||||
class StreamSerial(IStream):
|
||||
""" An IStream interface implementation for serial devices. """
|
||||
|
||||
def __init__(self, dev, baudrate=115200):
|
||||
try:
|
||||
self.serial = serial.Serial(dev, baudrate)
|
||||
except:
|
||||
logging.error("Couldn't open " + dev)
|
||||
print traceback.format_exc()
|
||||
|
||||
def write(self, data):
|
||||
self.serial.write(data)
|
||||
if CONFIG.DEBUG_STREAM_TX:
|
||||
logging.debug("TX Raw: " + str(map(spinel.util.hexify_chr, data)))
|
||||
|
||||
def read(self, size=1):
|
||||
pkt = self.serial.read(size)
|
||||
if CONFIG.DEBUG_STREAM_RX:
|
||||
logging.debug("RX Raw: " + str(map(spinel.util.hexify_chr, pkt)))
|
||||
return map(ord, pkt)[0]
|
||||
|
||||
|
||||
class StreamSocket(IStream):
|
||||
""" An IStream interface implementation over an internet socket. """
|
||||
|
||||
def __init__(self, hostname, port):
|
||||
# Open socket
|
||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.sock.connect((hostname, port))
|
||||
|
||||
def write(self, data):
|
||||
self.sock.send(data)
|
||||
if CONFIG.DEBUG_STREAM_TX:
|
||||
logging.debug("TX Raw: " + str(map(spinel.util.hexify_chr, data)))
|
||||
|
||||
def read(self, size=1):
|
||||
pkt = self.sock.recv(size)
|
||||
if CONFIG.DEBUG_STREAM_RX:
|
||||
logging.debug("RX Raw: " + str(map(spinel.util.hexify_chr, pkt)))
|
||||
return map(ord, pkt)[0]
|
||||
|
||||
|
||||
class StreamPipe(IStream):
|
||||
""" An IStream interface implementation to stdin/out of a piped process. """
|
||||
|
||||
def __init__(self, filename):
|
||||
""" Create a stream object from a piped system call """
|
||||
try:
|
||||
self.pipe = subprocess.Popen(filename, shell=True,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=sys.stdout.fileno())
|
||||
except:
|
||||
logging.error("Couldn't open " + filename)
|
||||
print traceback.format_exc()
|
||||
|
||||
def write(self, data):
|
||||
if CONFIG.DEBUG_STREAM_TX:
|
||||
logging.debug("TX Raw: (%d) %s",
|
||||
len(data), spinel.util.hexify_bytes(data))
|
||||
self.pipe.stdin.write(data)
|
||||
|
||||
def read(self, size=1):
|
||||
""" Blocking read on stream object """
|
||||
pkt = self.pipe.stdout.read(size)
|
||||
if CONFIG.DEBUG_STREAM_RX:
|
||||
logging.debug("RX Raw: " + str(map(spinel.util.hexify_chr, pkt)))
|
||||
return map(ord, pkt)[0]
|
||||
|
||||
def close(self):
|
||||
if self.pipe:
|
||||
self.pipe.kill()
|
||||
self.pipe = None
|
||||
|
||||
|
||||
def StreamOpen(stream_type, descriptor, verbose=True):
|
||||
"""
|
||||
Factory function that creates and opens a stream connection.
|
||||
|
||||
stream_type:
|
||||
'u' = uart (/dev/tty#)
|
||||
's' = socket (port #)
|
||||
'p' = pipe (stdin/stdout)
|
||||
|
||||
descriptor:
|
||||
uart - filename of device (/dev/tty#)
|
||||
socket - port to open connection to on localhost
|
||||
pipe - filename of command to execute and bind via stdin/stdout
|
||||
"""
|
||||
|
||||
if stream_type == 'p':
|
||||
if verbose:
|
||||
print "Opening pipe to " + str(descriptor)
|
||||
return StreamPipe(descriptor)
|
||||
|
||||
elif stream_type == 's':
|
||||
port = int(descriptor)
|
||||
hostname = "localhost"
|
||||
if verbose:
|
||||
print "Opening socket to " + hostname + ":" + str(port)
|
||||
return StreamSocket(hostname, port)
|
||||
|
||||
elif stream_type == 'u':
|
||||
dev = str(descriptor)
|
||||
baudrate = 115200
|
||||
if verbose:
|
||||
print "Opening serial to " + dev + " @ " + str(baudrate)
|
||||
return StreamSerial(dev, baudrate)
|
||||
|
||||
else:
|
||||
return None
|
||||
@@ -0,0 +1,71 @@
|
||||
#
|
||||
# Copyright (c) 2016, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
""" Unittest for spinel.codec module. """
|
||||
|
||||
import unittest
|
||||
|
||||
from spinel.const import SPINEL
|
||||
from spinel.codec import WpanApi
|
||||
from spinel.test_stream import MockStream
|
||||
|
||||
|
||||
class TestCodec(unittest.TestCase):
|
||||
""" Unit TestCase class for spinel.codec.SpinelCodec class. """
|
||||
|
||||
# Tests parsing and format demuxing of various properties with canned
|
||||
# values.
|
||||
VECTOR = {
|
||||
SPINEL.PROP_MAC_15_4_PANID: 65535,
|
||||
SPINEL.PROP_NCP_VERSION: "OPENTHREAD",
|
||||
SPINEL.PROP_NET_ROLE: 0,
|
||||
SPINEL.PROP_NET_KEY_SEQUENCE: 5,
|
||||
SPINEL.PROP_NET_NETWORK_NAME: "OpenThread",
|
||||
SPINEL.PROP_THREAD_MODE: 0xF,
|
||||
}
|
||||
|
||||
def test_prop_get(self):
|
||||
""" Unit test of SpinelCodec.prop_get_value. """
|
||||
|
||||
mock_stream = MockStream({
|
||||
# Request: Response
|
||||
"810236": "810636ffff", # get panid = 65535
|
||||
"810243": "81064300", # get state = detached
|
||||
"81025e": "81065e0f", # mode = 0xF
|
||||
"810202": "8106024f50454e54485245414400", # get version
|
||||
"810247": "81064705000000", # get keysequence
|
||||
"810244": "8106444f70656e54687265616400", # get networkname
|
||||
})
|
||||
nodeid = 1
|
||||
use_hdlc = False
|
||||
wpan_api = WpanApi(mock_stream, nodeid, use_hdlc)
|
||||
|
||||
for prop_id, truth_value in self.VECTOR.iteritems():
|
||||
value = wpan_api.prop_get_value(prop_id)
|
||||
# print "value "+util.hexify_str(value)
|
||||
# print "truth "+util.hexify_str(truth_value)
|
||||
self.failUnless(value == truth_value)
|
||||
@@ -0,0 +1,58 @@
|
||||
#
|
||||
# Copyright (c) 2016, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
"""
|
||||
Unittest for spinel.hdlc module.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import binascii
|
||||
|
||||
from spinel.hdlc import Hdlc
|
||||
|
||||
class TestHdlc(unittest.TestCase):
|
||||
""" Unittest class for spinel.hdlc.Hdlc class. """
|
||||
|
||||
VECTOR = {
|
||||
# Data HDLC Encoded
|
||||
"810243": "7e810243d3d37e",
|
||||
"8103367e7d": "7e8103367d5e7d5d6af97e",
|
||||
}
|
||||
|
||||
def test_hdlc_encode(self):
|
||||
""" Unit test for Hdle.encode method. """
|
||||
hdlc = Hdlc(None)
|
||||
for in_hex, out_hex in self.VECTOR.iteritems():
|
||||
in_binary = binascii.unhexlify(in_hex)
|
||||
out_binary = hdlc.encode(in_binary)
|
||||
#print "inHex = "+binascii.hexlify(in_binary)
|
||||
#print "outHex = "+binascii.hexlify(out_binary)
|
||||
self.failUnless(out_hex == binascii.hexlify(out_binary))
|
||||
|
||||
def test_hdlc_decode(self):
|
||||
""" Unit test for Hdle.decode method. """
|
||||
pass
|
||||
@@ -0,0 +1,66 @@
|
||||
#
|
||||
# Copyright (c) 2016, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
""" Unittest for spinel.codec module. """
|
||||
|
||||
import unittest
|
||||
|
||||
import spinel.util as util
|
||||
from spinel.const import SPINEL
|
||||
from spinel.codec import WpanApi
|
||||
from spinel.test_stream import MockStream
|
||||
|
||||
|
||||
class TestSniffer(unittest.TestCase):
|
||||
""" Unit TestCase class for sniffer relevant portions of spinel.codec.SpinelCodec. """
|
||||
|
||||
HEADER = "800671" # CMD_PROP_IS RAW_STREAM
|
||||
VECTOR = [
|
||||
# Some raw 6lo packets: ICMPv6EchoRequest to ff02::1, fe80::1, and MLE Advertisement
|
||||
"2d00499880fffffffffeff0d0100000001a7acdf3be9272c2d88765ff76f0bf08a7c3df0a78e9c1b23eb019c58740300800000",
|
||||
"3200699c81ffff0100000000000002feff0d030000000198e80cac00f8e0754e7542f5cb1171069f5c9689ef8d1d45a75e26b3f600800000",
|
||||
"450041d8980100ffffa8cb25ab2c32a0227f3b01f04d4c4d4cdc3b0015060000000000000001f226cce17968521d92904fec1adb0b94777030b944df65450bc955f05737e3901700800000"
|
||||
]
|
||||
|
||||
def test_prop_get(self):
|
||||
""" Unit test of SpinelCodec.prop_get_value. """
|
||||
|
||||
mock_stream = MockStream({})
|
||||
|
||||
nodeid = 1
|
||||
use_hdlc = False
|
||||
tid = SPINEL.HEADER_ASYNC
|
||||
prop_id = SPINEL.PROP_STREAM_RAW
|
||||
|
||||
wpan_api = WpanApi(mock_stream, nodeid, use_hdlc)
|
||||
wpan_api.queue_register(tid)
|
||||
|
||||
for truth in self.VECTOR:
|
||||
mock_stream.write_child_hex(self.HEADER+truth)
|
||||
result = wpan_api.queue_wait_for_prop(prop_id, tid)
|
||||
packet = util.hexify_str(result.value,"")
|
||||
self.failUnless(packet == truth)
|
||||
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2016, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
"""
|
||||
Tests for spinel.stream and implementation of MockStream class.
|
||||
"""
|
||||
|
||||
import binascii
|
||||
import logging
|
||||
|
||||
import Queue
|
||||
|
||||
import spinel.util as util
|
||||
import spinel.config as CONFIG
|
||||
from spinel.stream import IStream
|
||||
|
||||
|
||||
class MockStream(IStream):
|
||||
""" A pluggable IStream class for mock testing input/output data flows. """
|
||||
|
||||
def __init__(self, vector):
|
||||
"""
|
||||
Pass a test vector as dictionary of hexstream outputs keyed on inputs.
|
||||
"""
|
||||
self.vector = vector
|
||||
self.rx_queue = Queue.Queue()
|
||||
self.response = None
|
||||
|
||||
def write(self, out_binary):
|
||||
""" Write to the MockStream, triggering a lookup for mock response. """
|
||||
if CONFIG.DEBUG_STREAM_TX:
|
||||
logging.debug("TX Raw: (%d) %s", len(out_binary),
|
||||
util.hexify_bytes(out_binary))
|
||||
out_hex = binascii.hexlify(out_binary)
|
||||
in_hex = self.vector[out_hex]
|
||||
self.rx_queue.put_nowait(binascii.unhexlify(in_hex))
|
||||
|
||||
def read(self, size=None):
|
||||
""" Blocking read from the MockStream. """
|
||||
if not self.response or len(self.response) == 0:
|
||||
self.response = self.rx_queue.get(True)
|
||||
|
||||
if size:
|
||||
in_binary = self.response[:size]
|
||||
self.response = self.response[size:]
|
||||
else:
|
||||
in_binary = self.response
|
||||
self.response = None
|
||||
|
||||
if CONFIG.DEBUG_STREAM_RX:
|
||||
logging.debug("RX Raw: " + util.hexify_bytes(in_binary))
|
||||
return in_binary
|
||||
|
||||
def write_child(self, out_binary):
|
||||
""" Mock asynchronous write from child process. """
|
||||
self.rx_queue.put_nowait(out_binary)
|
||||
|
||||
def write_child_hex(self, out_hex):
|
||||
""" Mock asynchronous write from child process. """
|
||||
self.write_child(binascii.unhexlify(out_hex))
|
||||
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2016, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
from spinel.test_hdlc import TestHdlc
|
||||
from spinel.test_codec import TestCodec
|
||||
from spinel.test_sniffer import TestSniffer
|
||||
@@ -0,0 +1,153 @@
|
||||
#
|
||||
# Copyright (c) 2016, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
""" Utility class for creating TUN network interfaces on Linux and OSX. """
|
||||
|
||||
import os
|
||||
import sys
|
||||
import fcntl
|
||||
import struct
|
||||
import logging
|
||||
import threading
|
||||
import traceback
|
||||
import subprocess
|
||||
|
||||
from select import select
|
||||
|
||||
import spinel.util as util
|
||||
import spinel.config as CONFIG
|
||||
|
||||
IFF_TUN = 0x0001
|
||||
IFF_TAP = 0x0002
|
||||
IFF_NO_PI = 0x1000
|
||||
IFF_TUNSETIFF = 0x400454ca
|
||||
IFF_TUNSETOWNER = IFF_TUNSETIFF + 2
|
||||
|
||||
class TunInterface(object):
|
||||
""" Utility class for creating a TUN network interface. """
|
||||
|
||||
def __init__(self, identifier):
|
||||
self.identifier = identifier
|
||||
self.ifname = "tun" + str(self.identifier)
|
||||
self.tun = None
|
||||
self.fd = None
|
||||
|
||||
platform = sys.platform
|
||||
if platform == "linux" or platform == "linux2":
|
||||
self.__init_linux()
|
||||
elif platform == "darwin":
|
||||
self.__init_osx()
|
||||
|
||||
self.ifconfig("up")
|
||||
#self.ifconfig("inet6 add fd00::1/64")
|
||||
self.__start_tun_thread()
|
||||
|
||||
def __init_osx(self):
|
||||
logging.info("TUN: Starting osx " + self.ifname)
|
||||
filename = "/dev/" + self.ifname
|
||||
self.tun = os.open(filename, os.O_RDWR)
|
||||
self.fd = self.tun
|
||||
# trick osx to auto-assign a link local address
|
||||
self.addr_add("fe80::1")
|
||||
self.addr_del("fe80::1")
|
||||
|
||||
def __init_linux(self):
|
||||
logging.info("TUN: Starting linux " + self.ifname)
|
||||
self.tun = open("/dev/net/tun", "r+b")
|
||||
self.fd = self.tun.fileno()
|
||||
|
||||
ifr = struct.pack("16sH", self.ifname, IFF_TUN | IFF_NO_PI)
|
||||
fcntl.ioctl(self.tun, IFF_TUNSETIFF, ifr) # Name interface tun#
|
||||
fcntl.ioctl(self.tun, IFF_TUNSETOWNER, 1000) # Allow non-sudo access
|
||||
|
||||
def close(self):
|
||||
""" Close this tunnel interface. """
|
||||
if self.tun:
|
||||
os.close(self.fd)
|
||||
self.fd = None
|
||||
self.tun = None
|
||||
|
||||
@classmethod
|
||||
def command(cls, cmd):
|
||||
""" Utility to make a system call. """
|
||||
subprocess.check_call(cmd, shell=True)
|
||||
|
||||
def ifconfig(self, args):
|
||||
""" Bring interface up and/or assign addresses. """
|
||||
self.command('ifconfig ' + self.ifname + ' ' + args)
|
||||
|
||||
def ping6(self, args):
|
||||
""" Ping an address. """
|
||||
cmd = 'ping6 ' + args
|
||||
print cmd
|
||||
self.command(cmd)
|
||||
|
||||
def addr_add(self, addr):
|
||||
""" Add the given IPv6 address to the tunnel interface. """
|
||||
self.ifconfig('inet6 add ' + addr)
|
||||
|
||||
def addr_del(self, addr):
|
||||
""" Delete the given IPv6 address from the tunnel interface. """
|
||||
platform = sys.platform
|
||||
if platform == "linux" or platform == "linux2":
|
||||
self.ifconfig('inet6 del ' + addr)
|
||||
elif platform == "darwin":
|
||||
self.ifconfig('inet6 delete ' + addr)
|
||||
|
||||
def write(self, packet):
|
||||
#global gWpanApi
|
||||
#gWpanApi.ip_send(packet)
|
||||
# os.write(self.fd, packet) # Loop back
|
||||
if CONFIG.DEBUG_TUN:
|
||||
logging.debug("\nTUN: TX (" + str(len(packet)) +
|
||||
") " + util.hexify_str(packet))
|
||||
|
||||
def __run_tun_thread(self):
|
||||
while self.fd:
|
||||
try:
|
||||
ready_fd = select([self.fd], [], [])[0][0]
|
||||
if ready_fd == self.fd:
|
||||
packet = os.read(self.fd, 4000)
|
||||
if CONFIG.DEBUG_TUN:
|
||||
logging.debug("\nTUN: RX (" + str(len(packet)) + ") " +
|
||||
util.hexify_str(packet))
|
||||
self.write(packet)
|
||||
except:
|
||||
print traceback.format_exc()
|
||||
break
|
||||
|
||||
logging.info("TUN: exiting")
|
||||
if self.fd:
|
||||
os.close(self.fd)
|
||||
self.fd = None
|
||||
|
||||
def __start_tun_thread(self):
|
||||
"""Start reader thread"""
|
||||
self._reader_alive = True
|
||||
self.receiver_thread = threading.Thread(target=self.__run_tun_thread)
|
||||
self.receiver_thread.setDaemon(True)
|
||||
self.receiver_thread.start()
|
||||
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2016, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
def hexify_chr(s): return "%02X" % ord(s)
|
||||
def hexify_int(i): return "%02X" % i
|
||||
def hexify_bytes(data): return str(map(hexify_chr,data))
|
||||
def hexify_str(s,delim=':'):
|
||||
return delim.join(x.encode('hex') for x in s)
|
||||
|
||||
def pack_bytes(packet): return pack("%dB" % len(packet), *packet)
|
||||
def packed_to_array(packet): return map(ord, packet)
|
||||
|
||||
def asciify_int(i): return "%c" % (i)
|
||||
|
||||
def hex_to_bytes(s):
|
||||
result = ''
|
||||
for i in xrange(0, len(s), 2):
|
||||
(b1, b2) = s[i:i+2]
|
||||
hex = b1+b2
|
||||
v = int(hex, 16)
|
||||
result += chr(v)
|
||||
return result
|
||||
Executable
+55
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2016, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
""" Run all unittests for spinel module. """
|
||||
|
||||
import sys
|
||||
import optparse
|
||||
import unittest
|
||||
|
||||
import spinel.config as CONFIG
|
||||
from spinel.tests import *
|
||||
|
||||
def main():
|
||||
""" Run all unit tests for spinel module. """
|
||||
args = sys.argv[1:]
|
||||
|
||||
opt_parser = optparse.OptionParser()
|
||||
opt_parser.add_option("-d", "--debug", action="store",
|
||||
dest="debug", type="int", default=CONFIG.DEBUG_ENABLE)
|
||||
|
||||
(options, remaining_args) = opt_parser.parse_args(args)
|
||||
|
||||
if options.debug:
|
||||
CONFIG.debug_set_level(options.debug)
|
||||
|
||||
sys.argv[1:] = remaining_args
|
||||
unittest.main()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user