wpantund: Initial Open-Source Release
diff --git a/.default-version b/.default-version
new file mode 100644
index 0000000..bcf7a10
--- /dev/null
+++ b/.default-version
@@ -0,0 +1 @@
+0.07.00
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..aa99ff1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,56 @@
+.git
+.DS_Store
+*.swp
+*.o
+*.lo
+.deps
+Makefile.in
+/Makefile
+/doc/Makefile
+/src/Makefile
+/src/connman-plugin/Makefile
+/src/ipc-dbus/Makefile
+/src/ncp-spinel/Makefile
+/src/ncp-dummy/Makefile
+/src/util/Makefile
+/src/wpanctl/Makefile
+/src/wpantund/Makefile
+/third_party/Makefile
+*.gcda
+*.gcno
+*~
+*~.c
+*.xcuserdatad
+*.orig
+xcuserdata
+.gdb_history
+*.a
+*.la
+.gdbinit
+/build
+/build-*
+/DerivedData
+/autom4te.cache
+/aclocal.m4
+/config.h.in
+/config.log
+/config.status
+/configure
+/libtool
+/src/config.h
+/m4
+/wpantund-*/
+/wpantund-*.tar.*
+src/stamp-h1
+src/config.h.in
+doxygen.cfg
+*.gz
+src/wpantund/wpantund
+src/wpanctl/wpanctl
+.dirstamp
+src/wpanctl/version.c
+wpantund.xcodeproj/project.xcworkspace/xcshareddata/wpan-tunnel-driver.xccheckout
+wpantund.xcodeproj/project.xcworkspace/xcshareddata/wpantund.xccheckout
+.cproject
+.project
+src/wpantund/.libs/wpantund
diff --git a/.uncrustify.cfg b/.uncrustify.cfg
new file mode 100644
index 0000000..e5f1502
--- /dev/null
+++ b/.uncrustify.cfg
@@ -0,0 +1,1578 @@
+# Uncrustify 0.60
+
+#
+# General options
+#
+
+# The type of line endings
+newlines                                 = auto     # auto/lf/crlf/cr
+
+# The original size of tabs in the input
+input_tab_size                           = 4        # number
+
+# The size of tabs in the output (only used if align_with_tabs=true)
+output_tab_size                          = 4        # number
+
+# The ASCII value of the string escape char, usually 92 (\) or 94 (^). (Pawn)
+string_escape_char                       = 92       # number
+
+# Alternate string escape char for Pawn. Only works right before the quote char.
+string_escape_char2                      = 0        # number
+
+# Allow interpreting '>=' and '>>=' as part of a template in 'void f(list<list<B>>=val);'.
+# If true (default), 'assert(x<0 && y>=3)' will be broken.
+# Improvements to template detection may make this option obsolete.
+tok_split_gte                            = false    # false/true
+
+# Control what to do with the UTF-8 BOM (recommend 'remove')
+utf8_bom                                 = ignore   # ignore/add/remove/force
+
+# If the file contains bytes with values between 128 and 255, but is not UTF-8, then output as UTF-8
+utf8_byte                                = false    # false/true
+
+# Force the output encoding to UTF-8
+utf8_force                               = false    # false/true
+
+#
+# Indenting
+#
+
+# The number of columns to indent per level.
+# Usually 2, 3, 4, or 8.
+indent_columns                           = 4        # number
+
+# The continuation indent. If non-zero, this overrides the indent of '(' and '=' continuation indents.
+# For FreeBSD, this is set to 4. Negative value is absolute and not increased for each ( level
+indent_continue                          = 0        # number
+
+# How to use tabs when indenting code
+# 0=spaces only
+# 1=indent with tabs to brace level, align with spaces
+# 2=indent and align with tabs, using spaces when not on a tabstop
+indent_with_tabs                         = 1        # number
+
+# Comments that are not a brace level are indented with tabs on a tabstop.
+# Requires indent_with_tabs=2. If false, will use spaces.
+indent_cmt_with_tabs                     = false    # false/true
+
+# Whether to indent strings broken by '\' so that they line up
+indent_align_string                      = false    # false/true
+
+# The number of spaces to indent multi-line XML strings.
+# Requires indent_align_string=True
+indent_xml_string                        = 0        # number
+
+# Spaces to indent '{' from level
+indent_brace                             = 0        # number
+
+# Whether braces are indented to the body level
+indent_braces                            = false    # false/true
+
+# Disabled indenting function braces if indent_braces is true
+indent_braces_no_func                    = false    # false/true
+
+# Disabled indenting class braces if indent_braces is true
+indent_braces_no_class                   = false    # false/true
+
+# Disabled indenting struct braces if indent_braces is true
+indent_braces_no_struct                  = false    # false/true
+
+# Indent based on the size of the brace parent, i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc.
+indent_brace_parent                      = false    # false/true
+
+# Whether the 'namespace' body is indented
+indent_namespace                         = false    # false/true
+
+# The number of spaces to indent a namespace block
+indent_namespace_level                   = 0        # number
+
+# If the body of the namespace is longer than this number, it won't be indented.
+# Requires indent_namespace=true. Default=0 (no limit)
+indent_namespace_limit                   = 0        # number
+
+# Whether the 'extern "C"' body is indented
+indent_extern                            = false    # false/true
+
+# Whether the 'class' body is indented
+indent_class                             = true    # false/true
+
+# Whether to indent the stuff after a leading class colon
+indent_class_colon                       = false    # false/true
+
+# Virtual indent from the ':' for member initializers. Default is 2
+indent_ctor_init_leading                 = 2        # number
+
+# Additional indenting for constructor initializer list
+indent_ctor_init                         = 0        # number
+
+# False=treat 'else\nif' as 'else if' for indenting purposes
+# True=indent the 'if' one level
+indent_else_if                           = false    # false/true
+
+# Amount to indent variable declarations after a open brace. neg=relative, pos=absolute
+indent_var_def_blk                       = 0        # number
+
+# Indent continued variable declarations instead of aligning.
+indent_var_def_cont                      = false    # false/true
+
+# True:  force indentation of function definition to start in column 1
+# False: use the default behavior
+indent_func_def_force_col1               = false    # false/true
+
+# True:  indent continued function call parameters one indent level
+# False: align parameters under the open paren
+indent_func_call_param                   = false    # false/true
+
+# Same as indent_func_call_param, but for function defs
+indent_func_def_param                    = false    # false/true
+
+# Same as indent_func_call_param, but for function protos
+indent_func_proto_param                  = false    # false/true
+
+# Same as indent_func_call_param, but for class declarations
+indent_func_class_param                  = false    # false/true
+
+# Same as indent_func_call_param, but for class variable constructors
+indent_func_ctor_var_param               = false    # false/true
+
+# Same as indent_func_call_param, but for templates
+indent_template_param                    = false    # false/true
+
+# Double the indent for indent_func_xxx_param options
+indent_func_param_double                 = false    # false/true
+
+# Indentation column for standalone 'const' function decl/proto qualifier
+indent_func_const                        = 0        # number
+
+# Indentation column for standalone 'throw' function decl/proto qualifier
+indent_func_throw                        = 0        # number
+
+# The number of spaces to indent a continued '->' or '.'
+# Usually set to 0, 1, or indent_columns.
+indent_member                            = 0        # number
+
+# Spaces to indent single line ('//') comments on lines before code
+indent_sing_line_comments                = 0        # number
+
+# If set, will indent trailing single line ('//') comments relative
+# to the code instead of trying to keep the same absolute column
+indent_relative_single_line_comments     = false    # false/true
+
+# Spaces to indent 'case' from 'switch'
+# Usually 0 or indent_columns.
+indent_switch_case                       = 0        # number
+
+# Spaces to shift the 'case' line, without affecting any other lines
+# Usually 0.
+indent_case_shift                        = 0        # number
+
+# Spaces to indent '{' from 'case'.
+# By default, the brace will appear under the 'c' in case.
+# Usually set to 0 or indent_columns.
+indent_case_brace                        = 0        # number
+
+# Whether to indent comments found in first column
+indent_col1_comment                      = false    # false/true
+
+# How to indent goto labels
+#  >0 : absolute column where 1 is the leftmost column
+#  <=0 : subtract from brace indent
+indent_label                             = 1        # number
+
+# Same as indent_label, but for access specifiers that are followed by a colon
+indent_access_spec                       = 1        # number
+
+# Indent the code after an access specifier by one level.
+# If set, this option forces 'indent_access_spec=0'
+indent_access_spec_body                  = false    # false/true
+
+# If an open paren is followed by a newline, indent the next line so that it lines up after the open paren (not recommended)
+indent_paren_nl                          = false    # false/true
+
+# Controls the indent of a close paren after a newline.
+# 0: Indent to body level
+# 1: Align under the open paren
+# 2: Indent to the brace level
+indent_paren_close                       = 0        # number
+
+# Controls the indent of a comma when inside a paren.If TRUE, aligns under the open paren
+indent_comma_paren                       = false    # false/true
+
+# Controls the indent of a BOOL operator when inside a paren.If TRUE, aligns under the open paren
+indent_bool_paren                        = false    # false/true
+
+# If 'indent_bool_paren' is true, controls the indent of the first expression. If TRUE, aligns the first expression to the following ones
+indent_first_bool_expr                   = false    # false/true
+
+# If an open square is followed by a newline, indent the next line so that it lines up after the open square (not recommended)
+indent_square_nl                         = false    # false/true
+
+# Don't change the relative indent of ESQL/C 'EXEC SQL' bodies
+indent_preserve_sql                      = false    # false/true
+
+# Align continued statements at the '='. Default=True
+# If FALSE or the '=' is followed by a newline, the next line is indent one tab.
+indent_align_assign                      = true     # false/true
+
+# Indent OC blocks at brace level instead of usual rules.
+indent_oc_block                          = false    # false/true
+
+# Indent OC blocks in a message relative to the parameter name.
+# 0=use indent_oc_block rules, 1+=spaces to indent
+indent_oc_block_msg                      = 0        # number
+
+# Minimum indent for subsequent parameters
+indent_oc_msg_colon                      = 0        # number
+
+#
+# Spacing options
+#
+
+# Add or remove space around arithmetic operator '+', '-', '/', '*', etc
+sp_arith                                 = ignore   # ignore/add/remove/force
+
+# Add or remove space around assignment operator '=', '+=', etc
+sp_assign                                = ignore   # ignore/add/remove/force
+
+# Add or remove space around '=' in C++11 lambda capture specifications. Overrides sp_assign
+sp_cpp_lambda_assign                     = ignore   # ignore/add/remove/force
+
+# Add or remove space after the capture specification in C++11 lambda.
+sp_cpp_lambda_paren                      = ignore   # ignore/add/remove/force
+
+# Add or remove space around assignment operator '=' in a prototype
+sp_assign_default                        = ignore   # ignore/add/remove/force
+
+# Add or remove space before assignment operator '=', '+=', etc. Overrides sp_assign.
+sp_before_assign                         = ignore   # ignore/add/remove/force
+
+# Add or remove space after assignment operator '=', '+=', etc. Overrides sp_assign.
+sp_after_assign                          = ignore   # ignore/add/remove/force
+
+# Add or remove space around assignment '=' in enum
+sp_enum_assign                           = ignore   # ignore/add/remove/force
+
+# Add or remove space before assignment '=' in enum. Overrides sp_enum_assign.
+sp_enum_before_assign                    = ignore   # ignore/add/remove/force
+
+# Add or remove space after assignment '=' in enum. Overrides sp_enum_assign.
+sp_enum_after_assign                     = ignore   # ignore/add/remove/force
+
+# Add or remove space around preprocessor '##' concatenation operator. Default=Add
+sp_pp_concat                             = add      # ignore/add/remove/force
+
+# Add or remove space after preprocessor '#' stringify operator. Also affects the '#@' charizing operator.
+sp_pp_stringify                          = ignore   # ignore/add/remove/force
+
+# Add or remove space before preprocessor '#' stringify operator as in '#define x(y) L#y'.
+sp_before_pp_stringify                   = ignore   # ignore/add/remove/force
+
+# Add or remove space around boolean operators '&&' and '||'
+sp_bool                                  = ignore   # ignore/add/remove/force
+
+# Add or remove space around compare operator '<', '>', '==', etc
+sp_compare                               = ignore   # ignore/add/remove/force
+
+# Add or remove space inside '(' and ')'
+sp_inside_paren                          = ignore   # ignore/add/remove/force
+
+# Add or remove space between nested parens
+sp_paren_paren                           = ignore   # ignore/add/remove/force
+
+# Whether to balance spaces inside nested parens
+sp_balance_nested_parens                 = false    # false/true
+
+# Add or remove space between ')' and '{'
+sp_paren_brace                           = ignore   # ignore/add/remove/force
+
+# Add or remove space before pointer star '*'
+sp_before_ptr_star                       = ignore   # ignore/add/remove/force
+
+# Add or remove space before pointer star '*' that isn't followed by a variable name
+# If set to 'ignore', sp_before_ptr_star is used instead.
+sp_before_unnamed_ptr_star               = ignore   # ignore/add/remove/force
+
+# Add or remove space between pointer stars '*'
+sp_between_ptr_star                      = ignore   # ignore/add/remove/force
+
+# Add or remove space after pointer star '*', if followed by a word.
+sp_after_ptr_star                        = ignore   # ignore/add/remove/force
+
+# Add or remove space after a pointer star '*', if followed by a func proto/def.
+sp_after_ptr_star_func                   = ignore   # ignore/add/remove/force
+
+# Add or remove space after a pointer star '*', if followed by an open paren (function types).
+sp_ptr_star_paren                        = ignore   # ignore/add/remove/force
+
+# Add or remove space before a pointer star '*', if followed by a func proto/def.
+sp_before_ptr_star_func                  = ignore   # ignore/add/remove/force
+
+# Add or remove space before a reference sign '&'
+sp_before_byref                          = ignore   # ignore/add/remove/force
+
+# Add or remove space before a reference sign '&' that isn't followed by a variable name
+# If set to 'ignore', sp_before_byref is used instead.
+sp_before_unnamed_byref                  = ignore   # ignore/add/remove/force
+
+# Add or remove space after reference sign '&', if followed by a word.
+sp_after_byref                           = ignore   # ignore/add/remove/force
+
+# Add or remove space after a reference sign '&', if followed by a func proto/def.
+sp_after_byref_func                      = ignore   # ignore/add/remove/force
+
+# Add or remove space before a reference sign '&', if followed by a func proto/def.
+sp_before_byref_func                     = ignore   # ignore/add/remove/force
+
+# Add or remove space between type and word. Default=Force
+sp_after_type                            = force    # ignore/add/remove/force
+
+# Add or remove space before the paren in the D constructs 'template Foo(' and 'class Foo('.
+sp_before_template_paren                 = ignore   # ignore/add/remove/force
+
+# Add or remove space in 'template <' vs 'template<'.
+# If set to ignore, sp_before_angle is used.
+sp_template_angle                        = ignore   # ignore/add/remove/force
+
+# Add or remove space before '<>'
+sp_before_angle                          = ignore   # ignore/add/remove/force
+
+# Add or remove space inside '<' and '>'
+sp_inside_angle                          = ignore   # ignore/add/remove/force
+
+# Add or remove space after '<>'
+sp_after_angle                           = ignore   # ignore/add/remove/force
+
+# Add or remove space between '<>' and '(' as found in 'new List<byte>();'
+sp_angle_paren                           = ignore   # ignore/add/remove/force
+
+# Add or remove space between '<>' and a word as in 'List<byte> m;'
+sp_angle_word                            = ignore   # ignore/add/remove/force
+
+# Add or remove space between '>' and '>' in '>>' (template stuff C++/C# only). Default=Add
+sp_angle_shift                           = add      # ignore/add/remove/force
+
+# Permit removal of the space between '>>' in 'foo<bar<int> >' (C++11 only). Default=False
+# sp_angle_shift cannot remove the space without this option.
+sp_permit_cpp11_shift                    = false    # false/true
+
+# Add or remove space before '(' of 'if', 'for', 'switch', and 'while'
+sp_before_sparen                         = ignore   # ignore/add/remove/force
+
+# Add or remove space inside if-condition '(' and ')'
+sp_inside_sparen                         = ignore   # ignore/add/remove/force
+
+# Add or remove space before if-condition ')'. Overrides sp_inside_sparen.
+sp_inside_sparen_close                   = ignore   # ignore/add/remove/force
+
+# Add or remove space before if-condition '('. Overrides sp_inside_sparen.
+sp_inside_sparen_open                    = ignore   # ignore/add/remove/force
+
+# Add or remove space after ')' of 'if', 'for', 'switch', and 'while'
+sp_after_sparen                          = ignore   # ignore/add/remove/force
+
+# Add or remove space between ')' and '{' of 'if', 'for', 'switch', and 'while'
+sp_sparen_brace                          = ignore   # ignore/add/remove/force
+
+# Add or remove space between 'invariant' and '(' in the D language.
+sp_invariant_paren                       = ignore   # ignore/add/remove/force
+
+# Add or remove space after the ')' in 'invariant (C) c' in the D language.
+sp_after_invariant_paren                 = ignore   # ignore/add/remove/force
+
+# Add or remove space before empty statement ';' on 'if', 'for' and 'while'
+sp_special_semi                          = ignore   # ignore/add/remove/force
+
+# Add or remove space before ';'. Default=Remove
+sp_before_semi                           = remove   # ignore/add/remove/force
+
+# Add or remove space before ';' in non-empty 'for' statements
+sp_before_semi_for                       = ignore   # ignore/add/remove/force
+
+# Add or remove space before a semicolon of an empty part of a for statement.
+sp_before_semi_for_empty                 = ignore   # ignore/add/remove/force
+
+# Add or remove space after ';', except when followed by a comment. Default=Add
+sp_after_semi                            = add      # ignore/add/remove/force
+
+# Add or remove space after ';' in non-empty 'for' statements. Default=Force
+sp_after_semi_for                        = force    # ignore/add/remove/force
+
+# Add or remove space after the final semicolon of an empty part of a for statement: for ( ; ; <here> ).
+sp_after_semi_for_empty                  = ignore   # ignore/add/remove/force
+
+# Add or remove space before '[' (except '[]')
+sp_before_square                         = ignore   # ignore/add/remove/force
+
+# Add or remove space before '[]'
+sp_before_squares                        = ignore   # ignore/add/remove/force
+
+# Add or remove space inside a non-empty '[' and ']'
+sp_inside_square                         = ignore   # ignore/add/remove/force
+
+# Add or remove space after ','
+sp_after_comma                           = ignore   # ignore/add/remove/force
+
+# Add or remove space before ','
+sp_before_comma                          = remove   # ignore/add/remove/force
+
+# Add or remove space between an open paren and comma: '(,' vs '( ,'
+sp_paren_comma                           = force    # ignore/add/remove/force
+
+# Add or remove space before the variadic '...' when preceded by a non-punctuator
+sp_before_ellipsis                       = ignore   # ignore/add/remove/force
+
+# Add or remove space after class ':'
+sp_after_class_colon                     = ignore   # ignore/add/remove/force
+
+# Add or remove space before class ':'
+sp_before_class_colon                    = ignore   # ignore/add/remove/force
+
+# Add or remove space before case ':'. Default=Remove
+sp_before_case_colon                     = remove   # ignore/add/remove/force
+
+# Add or remove space between 'operator' and operator sign
+sp_after_operator                        = ignore   # ignore/add/remove/force
+
+# Add or remove space between the operator symbol and the open paren, as in 'operator ++('
+sp_after_operator_sym                    = ignore   # ignore/add/remove/force
+
+# Add or remove space after C/D cast, i.e. 'cast(int)a' vs 'cast(int) a' or '(int)a' vs '(int) a'
+sp_after_cast                            = ignore   # ignore/add/remove/force
+
+# Add or remove spaces inside cast parens
+sp_inside_paren_cast                     = ignore   # ignore/add/remove/force
+
+# Add or remove space between the type and open paren in a C++ cast, i.e. 'int(exp)' vs 'int (exp)'
+sp_cpp_cast_paren                        = ignore   # ignore/add/remove/force
+
+# Add or remove space between 'sizeof' and '('
+sp_sizeof_paren                          = ignore   # ignore/add/remove/force
+
+# Add or remove space after the tag keyword (Pawn)
+sp_after_tag                             = ignore   # ignore/add/remove/force
+
+# Add or remove space inside enum '{' and '}'
+sp_inside_braces_enum                    = ignore   # ignore/add/remove/force
+
+# Add or remove space inside struct/union '{' and '}'
+sp_inside_braces_struct                  = ignore   # ignore/add/remove/force
+
+# Add or remove space inside '{' and '}'
+sp_inside_braces                         = ignore   # ignore/add/remove/force
+
+# Add or remove space inside '{}'
+sp_inside_braces_empty                   = ignore   # ignore/add/remove/force
+
+# Add or remove space between return type and function name
+# A minimum of 1 is forced except for pointer return types.
+sp_type_func                             = ignore   # ignore/add/remove/force
+
+# Add or remove space between function name and '(' on function declaration
+sp_func_proto_paren                      = ignore   # ignore/add/remove/force
+
+# Add or remove space between function name and '(' on function definition
+sp_func_def_paren                        = ignore   # ignore/add/remove/force
+
+# Add or remove space inside empty function '()'
+sp_inside_fparens                        = ignore   # ignore/add/remove/force
+
+# Add or remove space inside function '(' and ')'
+sp_inside_fparen                         = ignore   # ignore/add/remove/force
+
+# Add or remove space inside the first parens in the function type: 'void (*x)(...)'
+sp_inside_tparen                         = ignore   # ignore/add/remove/force
+
+# Add or remove between the parens in the function type: 'void (*x)(...)'
+sp_after_tparen_close                    = ignore   # ignore/add/remove/force
+
+# Add or remove space between ']' and '(' when part of a function call.
+sp_square_fparen                         = ignore   # ignore/add/remove/force
+
+# Add or remove space between ')' and '{' of function
+sp_fparen_brace                          = ignore   # ignore/add/remove/force
+
+# Add or remove space between function name and '(' on function calls
+sp_func_call_paren                       = ignore   # ignore/add/remove/force
+
+# Add or remove space between function name and '()' on function calls without parameters.
+# If set to 'ignore' (the default), sp_func_call_paren is used.
+sp_func_call_paren_empty                 = ignore   # ignore/add/remove/force
+
+# Add or remove space between the user function name and '(' on function calls
+# You need to set a keyword to be a user function, like this: 'set func_call_user _' in the config file.
+sp_func_call_user_paren                  = ignore   # ignore/add/remove/force
+
+# Add or remove space between a constructor/destructor and the open paren
+sp_func_class_paren                      = ignore   # ignore/add/remove/force
+
+# Add or remove space between 'return' and '('
+sp_return_paren                          = ignore   # ignore/add/remove/force
+
+# Add or remove space between '__attribute__' and '('
+sp_attribute_paren                       = ignore   # ignore/add/remove/force
+
+# Add or remove space between 'defined' and '(' in '#if defined (FOO)'
+sp_defined_paren                         = ignore   # ignore/add/remove/force
+
+# Add or remove space between 'throw' and '(' in 'throw (something)'
+sp_throw_paren                           = ignore   # ignore/add/remove/force
+
+# Add or remove space between 'throw' and anything other than '(' as in '@throw [...];'
+sp_after_throw                           = ignore   # ignore/add/remove/force
+
+# Add or remove space between 'catch' and '(' in 'catch (something) { }'
+# If set to ignore, sp_before_sparen is used.
+sp_catch_paren                           = ignore   # ignore/add/remove/force
+
+# Add or remove space between 'version' and '(' in 'version (something) { }' (D language)
+# If set to ignore, sp_before_sparen is used.
+sp_version_paren                         = ignore   # ignore/add/remove/force
+
+# Add or remove space between 'scope' and '(' in 'scope (something) { }' (D language)
+# If set to ignore, sp_before_sparen is used.
+sp_scope_paren                           = ignore   # ignore/add/remove/force
+
+# Add or remove space between macro and value
+sp_macro                                 = ignore   # ignore/add/remove/force
+
+# Add or remove space between macro function ')' and value
+sp_macro_func                            = ignore   # ignore/add/remove/force
+
+# Add or remove space between 'else' and '{' if on the same line
+sp_else_brace                            = ignore   # ignore/add/remove/force
+
+# Add or remove space between '}' and 'else' if on the same line
+sp_brace_else                            = ignore   # ignore/add/remove/force
+
+# Add or remove space between '}' and the name of a typedef on the same line
+sp_brace_typedef                         = ignore   # ignore/add/remove/force
+
+# Add or remove space between 'catch' and '{' if on the same line
+sp_catch_brace                           = ignore   # ignore/add/remove/force
+
+# Add or remove space between '}' and 'catch' if on the same line
+sp_brace_catch                           = ignore   # ignore/add/remove/force
+
+# Add or remove space between 'finally' and '{' if on the same line
+sp_finally_brace                         = ignore   # ignore/add/remove/force
+
+# Add or remove space between '}' and 'finally' if on the same line
+sp_brace_finally                         = ignore   # ignore/add/remove/force
+
+# Add or remove space between 'try' and '{' if on the same line
+sp_try_brace                             = ignore   # ignore/add/remove/force
+
+# Add or remove space between get/set and '{' if on the same line
+sp_getset_brace                          = ignore   # ignore/add/remove/force
+
+# Add or remove space before the '::' operator
+sp_before_dc                             = ignore   # ignore/add/remove/force
+
+# Add or remove space after the '::' operator
+sp_after_dc                              = ignore   # ignore/add/remove/force
+
+# Add or remove around the D named array initializer ':' operator
+sp_d_array_colon                         = ignore   # ignore/add/remove/force
+
+# Add or remove space after the '!' (not) operator. Default=Remove
+sp_not                                   = remove   # ignore/add/remove/force
+
+# Add or remove space after the '~' (invert) operator. Default=Remove
+sp_inv                                   = remove   # ignore/add/remove/force
+
+# Add or remove space after the '&' (address-of) operator. Default=Remove
+# This does not affect the spacing after a '&' that is part of a type.
+sp_addr                                  = remove   # ignore/add/remove/force
+
+# Add or remove space around the '.' or '->' operators. Default=Remove
+sp_member                                = remove   # ignore/add/remove/force
+
+# Add or remove space after the '*' (dereference) operator. Default=Remove
+# This does not affect the spacing after a '*' that is part of a type.
+sp_deref                                 = remove   # ignore/add/remove/force
+
+# Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'. Default=Remove
+sp_sign                                  = remove   # ignore/add/remove/force
+
+# Add or remove space before or after '++' and '--', as in '(--x)' or 'y++;'. Default=Remove
+sp_incdec                                = remove   # ignore/add/remove/force
+
+# Add or remove space before a backslash-newline at the end of a line. Default=Add
+sp_before_nl_cont                        = add      # ignore/add/remove/force
+
+# Add or remove space after the scope '+' or '-', as in '-(void) foo;' or '+(int) bar;'
+sp_after_oc_scope                        = ignore   # ignore/add/remove/force
+
+# Add or remove space after the colon in message specs
+# '-(int) f:(int) x;' vs '-(int) f: (int) x;'
+sp_after_oc_colon                        = ignore   # ignore/add/remove/force
+
+# Add or remove space before the colon in message specs
+# '-(int) f: (int) x;' vs '-(int) f : (int) x;'
+sp_before_oc_colon                       = ignore   # ignore/add/remove/force
+
+# Add or remove space after the colon in immutable dictionary expression
+# 'NSDictionary *test = @{@"foo" :@"bar"};'
+sp_after_oc_dict_colon                   = ignore   # ignore/add/remove/force
+
+# Add or remove space before the colon in immutable dictionary expression
+# 'NSDictionary *test = @{@"foo" :@"bar"};'
+sp_before_oc_dict_colon                  = ignore   # ignore/add/remove/force
+
+# Add or remove space after the colon in message specs
+# '[object setValue:1];' vs '[object setValue: 1];'
+sp_after_send_oc_colon                   = ignore   # ignore/add/remove/force
+
+# Add or remove space before the colon in message specs
+# '[object setValue:1];' vs '[object setValue :1];'
+sp_before_send_oc_colon                  = ignore   # ignore/add/remove/force
+
+# Add or remove space after the (type) in message specs
+# '-(int)f: (int) x;' vs '-(int)f: (int)x;'
+sp_after_oc_type                         = ignore   # ignore/add/remove/force
+
+# Add or remove space after the first (type) in message specs
+# '-(int) f:(int)x;' vs '-(int)f:(int)x;'
+sp_after_oc_return_type                  = ignore   # ignore/add/remove/force
+
+# Add or remove space between '@selector' and '('
+# '@selector(msgName)' vs '@selector (msgName)'
+# Also applies to @protocol() constructs
+sp_after_oc_at_sel                       = ignore   # ignore/add/remove/force
+
+# Add or remove space between '@selector(x)' and the following word
+# '@selector(foo) a:' vs '@selector(foo)a:'
+sp_after_oc_at_sel_parens                = ignore   # ignore/add/remove/force
+
+# Add or remove space inside '@selector' parens
+# '@selector(foo)' vs '@selector( foo )'
+# Also applies to @protocol() constructs
+sp_inside_oc_at_sel_parens               = ignore   # ignore/add/remove/force
+
+# Add or remove space before a block pointer caret
+# '^int (int arg){...}' vs. ' ^int (int arg){...}'
+sp_before_oc_block_caret                 = ignore   # ignore/add/remove/force
+
+# Add or remove space after a block pointer caret
+# '^int (int arg){...}' vs. '^ int (int arg){...}'
+sp_after_oc_block_caret                  = ignore   # ignore/add/remove/force
+
+# Add or remove space between the receiver and selector in a message.
+# '[receiver selector ...]'
+sp_after_oc_msg_receiver                 = ignore   # ignore/add/remove/force
+
+# Add or remove space after @property.
+sp_after_oc_property                     = ignore   # ignore/add/remove/force
+
+# Add or remove space around the ':' in 'b ? t : f'
+sp_cond_colon                            = ignore   # ignore/add/remove/force
+
+# Add or remove space around the '?' in 'b ? t : f'
+sp_cond_question                         = ignore   # ignore/add/remove/force
+
+# Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make sense here.
+sp_case_label                            = ignore   # ignore/add/remove/force
+
+# Control the space around the D '..' operator.
+sp_range                                 = ignore   # ignore/add/remove/force
+
+# Control the spacing after ':' in 'for (TYPE VAR : EXPR)' (Java)
+sp_after_for_colon                       = ignore   # ignore/add/remove/force
+
+# Control the spacing before ':' in 'for (TYPE VAR : EXPR)' (Java)
+sp_before_for_colon                      = ignore   # ignore/add/remove/force
+
+# Control the spacing in 'extern (C)' (D)
+sp_extern_paren                          = ignore   # ignore/add/remove/force
+
+# Control the space after the opening of a C++ comment '// A' vs '//A'
+sp_cmt_cpp_start                         = ignore   # ignore/add/remove/force
+
+# Controls the spaces between #else or #endif and a trailing comment
+sp_endif_cmt                             = ignore   # ignore/add/remove/force
+
+# Controls the spaces after 'new', 'delete', and 'delete[]'
+sp_after_new                             = ignore   # ignore/add/remove/force
+
+# Controls the spaces before a trailing or embedded comment
+sp_before_tr_emb_cmt                     = ignore   # ignore/add/remove/force
+
+# Number of spaces before a trailing or embedded comment
+sp_num_before_tr_emb_cmt                 = 0        # number
+
+# Control space between a Java annotation and the open paren.
+sp_annotation_paren                      = ignore   # ignore/add/remove/force
+
+#
+# Code alignment (not left column spaces/tabs)
+#
+
+# Whether to keep non-indenting tabs
+align_keep_tabs                          = false    # false/true
+
+# Whether to use tabs for aligning
+align_with_tabs                          = false    # false/true
+
+# Whether to bump out to the next tab when aligning
+align_on_tabstop                         = false    # false/true
+
+# Whether to left-align numbers
+align_number_left                        = false    # false/true
+
+# Align variable definitions in prototypes and functions
+align_func_params                        = false    # false/true
+
+# Align parameters in single-line functions that have the same name.
+# The function names must already be aligned with each other.
+align_same_func_call_params              = false    # false/true
+
+# The span for aligning variable definitions (0=don't align)
+align_var_def_span                       = 0        # number
+
+# How to align the star in variable definitions.
+#  0=Part of the type     'void *   foo;'
+#  1=Part of the variable 'void     *foo;'
+#  2=Dangling             'void    *foo;'
+align_var_def_star_style                 = 0        # number
+
+# How to align the '&' in variable definitions.
+#  0=Part of the type
+#  1=Part of the variable
+#  2=Dangling
+align_var_def_amp_style                  = 0        # number
+
+# The threshold for aligning variable definitions (0=no limit)
+align_var_def_thresh                     = 0        # number
+
+# The gap for aligning variable definitions
+align_var_def_gap                        = 0        # number
+
+# Whether to align the colon in struct bit fields
+align_var_def_colon                      = false    # false/true
+
+# Whether to align any attribute after the variable name
+align_var_def_attribute                  = false    # false/true
+
+# Whether to align inline struct/enum/union variable definitions
+align_var_def_inline                     = false    # false/true
+
+# The span for aligning on '=' in assignments (0=don't align)
+align_assign_span                        = 0        # number
+
+# The threshold for aligning on '=' in assignments (0=no limit)
+align_assign_thresh                      = 0        # number
+
+# The span for aligning on '=' in enums (0=don't align)
+align_enum_equ_span                      = 0        # number
+
+# The threshold for aligning on '=' in enums (0=no limit)
+align_enum_equ_thresh                    = 0        # number
+
+# The span for aligning struct/union (0=don't align)
+align_var_struct_span                    = 0        # number
+
+# The threshold for aligning struct/union member definitions (0=no limit)
+align_var_struct_thresh                  = 0        # number
+
+# The gap for aligning struct/union member definitions
+align_var_struct_gap                     = 0        # number
+
+# The span for aligning struct initializer values (0=don't align)
+align_struct_init_span                   = 0        # number
+
+# The minimum space between the type and the synonym of a typedef
+align_typedef_gap                        = 0        # number
+
+# The span for aligning single-line typedefs (0=don't align)
+align_typedef_span                       = 0        # number
+
+# How to align typedef'd functions with other typedefs
+# 0: Don't mix them at all
+# 1: align the open paren with the types
+# 2: align the function type name with the other type names
+align_typedef_func                       = 0        # number
+
+# Controls the positioning of the '*' in typedefs. Just try it.
+# 0: Align on typedef type, ignore '*'
+# 1: The '*' is part of type name: typedef int  *pint;
+# 2: The '*' is part of the type, but dangling: typedef int *pint;
+align_typedef_star_style                 = 0        # number
+
+# Controls the positioning of the '&' in typedefs. Just try it.
+# 0: Align on typedef type, ignore '&'
+# 1: The '&' is part of type name: typedef int  &pint;
+# 2: The '&' is part of the type, but dangling: typedef int &pint;
+align_typedef_amp_style                  = 0        # number
+
+# The span for aligning comments that end lines (0=don't align)
+align_right_cmt_span                     = 0        # number
+
+# If aligning comments, mix with comments after '}' and #endif with less than 3 spaces before the comment
+align_right_cmt_mix                      = false    # false/true
+
+# If a trailing comment is more than this number of columns away from the text it follows,
+# it will qualify for being aligned. This has to be > 0 to do anything.
+align_right_cmt_gap                      = 0        # number
+
+# Align trailing comment at or beyond column N; 'pulls in' comments as a bonus side effect (0=ignore)
+align_right_cmt_at_col                   = 0        # number
+
+# The span for aligning function prototypes (0=don't align)
+align_func_proto_span                    = 0        # number
+
+# Minimum gap between the return type and the function name.
+align_func_proto_gap                     = 0        # number
+
+# Align function protos on the 'operator' keyword instead of what follows
+align_on_operator                        = false    # false/true
+
+# Whether to mix aligning prototype and variable declarations.
+# If true, align_var_def_XXX options are used instead of align_func_proto_XXX options.
+align_mix_var_proto                      = false    # false/true
+
+# Align single-line functions with function prototypes, uses align_func_proto_span
+align_single_line_func                   = false    # false/true
+
+# Aligning the open brace of single-line functions.
+# Requires align_single_line_func=true, uses align_func_proto_span
+align_single_line_brace                  = false    # false/true
+
+# Gap for align_single_line_brace.
+align_single_line_brace_gap              = 0        # number
+
+# The span for aligning ObjC msg spec (0=don't align)
+align_oc_msg_spec_span                   = 0        # number
+
+# Whether to align macros wrapped with a backslash and a newline.
+# This will not work right if the macro contains a multi-line comment.
+align_nl_cont                            = false    # false/true
+
+# # Align macro functions and variables together
+align_pp_define_together                 = false    # false/true
+
+# The minimum space between label and value of a preprocessor define
+align_pp_define_gap                      = 0        # number
+
+# The span for aligning on '#define' bodies (0=don't align)
+align_pp_define_span                     = 0        # number
+
+# Align lines that start with '<<' with previous '<<'. Default=true
+align_left_shift                         = true     # false/true
+
+# Span for aligning parameters in an Obj-C message call on the ':' (0=don't align)
+align_oc_msg_colon_span                  = 0        # number
+
+# If true, always align with the first parameter, even if it is too short.
+align_oc_msg_colon_first                 = false    # false/true
+
+# Aligning parameters in an Obj-C '+' or '-' declaration on the ':'
+align_oc_decl_colon                      = false    # false/true
+
+#
+# Newline adding and removing options
+#
+
+# Whether to collapse empty blocks between '{' and '}'
+nl_collapse_empty_body                   = false    # false/true
+
+# Don't split one-line braced assignments - 'foo_t f = { 1, 2 };'
+nl_assign_leave_one_liners               = false    # false/true
+
+# Don't split one-line braced statements inside a class xx { } body
+nl_class_leave_one_liners                = false    # false/true
+
+# Don't split one-line enums: 'enum foo { BAR = 15 };'
+nl_enum_leave_one_liners                 = false    # false/true
+
+# Don't split one-line get or set functions
+nl_getset_leave_one_liners               = false    # false/true
+
+# Don't split one-line function definitions - 'int foo() { return 0; }'
+nl_func_leave_one_liners                 = false    # false/true
+
+# Don't split one-line if/else statements - 'if(a) b++;'
+nl_if_leave_one_liners                   = false    # false/true
+
+# Don't split one-line OC messages
+nl_oc_msg_leave_one_liner                = false    # false/true
+
+# Add or remove newlines at the start of the file
+nl_start_of_file                         = ignore   # ignore/add/remove/force
+
+# The number of newlines at the start of the file (only used if nl_start_of_file is 'add' or 'force'
+nl_start_of_file_min                     = 0        # number
+
+# Add or remove newline at the end of the file
+nl_end_of_file                           = ignore   # ignore/add/remove/force
+
+# The number of newlines at the end of the file (only used if nl_end_of_file is 'add' or 'force')
+nl_end_of_file_min                       = 0        # number
+
+# Add or remove newline between '=' and '{'
+nl_assign_brace                          = ignore   # ignore/add/remove/force
+
+# Add or remove newline between '=' and '[' (D only)
+nl_assign_square                         = ignore   # ignore/add/remove/force
+
+# Add or remove newline after '= [' (D only). Will also affect the newline before the ']'
+nl_after_square_assign                   = ignore   # ignore/add/remove/force
+
+# The number of blank lines after a block of variable definitions at the top of a function body
+# 0 = No change (default)
+nl_func_var_def_blk                      = 0        # number
+
+# The number of newlines before a block of typedefs
+# 0 = No change (default)
+nl_typedef_blk_start                     = 0        # number
+
+# The number of newlines after a block of typedefs
+# 0 = No change (default)
+nl_typedef_blk_end                       = 0        # number
+
+# The maximum consecutive newlines within a block of typedefs
+# 0 = No change (default)
+nl_typedef_blk_in                        = 0        # number
+
+# The number of newlines before a block of variable definitions not at the top of a function body
+# 0 = No change (default)
+nl_var_def_blk_start                     = 0        # number
+
+# The number of newlines after a block of variable definitions not at the top of a function body
+# 0 = No change (default)
+nl_var_def_blk_end                       = 0        # number
+
+# The maximum consecutive newlines within a block of variable definitions
+# 0 = No change (default)
+nl_var_def_blk_in                        = 0        # number
+
+# Add or remove newline between a function call's ')' and '{', as in:
+# list_for_each(item, &list) { }
+nl_fcall_brace                           = ignore   # ignore/add/remove/force
+
+# Add or remove newline between 'enum' and '{'
+nl_enum_brace                            = ignore   # ignore/add/remove/force
+
+# Add or remove newline between 'struct and '{'
+nl_struct_brace                          = ignore   # ignore/add/remove/force
+
+# Add or remove newline between 'union' and '{'
+nl_union_brace                           = ignore   # ignore/add/remove/force
+
+# Add or remove newline between 'if' and '{'
+nl_if_brace                              = ignore   # ignore/add/remove/force
+
+# Add or remove newline between '}' and 'else'
+nl_brace_else                            = ignore   # ignore/add/remove/force
+
+# Add or remove newline between 'else if' and '{'
+# If set to ignore, nl_if_brace is used instead
+nl_elseif_brace                          = ignore   # ignore/add/remove/force
+
+# Add or remove newline between 'else' and '{'
+nl_else_brace                            = ignore   # ignore/add/remove/force
+
+# Add or remove newline between 'else' and 'if'
+nl_else_if                               = ignore   # ignore/add/remove/force
+
+# Add or remove newline between '}' and 'finally'
+nl_brace_finally                         = ignore   # ignore/add/remove/force
+
+# Add or remove newline between 'finally' and '{'
+nl_finally_brace                         = ignore   # ignore/add/remove/force
+
+# Add or remove newline between 'try' and '{'
+nl_try_brace                             = ignore   # ignore/add/remove/force
+
+# Add or remove newline between get/set and '{'
+nl_getset_brace                          = ignore   # ignore/add/remove/force
+
+# Add or remove newline between 'for' and '{'
+nl_for_brace                             = ignore   # ignore/add/remove/force
+
+# Add or remove newline between 'catch' and '{'
+nl_catch_brace                           = ignore   # ignore/add/remove/force
+
+# Add or remove newline between '}' and 'catch'
+nl_brace_catch                           = ignore   # ignore/add/remove/force
+
+# Add or remove newline between 'while' and '{'
+nl_while_brace                           = ignore   # ignore/add/remove/force
+
+# Add or remove newline between 'scope (x)' and '{' (D)
+nl_scope_brace                           = ignore   # ignore/add/remove/force
+
+# Add or remove newline between 'unittest' and '{' (D)
+nl_unittest_brace                        = ignore   # ignore/add/remove/force
+
+# Add or remove newline between 'version (x)' and '{' (D)
+nl_version_brace                         = ignore   # ignore/add/remove/force
+
+# Add or remove newline between 'using' and '{'
+nl_using_brace                           = ignore   # ignore/add/remove/force
+
+# Add or remove newline between two open or close braces.
+# Due to general newline/brace handling, REMOVE may not work.
+nl_brace_brace                           = ignore   # ignore/add/remove/force
+
+# Add or remove newline between 'do' and '{'
+nl_do_brace                              = ignore   # ignore/add/remove/force
+
+# Add or remove newline between '}' and 'while' of 'do' statement
+nl_brace_while                           = ignore   # ignore/add/remove/force
+
+# Add or remove newline between 'switch' and '{'
+nl_switch_brace                          = ignore   # ignore/add/remove/force
+
+# Add a newline between ')' and '{' if the ')' is on a different line than the if/for/etc.
+# Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch, and nl_catch_brace.
+nl_multi_line_cond                       = false    # false/true
+
+# Force a newline in a define after the macro name for multi-line defines.
+nl_multi_line_define                     = false    # false/true
+
+# Whether to put a newline before 'case' statement
+nl_before_case                           = false    # false/true
+
+# Add or remove newline between ')' and 'throw'
+nl_before_throw                          = ignore   # ignore/add/remove/force
+
+# Whether to put a newline after 'case' statement
+nl_after_case                            = false    # false/true
+
+# Add or remove a newline between a case ':' and '{'. Overrides nl_after_case.
+nl_case_colon_brace                      = ignore   # ignore/add/remove/force
+
+# Newline between namespace and {
+nl_namespace_brace                       = ignore   # ignore/add/remove/force
+
+# Add or remove newline between 'template<>' and whatever follows.
+nl_template_class                        = ignore   # ignore/add/remove/force
+
+# Add or remove newline between 'class' and '{'
+nl_class_brace                           = ignore   # ignore/add/remove/force
+
+# Add or remove newline after each ',' in the constructor member initialization
+nl_class_init_args                       = ignore   # ignore/add/remove/force
+
+# Add or remove newline between return type and function name in a function definition
+nl_func_type_name                        = ignore   # ignore/add/remove/force
+
+# Add or remove newline between return type and function name inside a class {}
+# Uses nl_func_type_name or nl_func_proto_type_name if set to ignore.
+nl_func_type_name_class                  = ignore   # ignore/add/remove/force
+
+# Add or remove newline between function scope and name in a definition
+# Controls the newline after '::' in 'void A::f() { }'
+nl_func_scope_name                       = ignore   # ignore/add/remove/force
+
+# Add or remove newline between return type and function name in a prototype
+nl_func_proto_type_name                  = ignore   # ignore/add/remove/force
+
+# Add or remove newline between a function name and the opening '('
+nl_func_paren                            = ignore   # ignore/add/remove/force
+
+# Add or remove newline between a function name and the opening '(' in the definition
+nl_func_def_paren                        = ignore   # ignore/add/remove/force
+
+# Add or remove newline after '(' in a function declaration
+nl_func_decl_start                       = ignore   # ignore/add/remove/force
+
+# Add or remove newline after '(' in a function definition
+nl_func_def_start                        = ignore   # ignore/add/remove/force
+
+# Overrides nl_func_decl_start when there is only one parameter.
+nl_func_decl_start_single                = ignore   # ignore/add/remove/force
+
+# Overrides nl_func_def_start when there is only one parameter.
+nl_func_def_start_single                 = ignore   # ignore/add/remove/force
+
+# Add or remove newline after each ',' in a function declaration
+nl_func_decl_args                        = ignore   # ignore/add/remove/force
+
+# Add or remove newline after each ',' in a function definition
+nl_func_def_args                         = ignore   # ignore/add/remove/force
+
+# Add or remove newline before the ')' in a function declaration
+nl_func_decl_end                         = ignore   # ignore/add/remove/force
+
+# Add or remove newline before the ')' in a function definition
+nl_func_def_end                          = ignore   # ignore/add/remove/force
+
+# Overrides nl_func_decl_end when there is only one parameter.
+nl_func_decl_end_single                  = ignore   # ignore/add/remove/force
+
+# Overrides nl_func_def_end when there is only one parameter.
+nl_func_def_end_single                   = ignore   # ignore/add/remove/force
+
+# Add or remove newline between '()' in a function declaration.
+nl_func_decl_empty                       = ignore   # ignore/add/remove/force
+
+# Add or remove newline between '()' in a function definition.
+nl_func_def_empty                        = ignore   # ignore/add/remove/force
+
+# Whether to put each OC message parameter on a separate line
+# See nl_oc_msg_leave_one_liner
+nl_oc_msg_args                           = false    # false/true
+
+# Add or remove newline between function signature and '{'
+nl_fdef_brace                            = ignore   # ignore/add/remove/force
+
+# Add or remove a newline between the return keyword and return expression.
+nl_return_expr                           = ignore   # ignore/add/remove/force
+
+# Whether to put a newline after semicolons, except in 'for' statements
+nl_after_semicolon                       = false    # false/true
+
+# Whether to put a newline after brace open.
+# This also adds a newline before the matching brace close.
+nl_after_brace_open                      = false    # false/true
+
+# If nl_after_brace_open and nl_after_brace_open_cmt are true, a newline is
+# placed between the open brace and a trailing single-line comment.
+nl_after_brace_open_cmt                  = false    # false/true
+
+# Whether to put a newline after a virtual brace open with a non-empty body.
+# These occur in un-braced if/while/do/for statement bodies.
+nl_after_vbrace_open                     = false    # false/true
+
+# Whether to put a newline after a virtual brace open with an empty body.
+# These occur in un-braced if/while/do/for statement bodies.
+nl_after_vbrace_open_empty               = false    # false/true
+
+# Whether to put a newline after a brace close.
+# Does not apply if followed by a necessary ';'.
+nl_after_brace_close                     = false    # false/true
+
+# Whether to put a newline after a virtual brace close.
+# Would add a newline before return in: 'if (foo) a++; return;'
+nl_after_vbrace_close                    = false    # false/true
+
+# Control the newline between the close brace and 'b' in: 'struct { int a; } b;'
+# Affects enums, unions, and structures. If set to ignore, uses nl_after_brace_close
+nl_brace_struct_var                      = ignore   # ignore/add/remove/force
+
+# Whether to alter newlines in '#define' macros
+nl_define_macro                          = false    # false/true
+
+# Whether to not put blanks after '#ifxx', '#elxx', or before '#endif'
+nl_squeeze_ifdef                         = false    # false/true
+
+# Add or remove blank line before 'if'
+nl_before_if                             = ignore   # ignore/add/remove/force
+
+# Add or remove blank line after 'if' statement
+nl_after_if                              = ignore   # ignore/add/remove/force
+
+# Add or remove blank line before 'for'
+nl_before_for                            = ignore   # ignore/add/remove/force
+
+# Add or remove blank line after 'for' statement
+nl_after_for                             = ignore   # ignore/add/remove/force
+
+# Add or remove blank line before 'while'
+nl_before_while                          = ignore   # ignore/add/remove/force
+
+# Add or remove blank line after 'while' statement
+nl_after_while                           = ignore   # ignore/add/remove/force
+
+# Add or remove blank line before 'switch'
+nl_before_switch                         = ignore   # ignore/add/remove/force
+
+# Add or remove blank line after 'switch' statement
+nl_after_switch                          = ignore   # ignore/add/remove/force
+
+# Add or remove blank line before 'do'
+nl_before_do                             = ignore   # ignore/add/remove/force
+
+# Add or remove blank line after 'do/while' statement
+nl_after_do                              = ignore   # ignore/add/remove/force
+
+# Whether to double-space commented-entries in struct/enum
+nl_ds_struct_enum_cmt                    = false    # false/true
+
+# Whether to double-space before the close brace of a struct/union/enum
+# (lower priority than 'eat_blanks_before_close_brace')
+nl_ds_struct_enum_close_brace            = false    # false/true
+
+# Add or remove a newline around a class colon.
+# Related to pos_class_colon, nl_class_init_args, and pos_comma.
+nl_class_colon                           = ignore   # ignore/add/remove/force
+
+# Change simple unbraced if statements into a one-liner
+# 'if(b)\n i++;' => 'if(b) i++;'
+nl_create_if_one_liner                   = false    # false/true
+
+# Change simple unbraced for statements into a one-liner
+# 'for (i=0;i<5;i++)\n foo(i);' => 'for (i=0;i<5;i++) foo(i);'
+nl_create_for_one_liner                  = false    # false/true
+
+# Change simple unbraced while statements into a one-liner
+# 'while (i<5)\n foo(i++);' => 'while (i<5) foo(i++);'
+nl_create_while_one_liner                = false    # false/true
+
+#
+# Positioning options
+#
+
+# The position of arithmetic operators in wrapped expressions
+pos_arith                                = ignore   # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
+
+# The position of assignment in wrapped expressions.
+# Do not affect '=' followed by '{'
+pos_assign                               = ignore   # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
+
+# The position of boolean operators in wrapped expressions
+pos_bool                                 = ignore   # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
+
+# The position of comparison operators in wrapped expressions
+pos_compare                              = ignore   # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
+
+# The position of conditional (b ? t : f) operators in wrapped expressions
+pos_conditional                          = ignore   # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
+
+# The position of the comma in wrapped expressions
+pos_comma                                = ignore   # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
+
+# The position of the comma in the constructor initialization list
+pos_class_comma                          = ignore   # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
+
+# The position of colons between constructor and member initialization
+pos_class_colon                          = ignore   # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
+
+#
+# Line Splitting options
+#
+
+# Try to limit code width to N number of columns
+code_width                               = 0        # number
+
+# Whether to fully split long 'for' statements at semi-colons
+ls_for_split_full                        = false    # false/true
+
+# Whether to fully split long function protos/calls at commas
+ls_func_split_full                       = false    # false/true
+
+# Whether to split lines as close to code_width as possible and ignore some groupings
+ls_code_width                            = false    # false/true
+
+#
+# Blank line options
+#
+
+# The maximum consecutive newlines
+nl_max                                   = 0        # number
+
+# The number of newlines after a function prototype, if followed by another function prototype
+nl_after_func_proto                      = 0        # number
+
+# The number of newlines after a function prototype, if not followed by another function prototype
+nl_after_func_proto_group                = 0        # number
+
+# The number of newlines after '}' of a multi-line function body
+nl_after_func_body                       = 0        # number
+
+# The number of newlines after '}' of a multi-line function body in a class declaration
+nl_after_func_body_class                 = 0        # number
+
+# The number of newlines after '}' of a single line function body
+nl_after_func_body_one_liner             = 0        # number
+
+# The minimum number of newlines before a multi-line comment.
+# Doesn't apply if after a brace open or another multi-line comment.
+nl_before_block_comment                  = 0        # number
+
+# The minimum number of newlines before a single-line C comment.
+# Doesn't apply if after a brace open or other single-line C comments.
+nl_before_c_comment                      = 0        # number
+
+# The minimum number of newlines before a CPP comment.
+# Doesn't apply if after a brace open or other CPP comments.
+nl_before_cpp_comment                    = 0        # number
+
+# Whether to force a newline after a multi-line comment.
+nl_after_multiline_comment               = false    # false/true
+
+# The number of newlines after '}' or ';' of a struct/enum/union definition
+nl_after_struct                          = 0        # number
+
+# The number of newlines after '}' or ';' of a class definition
+nl_after_class                           = 0        # number
+
+# The number of newlines before a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label.
+# Will not change the newline count if after a brace open.
+# 0 = No change.
+nl_before_access_spec                    = 0        # number
+
+# The number of newlines after a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label.
+# 0 = No change.
+nl_after_access_spec                     = 0        # number
+
+# The number of newlines between a function def and the function comment.
+# 0 = No change.
+nl_comment_func_def                      = 0        # number
+
+# The number of newlines after a try-catch-finally block that isn't followed by a brace close.
+# 0 = No change.
+nl_after_try_catch_finally               = 0        # number
+
+# The number of newlines before and after a property, indexer or event decl.
+# 0 = No change.
+nl_around_cs_property                    = 0        # number
+
+# The number of newlines between the get/set/add/remove handlers in C#.
+# 0 = No change.
+nl_between_get_set                       = 0        # number
+
+# Add or remove newline between C# property and the '{'
+nl_property_brace                        = ignore   # ignore/add/remove/force
+
+# Whether to remove blank lines after '{'
+eat_blanks_after_open_brace              = false    # false/true
+
+# Whether to remove blank lines before '}'
+eat_blanks_before_close_brace            = false    # false/true
+
+# How aggressively to remove extra newlines not in preproc.
+# 0: No change
+# 1: Remove most newlines not handled by other config
+# 2: Remove all newlines and reformat completely by config
+nl_remove_extra_newlines                 = 0        # number
+
+# Whether to put a blank line before 'return' statements, unless after an open brace.
+nl_before_return                         = false    # false/true
+
+# Whether to put a blank line after 'return' statements, unless followed by a close brace.
+nl_after_return                          = false    # false/true
+
+# Whether to put a newline after a Java annotation statement.
+# Only affects annotations that are after a newline.
+nl_after_annotation                      = ignore   # ignore/add/remove/force
+
+# Controls the newline between two annotations.
+nl_between_annotation                    = ignore   # ignore/add/remove/force
+
+#
+# Code modifying options (non-whitespace)
+#
+
+# Add or remove braces on single-line 'do' statement
+mod_full_brace_do                        = ignore   # ignore/add/remove/force
+
+# Add or remove braces on single-line 'for' statement
+mod_full_brace_for                       = ignore   # ignore/add/remove/force
+
+# Add or remove braces on single-line function definitions. (Pawn)
+mod_full_brace_function                  = ignore   # ignore/add/remove/force
+
+# Add or remove braces on single-line 'if' statement. Will not remove the braces if they contain an 'else'.
+mod_full_brace_if                        = ignore   # ignore/add/remove/force
+
+# Make all if/elseif/else statements in a chain be braced or not. Overrides mod_full_brace_if.
+# If any must be braced, they are all braced.  If all can be unbraced, then the braces are removed.
+mod_full_brace_if_chain                  = false    # false/true
+
+# Don't remove braces around statements that span N newlines
+mod_full_brace_nl                        = 0        # number
+
+# Add or remove braces on single-line 'while' statement
+mod_full_brace_while                     = ignore   # ignore/add/remove/force
+
+# Add or remove braces on single-line 'using ()' statement
+mod_full_brace_using                     = ignore   # ignore/add/remove/force
+
+# Add or remove unnecessary paren on 'return' statement
+mod_paren_on_return                      = ignore   # ignore/add/remove/force
+
+# Whether to change optional semicolons to real semicolons
+mod_pawn_semicolon                       = false    # false/true
+
+# Add parens on 'while' and 'if' statement around bools
+mod_full_paren_if_bool                   = false    # false/true
+
+# Whether to remove superfluous semicolons
+mod_remove_extra_semicolon               = false    # false/true
+
+# If a function body exceeds the specified number of newlines and doesn't have a comment after
+# the close brace, a comment will be added.
+mod_add_long_function_closebrace_comment = 0        # number
+
+# If a switch body exceeds the specified number of newlines and doesn't have a comment after
+# the close brace, a comment will be added.
+mod_add_long_switch_closebrace_comment   = 0        # number
+
+# If an #ifdef body exceeds the specified number of newlines and doesn't have a comment after
+# the #endif, a comment will be added.
+mod_add_long_ifdef_endif_comment         = 0        # number
+
+# If an #ifdef or #else body exceeds the specified number of newlines and doesn't have a comment after
+# the #else, a comment will be added.
+mod_add_long_ifdef_else_comment          = 0        # number
+
+# If TRUE, will sort consecutive single-line 'import' statements [Java, D]
+mod_sort_import                          = false    # false/true
+
+# If TRUE, will sort consecutive single-line 'using' statements [C#]
+mod_sort_using                           = false    # false/true
+
+# If TRUE, will sort consecutive single-line '#include' statements [C/C++] and '#import' statements [Obj-C]
+# This is generally a bad idea, as it may break your code.
+mod_sort_include                         = false    # false/true
+
+# If TRUE, it will move a 'break' that appears after a fully braced 'case' before the close brace.
+mod_move_case_break                      = false    # false/true
+
+# Will add or remove the braces around a fully braced case statement.
+# Will only remove the braces if there are no variable declarations in the block.
+mod_case_brace                           = ignore   # ignore/add/remove/force
+
+# If TRUE, it will remove a void 'return;' that appears as the last statement in a function.
+mod_remove_empty_return                  = false    # false/true
+
+#
+# Comment modifications
+#
+
+# Try to wrap comments at cmt_width columns
+cmt_width                                = 0        # number
+
+# Set the comment reflow mode (default: 0)
+# 0: no reflowing (apart from the line wrapping due to cmt_width)
+# 1: no touching at all
+# 2: full reflow
+cmt_reflow_mode                          = 0        # number
+
+# If false, disable all multi-line comment changes, including cmt_width. keyword substitution, and leading chars.
+# Default is true.
+cmt_indent_multi                         = true     # false/true
+
+# Whether to group c-comments that look like they are in a block
+cmt_c_group                              = false    # false/true
+
+# Whether to put an empty '/*' on the first line of the combined c-comment
+cmt_c_nl_start                           = false    # false/true
+
+# Whether to put a newline before the closing '*/' of the combined c-comment
+cmt_c_nl_end                             = false    # false/true
+
+# Whether to group cpp-comments that look like they are in a block
+cmt_cpp_group                            = false    # false/true
+
+# Whether to put an empty '/*' on the first line of the combined cpp-comment
+cmt_cpp_nl_start                         = false    # false/true
+
+# Whether to put a newline before the closing '*/' of the combined cpp-comment
+cmt_cpp_nl_end                           = false    # false/true
+
+# Whether to change cpp-comments into c-comments
+cmt_cpp_to_c                             = false    # false/true
+
+# Whether to put a star on subsequent comment lines
+cmt_star_cont                            = false    # false/true
+
+# The number of spaces to insert at the start of subsequent comment lines
+cmt_sp_before_star_cont                  = 0        # number
+
+# The number of spaces to insert after the star on subsequent comment lines
+cmt_sp_after_star_cont                   = 0        # number
+
+# For multi-line comments with a '*' lead, remove leading spaces if the first and last lines of
+# the comment are the same length. Default=True
+cmt_multi_check_last                     = true     # false/true
+
+# The filename that contains text to insert at the head of a file if the file doesn't start with a C/C++ comment.
+# Will substitute $(filename) with the current file's name.
+cmt_insert_file_header                   = ""         # string
+
+# The filename that contains text to insert at the end of a file if the file doesn't end with a C/C++ comment.
+# Will substitute $(filename) with the current file's name.
+cmt_insert_file_footer                   = ""         # string
+
+# The filename that contains text to insert before a function implementation if the function isn't preceded with a C/C++ comment.
+# Will substitute $(function) with the function name and $(javaparam) with the javadoc @param and @return stuff.
+# Will also substitute $(fclass) with the class name: void CFoo::Bar() { ... }
+cmt_insert_func_header                   = ""         # string
+
+# The filename that contains text to insert before a class if the class isn't preceded with a C/C++ comment.
+# Will substitute $(class) with the class name.
+cmt_insert_class_header                  = ""         # string
+
+# The filename that contains text to insert before a Obj-C message specification if the method isn't preceeded with a C/C++ comment.
+# Will substitute $(message) with the function name and $(javaparam) with the javadoc @param and @return stuff.
+cmt_insert_oc_msg_header                 = ""         # string
+
+# If a preprocessor is encountered when stepping backwards from a function name, then
+# this option decides whether the comment should be inserted.
+# Affects cmt_insert_oc_msg_header, cmt_insert_func_header and cmt_insert_class_header.
+cmt_insert_before_preproc                = false    # false/true
+
+#
+# Preprocessor options
+#
+
+# Control indent of preprocessors inside #if blocks at brace level 0
+pp_indent                                = ignore   # ignore/add/remove/force
+
+# Whether to indent #if/#else/#endif at the brace level (true) or from column 1 (false)
+pp_indent_at_level                       = false    # false/true
+
+# If pp_indent_at_level=false, specifies the number of columns to indent per level. Default=1.
+pp_indent_count                          = 1        # number
+
+# Add or remove space after # based on pp_level of #if blocks
+pp_space                                 = ignore   # ignore/add/remove/force
+
+# Sets the number of spaces added with pp_space
+pp_space_count                           = 0        # number
+
+# The indent for #region and #endregion in C# and '#pragma region' in C/C++
+pp_indent_region                         = 0        # number
+
+# Whether to indent the code between #region and #endregion
+pp_region_indent_code                    = false    # false/true
+
+# If pp_indent_at_level=true, sets the indent for #if, #else, and #endif when not at file-level
+pp_indent_if                             = 0        # number
+
+# Control whether to indent the code between #if, #else and #endif when not at file-level
+pp_if_indent_code                        = false    # false/true
+
+# Whether to indent '#define' at the brace level (true) or from column 1 (false)
+pp_define_at_level                       = false    # false/true
+
+# You can force a token to be a type with the 'type' option.
+# Example:
+# type myfoo1 myfoo2
+#
+# You can create custom macro-based indentation using macro-open,
+# macro-else and macro-close.
+# Example:
+# macro-open  BEGIN_TEMPLATE_MESSAGE_MAP
+# macro-open  BEGIN_MESSAGE_MAP
+# macro-close END_MESSAGE_MAP
+#
+# You can assign any keyword to any type with the set option.
+# set func_call_user _ N_
+#
+# The full syntax description of all custom definition config entries
+# is shown below:
+#
+# define custom tokens as:
+# - embed whitespace in token using '' escape character, or
+#   put token in quotes
+# - these: ' " and ` are recognized as quote delimiters
+#
+# type token1 token2 token3 ...
+#             ^ optionally specify multiple tokens on a single line
+# define def_token output_token
+#                  ^ output_token is optional, then NULL is assumed
+# macro-open token
+# macro-close token
+# macro-else token
+# set id token1 token2 ...
+#               ^ optionally specify multiple tokens on a single line
+#     ^ id is one of the names in token_enum.h sans the CT_ prefix,
+#       e.g. PP_PRAGMA
+#
+# all tokens are separated by any mix of ',' commas, '=' equal signs
+# and whitespace (space, tab)
+#
diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
index 0000000..b24cf43
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,3 @@
+## New in 0.07.00
+
+* Initial Open-Source Release.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..f50e5f2
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,37 @@
+Contributing to wpantund
+========================
+
+Want to contribute? Great! First, read this page (including the small
+print at the end).
+
+### Before you contribute ###
+
+Before we can use your code, you must sign the [Google Individual
+Contributor License Agreement][CLA-INDI] (CLA), which you can do
+online. The CLA is necessary mainly because you own the copyright to
+your changes, even after your contribution becomes part of our
+codebase, so we need your permission to use and distribute your code.
+We also need to be sure of various other things—for instance that
+you'll tell us if you know that your code infringes on other people's
+patents. You don't have to sign the CLA until after you've submitted
+your code for review and a member has approved it, but you must do it
+before we can put your code into our codebase. Before you start
+working on a larger contribution, you should get in touch with us
+first through the issue tracker with your idea so that we can help out
+and possibly guide you. Coordinating up front makes it much easier to
+avoid frustration later on.
+
+[CLA-INDI]: https://cla.developers.google.com/about/google-individual
+
+### Code reviews ###
+
+All submissions, including submissions by project members, require
+review. We use Github pull requests for this purpose.
+
+### The small print ###
+
+Contributions made by corporations are covered by a different
+agreement than the one above, the [Software Grant and Corporate
+Contributor License Agreement][CLA-CORP].
+
+[CLA-CORP]: https://cla.developers.google.com/about/google-corporate
diff --git a/HACKING.md b/HACKING.md
new file mode 100644
index 0000000..520977a
--- /dev/null
+++ b/HACKING.md
@@ -0,0 +1,56 @@
+Hacking wpantund
+================
+
+## Prerequisite Knowledge ##
+
+The wpantund project makes heavy use of the following:
+
+* [Protothreads](http://dunkels.com/adam/pt/)
+* [`boost::any`](http://www.boost.org/doc/libs/1_57_0/doc/html/any.html)
+* [`boost::signals2`](http://www.boost.org/doc/libs/1_51_0/doc/html/signals2.html)
+
+Understanding what these are and how they work are critical to being able to
+successfully work on WPAN Tunnel Driver.
+
+
+## General Project Structure ##
+
+There are give types of code in this project:
+
+1.  Third-party code - Always contained in the `third_party`
+    directory.
+2.  Separable Utilities - There are stand-alone utilities that are
+    generally useful and may be easily used by other projects. These
+    are in the `src/utils` directory.
+3.  Fundamental Utilities - These are utilitity classes and functions
+    which are specific to WPAN Tunnel Driver, but used by both
+    application-specific and modem-specific classes. These can be
+    found in `src/utils` and `src/wpantund`.
+4.  Application Specific - There are classes which define the basic
+    structure of the application. These are found in `src/wpantund`.
+5.  Modem Specific - These are classes that are specific to individual
+    NCP implementations. These are found in specific subdirectories.
+    For example, `src/ncp-spinel`.
+
+There are three primary application-level virtual base classes in
+wpantund:
+
+ *  `NCPControlInterface`
+ *  `NCPInstance`
+ *  `IPCServer`
+
+The `NCPControlInterface` is the interface through which all
+operations on an NCP are preformed, with the exception of sending and
+receiving packets on the network. For example, if you want to form or
+join a network, you do so via the virtual methods of this class.
+
+The `NCPInstance` class represents a NCP. The methods of this class
+are use by WPAN Tunnel Driver for low-level configuration and setup of
+the NCP. The `NCPInstance` class offers the `NCPControlInterface`
+object which is used to interact with this specific NCP. Generally,
+NCP implementations use this class for setting up the `TUN` interface,
+framing/unframing data and management packets, etc.
+
+The `IPCServer` class exposes the methods from the
+`NCPControlInterface` class via an unspecified IPC mechanism (DBus is
+currently the only sublass implemented).
diff --git a/INSTALL.md b/INSTALL.md
new file mode 100644
index 0000000..915cbec
--- /dev/null
+++ b/INSTALL.md
@@ -0,0 +1,219 @@
+`wpantund` Installation Guide
+=============================
+
+This document describes the process of building and installing
+`wpantund` on both Ubuntu and OS X. Installation on other platforms
+may be possible, but are left as an excercise for the reader. This
+document assumes that you are at least casually familiar with
+[Autoconf][1]. It also assumes that you have already gotten a copy of
+the wpantund sources, extracted them, and are wondering what to do
+next.
+
+[1]: http://www.gnu.org/software/autoconf/autoconf.html
+
+
+
+Installing `wpantund` on Ubuntu
+-------------------------------
+
+### 1. Install Dependencies ###
+
+Open up a terminal and perform the following commands:
+
+    sudo apt-get update
+    # Install runtine-dependent packages (libreadline is optional)
+    sudo apt-get install dbus libreadline
+    # Install build-dependent packages (libreadline-dev is optional)
+    sudo apt-get install gcc g++ libdbus-1-dev libboost-dev libreadline-dev
+
+### 2. Configure and build the project ###
+
+If the `configure` script is not already present in the root directory
+of your `wpantund` sources (which it should be if you got these
+sources from a tarball), you will need to *bootstrap* the project by
+doing the following:
+
+    sudo apt-get install libtool autoconf autoconf-archive
+    ./bootstrap.sh
+
+If the `configure` script is already present, then you can just do the
+following:
+
+    ./configure --sysconfdir=/etc
+    make
+
+This may take a while. You can speed up the process by adding the
+argument `-j4` to the call to `make`, substituting the number `4` with
+the number of processor cores you have on your machine. This greatly
+improves the speed of builds.
+
+Also, if additional debugging information is required or helpful from
+`wpantund`, add the argument `--enable-debug` to the `./configure`
+line above.
+
+### 3. Install `wpantund` ###
+
+Once the build above is complete, execute the following command:
+
+    sudo make install
+
+This will install `wpantund` onto your computer.
+
+Installing `wpantund` on OS X
+-----------------------------
+
+Installing `wpantund` on OS X is largely similar to the process above,
+except things are complicated by the fact that we depend on DBus---and
+there is no native package manager for OS X. These instructions assume
+that you are using [Homebrew][2] as your package manager.
+
+[2]: http://brew.sh/
+
+What is nice about homebrew is that we have a recipe to build
+`wpantund` for you. This makes installing `wpantund` on OS X as easy
+as:
+
+    brew update
+    brew install ./etc/wpantund.rb
+    sudo cp "$(brew --repository)"/Cellar/d-bus/*/org.freedesktop.dbus-session.plist /Library/LaunchDaemons/
+    sudo launchctl load -w /Library/LaunchDaemons/org.freedesktop.dbus-session.plist
+
+(the last two commands are for setting DBus up to launch properly at
+startup)
+
+PRO-TIP: Use `brew install wpantund --HEAD` if you want the latest
+bleeding-edge version of wpantund!
+
+However, if you want to build `wpantund` manually, the procedure
+described below allows you to manually set up the `wpantund`
+dependencies so that the build can run without a hitch.
+
+### 1. Install Xcode ###
+
+Go [here][http://itunes.apple.com/us/app/xcode/id497799835?ls=1&mt=12]
+and install [Xcode](https://developer.apple.com/xcode/) if you haven't
+already.
+
+After you have installed Xcode, you will need to install the Xcode
+command-line tools. You can do this easily from the command line with
+the following command:
+
+    xcode-select --install
+
+### 2. Install Homebrew ###
+
+Homebrew is a package management system for OS X. While it is possible
+to install wpantund and its dependencies manually, using homebrew
+makes this process much easier. If you don't already have it installed
+on your mac, you can install it using the following instructions
+(taken from go/homebrew):
+
+    cd ~
+    mkdir homebrew && curl -L https://github.com/mxcl/homebrew/tarball/master | tar xz --strip 1 -C homebrew
+    mkdir ~/bin
+
+Create the file ~/.bash\_profile with the following contents (tweak to
+your preference if you know what you're doing):
+
+    # Global stuff
+    export PATH=$HOME/bin:$PATH
+    export EDITOR=vi
+
+    # Homebrew stuff
+    export PATH=$HOME/homebrew/bin:$PATH
+
+Then close and reopen your terminal window.
+
+### 2. Install and Setup `wpantund` dependencies ###
+
+We need a few dependencies in order to be able to build and use
+wpantund. The following commands will get us up and running:
+
+    brew install autoconf automake libtool
+    brew install pkg-config boost d-bus
+    sudo cp "$(brew --repository)"/Cellar/d-bus/*/org.freedesktop.dbus-session.plist /Library/LaunchDaemons/
+    sudo launchctl load -w /Library/LaunchDaemons/org.freedesktop.dbus-session.plist
+
+### 3. Configure and build ###
+
+At this point, you can jump over to step 2 from the section
+*Installing `wpantund` on Ubuntu*, above.
+
+
+
+
+Configuring and Using `wpantund`
+-------------------------------
+
+### 1. Configuring `wpantund` ###
+
+Now that you have `wpantund` installed, you will need to edit the
+configuration file to tell the daemon how to communicate with the NCP.
+You do this by editing the `wpantund.conf` file, which (if you
+followed the directions above) should now be at `/etc/wpantund.conf`.
+
+This file is, by default, filled only with comments---which describe
+all of the important configuration options that you might need to set
+in order to make wpantund usable. Read them over and then uncomment
+and update the appropriate configuration properties.
+
+Alternatively, you can specify any needed properties on the command
+line when invoking `wpantund`. At a minimum, at least `NCPSocketName`
+needs to be specified, which describes how `wpantund` is supposed to
+talk to the NCP.
+
+Refer to the authorative documentation in `/etc/wpantund.conf` or
+`./src/wpantund/wpantund.conf` for more information.
+
+### 2. Start wpantund ###
+
+To connect to an NCP on the serial port `/dev/ttyUSB0`, type the
+following into terminal:
+
+    sudo /usr/local/sbin/wpantund -o NCPSocketName /dev/ttyUSB0
+
+To start wpan on more than one interface, you can specify the WPAN
+interface name. For example, to set `wpan0` network interface on
+`/dev/ttyUSB0` USB interface, and `wpan1` on `/dev/ttyUSB1`, run:
+
+    sudo /usr/local/sbin/wpantund -o NCPSocketName /dev/ttyUSB0 -o WPANInterfaceName wpan0
+
+and
+
+    sudo /usr/local/sbin/wpantund -o NCPSocketName /dev/ttyUSB1 -o WPANInterfaceName wpan1
+
+Note that, unless you are running as root, you *must* use `sudo` when
+invoking `wpantund` directly.
+
+On an embedded device, you would add the appropriate scripts or
+configuration files that would cause `wpantund` to be started at boot.
+Doing so should be pretty straightforward.
+
+### 3. Using `wpanctl` ###
+
+Now that you have `wpantund` running, you can now issue commands to
+the daemon using `wpanctl` from another window: (Again, unless you are
+running as root, you *must* use `sudo`)
+
+    $ sudo /usr/local/bin/wpanctl
+    wpanctl:wpan0> leave
+    Leaving current WPAN. . .
+    wpanctl:wpan0> setprop NetworkKey --data 000102030405060708090a0b0c0d0e0f
+    wpanctl:wpan0> form "MyCoolNetwork" -c 26
+    Forming WPAN "MyCoolNetwork" as node type 2
+    Successfully formed!
+    wpanctl:wpan0> permit-join 3600 22
+    Permitting Joining on the current WPAN for 3600 seconds, commissioning traffic on TCP/UDP port 22. . .
+    wpanctl:wpan0> status
+    wpan0 => [
+        "AssociationState" => "joined"
+        "NetworkName" => "MyCoolNetwork"
+        "XPanId" => 0xD6D8A04025AB3B0C
+        "PanId" => 0xE3C3
+        "Channel" => 26
+        "AllowingJoin" => true
+        "Prefix" => [FDD6D8A040250000]
+        "NCPVersion" => "OpenThread/1.0d26-25-gb684c7f; DEBUG; May 9 2016 18:22:04"
+        "HWAddr" => [18B430000003F202]
+    ]
+    wpanctl:wpan0>
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..b3dcc67
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,70 @@
+#
+# Copyright (c) 2016 Nest Labs, Inc.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+AUTOMAKE_OPTIONS = foreign
+ACLOCAL_AMFLAGS = -I m4
+DIST_TARGETS = dist-gzip dist-bzip2
+DIST_ARCHIVES = $(distdir).tar.gz $(distdir).tar.bz2
+AM_DISTCHECK_CONFIGURE_FLAGS = --enable-all-restricted-plugins
+
+SUBDIRS =                              \
+	src                                \
+	doc                                \
+	third_party                        \
+	$(NULL)
+
+DISTCLEANFILES =                       \
+	config.log                         \
+	config.status                      \
+	Makefile                           \
+	libtool                            \
+	make.out                           \
+	$(distdir).tar.*                   \
+	$(NULL)
+
+EXTRA_DIST =                           \
+	.default-version                   \
+	.gitignore                         \
+	.uncrustify.cfg                    \
+	bootstrap.sh                       \
+	README.md                          \
+	TODO.md                            \
+	HACKING.md                         \
+	LICENSE                            \
+	CHANGELOG                          \
+	CONTRIBUTING.md                    \
+	INSTALL.md                         \
+	doxygen.cfg.in                     \
+	etc/Dockerfile                     \
+	etc/build-in-docker.sh             \
+	etc/run-in-docker.sh               \
+	etc/wpantund.rb                    \
+	m4/nl.m4                           \
+	$(NULL)
+
+
+
+HASH_VERSION = $(shell                                                   \
+	git describe --dirty --match __poison__ --always 2> /dev/null        \
+)
+
+EXTRA_VERSION = $(shell                                                  \
+	git describe --all --contains 54e116add1f093663e047f88dac18b4851589483 1> /dev/null 2> /dev/null \
+		&& (test `git describe | sed 's:^full/::'` = $(VERSION) || echo -g$(HASH_VERSION))  \
+)
+
+distdir = $(PACKAGE)-$(VERSION)$(EXTRA_VERSION)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3bc77a7
--- /dev/null
+++ b/README.md
@@ -0,0 +1,204 @@
+wpantund, Userspace WPAN Network Daemon
+=======================================
+
+`wpantund` is a user-space network interface driver/daemon that
+provides a native IPv6 network interface to a low-power wireless
+**Network Co-Processor** (or *NCP*). It was written and developed by
+Nest Labs to make supporting [Thread](http://threadgroup.org)
+connectivity on unix-like operating systems more straightforward.
+
+`wpantund` is designed to marshall all access to the NCP, ensuring
+that it always remains in a consistent, well-defined state.
+
+This is not an official Google product.
+
+## Feature and Architecture Summary ##
+
+`wpantund` provides:
+
+ *  ...a native IPv6 interface to an NCP.
+ *  ...a command line interface (`wpanctl`) for managing and
+    configuring the NCP.
+ *  ...a DBus API for managing and configuring the NCP.
+ *  ...a way to reliably manage the power state of the NCP.
+ *  ...a uniform mechanism for handling NCP firmware updates.
+
+The architecture and design of `wpantund` has been motivated by the
+following design goals (in no particular order):
+
+ *  Portability across unix-like operating systems (Currently supports
+    Linux and OS X. BSD support should be fairly trivial to add)
+ *  Require few runtime dependencies (DBus, with boost needed when
+    building)
+ *  Single-threaded architecture, with heavy use of asynchronous I/O
+ *  Power efficiency (0% CPU usage when idle)
+ *  Allow management interface to be used by multiple independent
+    applications simultaneously.
+ *  Allow multiple instances of `wpantund` to gracefully co-exist on a
+    single machine.
+ *  Modular, plugin-based architecture (All details for communicating
+    with a specific NCP stack are implemented as plugins)
+
+Currently, the following NCP plugins are included:
+
+*   `src/ncp-spinel`: Supports NCPs that communicate using the [Spinel NCP
+    Protocol][1], used by NCPs running [OpenThread][2].
+*   `src/ncp-dummy`: A dummy NCP plug-in implementation meant to be the
+    starting point for implementing new NCP plug-ins.
+
+[1]: ./third_party/openthread/src/ncp/PROTOCOL.md
+[2]: https://github.com/openthread/openthread/
+
+## License ##
+
+`wpantund` is open-source software, released under the [Apache License,
+Version 2.0][3]. See the file [`LICENSE`][4] for more information.
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the [License][4] for the specific language governing permissions and
+limitations under the License.
+
+[3]: http://www.apache.org/licenses/LICENSE-2.0
+[4]: ./LICENSE
+
+## Conceptual Overview ##
+
+`wpantund` is conceptually similar in purpose to the point-to-point
+daemon (`pppd`, commonly used on unix platforms to provide network
+connectivity via a dial-up modems)---except that instead of
+communicating with a dial-up modem, `wpantund` is communicating with
+an NCP.
+
+`wpantund` communicates with the NCP via an abstraction of a
+asynchronous stream socket, which could be any of the following:
+
+ *  A real serial port (UART) connected to the NCP (preferably with
+    hardware flow control)
+ *  The stdin and stdout from a subprocess (For supporting SPI
+    interfaces using a translator program, or debugging virtual
+    stacks)
+ *  A TCP socket (For debugging, not recommended for production)
+
+Unlike a dial-up modem, NCPs often have a rich management interface
+for performing operations like forming a network, joining a network,
+scanning for nearby networks, etc. To perform these operations,
+`wpantund` includes a command line utility called `wpanctl`.
+Applications that need to directly configure the network interface can
+also communicate directly with `wpantund` using its DBus API.
+
+To expose a native IPv6 network interface to the host operating
+system, `wpantund` uses the `tun` driver on Linux and the `utun`
+driver on OS X. On Linux, the default name for this interface is
+`wpan0`. On OS X, the default name is `utun0`.
+
+## Usage Overview ##
+
+The behavior of `wpantund` is determined by its configuration
+parameters, which may be specified in a configuration file (typically
+`/etc/wpantund.conf`) or at the command line. A typical configuration
+file might look like this: (For a more thorough explanation of
+available configuration parameters, see the [included example][5]).
+
+    # Try to name the network interface `wpan0`.
+    # If not possible, a different name will be used.
+    Config:TUN:InterfaceName      "wpan0"
+
+    # The pathname of the socket used to communicate
+    # with the NCP.
+    Config:NCP:SocketPath         "/dev/ttyUSB0"
+
+    # The name of the driver plugin to use. The chosen
+    # plugin must support the NCP you are trying to use.
+    Config:NCP:DriverName         "spinel"
+
+    # Drop root privileges after opening all sockets
+    Config:Daemon:PrivDropToUser  "nobody"
+
+    # Use a CCA Threshold of -70db
+    NCP:CCAThreshold              "-70"
+
+When up and running, `wpanctl` can be used to check the status of the
+interface and perform various management operations. For example, to
+check the general status of an interface:
+
+    $ sudo wpanctl status
+    wpan0 => [
+        "NCP:State" => "offline"
+        "Daemon:Enabled" => true
+        "NCP:Version" => "OPENTHREAD/g1651a47; May 23 2016 17:23:24"
+        "Daemon:Version" => "0.07.00 (May 23 2016 12:58:54)"
+        "Config:NCP:DriverName" => "spinel"
+        "NCP:HardwareAddress" => [F1D92A82C8D8FE43]
+    ]
+
+Here we see that the NCP is in the `offline` state, along with a few
+additional bits of information like the version of the NCP and it's
+hardware address. From here we can easily form a new network:
+
+    $ sudo wpanctl form "wpantund-testnet"
+    Forming WPAN "wpantund-testnet" as node type router
+    Successfully formed!
+    $
+
+And now if we check the status, we will see a lot more information:
+
+    $ sudo wpanctl status
+    wpan0 => [
+        "NCP:State" => "associated"
+        "Daemon:Enabled" => true
+        "NCP:Version" => "OPENTHREAD/g1651a47; May 23 2016 17:23:24"
+        "Daemon:Version" => "0.07.00 (May 23 2016 12:58:54)"
+        "Config:NCP:DriverName" => "spinel"
+        "NCP:HardwareAddress" => [F1D92A82C8D8FE43]
+        "NCP:Channel" => 23
+        "Network:NodeType" => "leader"
+        "Network:Name" => "wpantund-testnet"
+        "Network:XPANID" => 0x09717AEF221F66FB
+        "Network:PANID" => 0xBFCD
+        "IPv6:LinkLocalAddress" => "fe80::f3d9:2a82:c8d8:fe43"
+        "IPv6:MeshLocalAddress" => "fd09:717a:ef22::9a5d:5d1e:5527:5fc8"
+        "IPv6:MeshLocalPrefix" => "fd09:717a:ef22::/64"
+    ]
+    $ ifconfig wpan0
+    wpan0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1280
+        inet6 fe80::f3d9:2a82:c8d8:fe43%wpan0 prefixlen 10 scopeid 0x15
+        inet6 fd09:717a:ef22::9a5d:5d1e:5527:5fc8 prefixlen 64
+
+If compiled with `libreadline` or `libedit`, `wpanctl` supports an
+interactive console, which is very convenient. All commands support
+online help: simply type `help` to get a list of supported commands,
+or add `-h` to a command to get help with that specific command.
+
+See the wiki for more information: <https://github.com/openthread/wpantund/wiki>
+
+[5]: ./src/wpantund/wpantund.conf
+
+## Support ##
+
+Submit bugs and feature requests to [issue tracker][6]. We use the
+following mailing lists for discussion and announcements:
+
+ *  [wpantund-announce](https://groups.google.com/forum/#!forum/wpantund-announce)
+    \- Official Anouncements About `wpantund`
+ *  [wpantund-users](https://groups.google.com/forum/#!forum/wpantund-users)
+    \- `wpantund` User Discussion Group
+ *  [wpantund-devel](https://groups.google.com/forum/#!forum/wpantund-devel)
+    \- `wpantund` Developer Discussion Group
+
+[6]: https://github.com/openthread/wpantund/issues
+
+## Authors and Contributors ##
+
+The following people have significantly contributed to the design
+and development of `wpantund`:
+
+ *  Robert Quattlebaum
+ *  Marcin Szczodrak
+ *  Vaas Krishnamurthy
+ *  Arjuna Siva
+ *  Abtin Keshavarzian
+
+If you would like to contribute to this project, please read
+[CONTRIBUTING.md](CONTRIBUTING.md) first.
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000..8ce3b26
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,35 @@
+TODO LIST
+=========
+
+This document describes various tasks and features that are wanted or
+in-progress.
+
+General Cleanup/Refactoring Tasks
+---------------------------------
+
+### Finish DBusv1 API and move `wpanctl` to use it ###
+
+The new DBusv1 API is all but complete, but we still need to move a
+few methods in `wpanctl` over to use the new API. We also need to add
+the DBusv1 API equivalent for the `GetInterfaces` method, which is
+currently only in the V0 API.
+
+
+
+Security Tasks
+--------------
+
+Since WPAN Tunnel Driver communicates directly with the dangerous
+uncontrolled real-world, additional hardening is warranted. The
+following tasks are important for hardening `wpantund` from malicious foes:
+
+ *  Investigate ways to harden the process by Limiting kernel attackable
+    surface-area by using a syscall filter like [`libseccomp`](https://github.com/seccomp/libseccomp).
+ *  Full security audit of code paths which directly interpret data
+    from the NCP. Should be performed after the refactoring described
+    above.
+
+Wanted Features/Tasks
+---------------------
+
+ *  Provide more behavioral logic in the `NCPInstanceBase` class.
diff --git a/bootstrap.sh b/bootstrap.sh
new file mode 100755
index 0000000..64b0028
--- /dev/null
+++ b/bootstrap.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+#
+# Copyright (c) 2016 Nest Labs, Inc.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOGFILE=`mktemp -q /tmp/bootstrap.log.XXXXXX`
+
+die() {
+	echo " ***************************** "
+	cat "$LOGFILE"
+	echo ""
+	echo " *** $1 failed with error code $?"
+	exit 1
+}
+
+cd "`dirname "$0"`"
+
+which autoreconf 1>/dev/null 2>&1 || {
+	echo " *** error: The 'autoreconf' command was not found."
+	echo "Use the appropriate command for your platform to install the package:"
+	echo ""
+	echo "Homebrew(OS X) ....... brew install libtool autoconf autoconf-archive"
+	echo "Debian/Ubuntu ........ apt-get install libtool autoconf autoconf-archive"
+	exit 1
+}
+
+AUTOMAKE="automake --foreign" autoreconf --verbose --force --install 2>"$LOGFILE" || die autoreconf
+
+grep -q AX_CHECK_ configure && {
+	echo " *** error: The 'autoconf-archive' package is not installed."
+	echo "Use the appropriate command for your platform to install the package:"
+	echo ""
+	echo "Homebrew(OS X) ....... brew install autoconf-archive"
+	echo "Debian/Ubuntu ........ apt-get install autoconf-archive"
+	exit 1
+}
+
+echo
+echo Success. Logs in '"'$LOGFILE'"'
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..e5044fe
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,323 @@
+dnl #
+dnl # Copyright (c) 2016 Nest Labs, Inc.
+dnl # All rights reserved.
+dnl #
+dnl # Licensed under the Apache License, Version 2.0 (the "License");
+dnl # you may not use this file except in compliance with the License.
+dnl # You may obtain a copy of the License at
+dnl #
+dnl #    http://www.apache.org/licenses/LICENSE-2.0
+dnl #
+dnl # Unless required by applicable law or agreed to in writing, software
+dnl # distributed under the License is distributed on an "AS IS" BASIS,
+dnl # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl # See the License for the specific language governing permissions and
+dnl # limitations under the License.
+dnl #
+
+AC_PREREQ(2.59)
+
+dnl This is the project name and release verison number.
+dnl The release version is stored in the file `.default-version`.
+dnl Update that file *after* every release to indicate the version
+dnl of the *next* anticipated release.
+AC_INIT(
+	[wpantund],
+	m4_esyscmd([printf "%s" `cat .default-version`]),
+	[wpantund-devel@googlegroups.com],
+	[wpantund],
+	[https://github.com/openthread/wpantund/]dnl
+)
+
+dnl These are the list of plugins that have usage restrictions
+dnl associated with their license. These will be automatically
+dnl disabled by default and print out a warning when enabled.
+m4_define(RESTRICTED_USE_NCP_PLUGINS, [  dnl
+])
+
+dnl This is the list of plugins which are included with wpantund.
+dnl As plugins are added, they should be appended to this list.
+m4_define(AVAILABLE_NCP_PLUGINS, [       dnl
+	RESTRICTED_USE_NCP_PLUGINS           dnl
+	spinel                               dnl
+	dummy                                dnl
+])
+
+dnl These are the list of plugins are disabled by default.
+dnl This list automatically includes the plugins from
+dnl RESTRICTED_USE_NCP_PLUGINS.
+m4_define(DISABLED_NCP_PLUGINS, [RESTRICTED_USE_NCP_PLUGINS])
+
+dnl Attempt to include a reasonable git tag description.
+SOURCE_VERSION=m4_esyscmd([
+	configver="$PACKAGE_VERSION"
+	(test -d .git && gitver=`git describe --dirty --match "[0-9].*"` && printf "$gitver") ||
+	(test -d .git && gitver=`git describe --always` && printf "$configver-g$gitver") ||
+	printf "$configver"
+])
+
+AC_CONFIG_AUX_DIR([m4])
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_FILES(Makefile doxygen.cfg src/Makefile doc/Makefile src/connman-plugin/Makefile src/ipc-dbus/Makefile src/wpanctl/Makefile src/wpantund/Makefile src/util/Makefile third_party/Makefile)
+
+# Set up AC_CONFIG_FILES for all of the available plugins.
+AC_CONFIG_FILES(m4_normalize(m4_foreach_w(THIS_PLUGIN,AVAILABLE_NCP_PLUGINS,[src/ncp-[]THIS_PLUGIN/Makefile ])))
+
+
+PLUGIN_SUBDIRS="[]m4_foreach_w(THIS_PLUGIN,AVAILABLE_NCP_PLUGINS,ncp-[]THIS_PLUGIN )"
+AC_SUBST(PLUGIN_SUBDIRS)
+
+AM_INIT_AUTOMAKE()
+
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+m4_ifdef([AX_CODE_COVERAGE], [AX_CODE_COVERAGE], [AC_SUBST([CODE_COVERAGE_RULES])])
+
+LT_INIT
+
+AC_LIBTOOL_DLOPEN_SELF
+
+AC_PROG_LIBTOOL
+
+AC_CONFIG_SRCDIR([src/version.h])
+AC_CONFIG_HEADERS([src/config.h])
+
+# This varable is for defining the default NCP plugin.
+# This is the default value, which can be overridden below.
+default_ncp_plugin=none
+
+dnl Add --enable/--disable configure arguments for all the available plugins.
+m4_foreach_w(THIS_PLUGIN,AVAILABLE_NCP_PLUGINS,[
+	m4_define([THIS_PLUGIN_DISABLED],m4_foreach_w(THAT_PLUGIN,DISABLED_NCP_PLUGINS,[m4_if(THIS_PLUGIN,THAT_PLUGIN,[disabled])]))
+
+	AC_ARG_ENABLE(
+		ncp-[]m4_translit(THIS_PLUGIN,[_],[-]),
+		AC_HELP_STRING(
+			[--[]m4_if(THIS_PLUGIN_DISABLED,[disabled],[enable],[disable])-ncp-[]m4_translit(THIS_PLUGIN,[_],[-])],
+			m4_do([[]m4_if(THIS_PLUGIN_DISABLED,[disabled],[enable],[disable]) the NCP plugin "],[THIS_PLUGIN],["])
+		),
+		[
+			if test "x$enableval" = "xdefault"
+			then
+				default_ncp_plugin=[]THIS_PLUGIN
+				enable_ncp_[]THIS_PLUGIN=yes
+			fi
+		],
+		[m4_if(THIS_PLUGIN_DISABLED,[disabled],[true],[enable_ncp_[]THIS_PLUGIN=yes])],
+	)
+])
+
+AC_ARG_ENABLE(
+	all-restricted-plugins,
+	AC_HELP_STRING(
+		[--enable-all-restricted-plugins],
+		[Enable all plugins with restrictive licenses]
+	)
+)
+
+AC_ARG_ENABLE(
+	static-link-ncp-plugin,
+	AC_HELP_STRING(
+		[--enable-static-link-ncp-plugin=[plugin]],
+		[Statically link against a single NCP plugin]
+	),
+	[],
+	[
+		if test "x$enable_shared" == "xno"
+		then enable_static_link_ncp_plugin=yes
+		fi
+	]
+)
+
+if test "x$enable_all_restricted_plugins" = "xyes"
+then {
+true
+m4_foreach_w(THIS_PLUGIN,RESTRICTED_USE_NCP_PLUGINS,
+[	if test "x$enable_ncp_[]m4_translit(THIS_PLUGIN,[-],[_])" = "x"
+	then enable_ncp_[]m4_translit(THIS_PLUGIN,[-],[_])="yes"
+	fi
+])}
+fi
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+test "x$pkglibexecdir" = x && pkglibexecdir='${libexecdir}/${PACKAGE_TARNAME}'
+
+AC_DEFINE_UNQUOTED([PREFIX], ["`eval echo "$prefix"`"], [Define to the install prefix])
+AC_DEFINE_UNQUOTED([SYSCONFDIR], ["`eval echo "$sysconfdir"`"], [Define to the sub-directory for system settings.])
+AC_DEFINE_UNQUOTED([PKGLIBEXECDIR], ["`eval eval eval echo "$pkglibexecdir"`"], [Define to the sub-directory for plugins.])
+
+AC_DEFINE_UNQUOTED([SOURCE_VERSION], ["`eval echo "$SOURCE_VERSION"`"], [Source version])
+
+AC_PROG_CC([],[AC_MSG_ERROR(["A working C compiler is required"])])
+AC_PROG_CXX([],[AC_MSG_ERROR(["A working C++ compiler is required"])])
+
+AC_HEADER_STDC
+AC_HEADER_TIME
+
+AC_CHECK_HEADERS([unistd.h errno.h stdbool.h], [], [AC_MSG_ERROR(["Missing a required header."])])
+
+AC_CHECK_HEADERS([sys/un.h sys/wait.h pty.h pwd.h execinfo.h asm/sigcontext.h])
+
+AC_C_CONST
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+
+AC_C_INLINE
+AC_C_VOLATILE
+
+dnl Raspberry Pi's clock_gettime() function is in librt.
+dnl This should only pick it up if necessary.
+AC_SEARCH_LIBS([clock_gettime], [rt])
+AC_CHECK_FUNCS([clock_gettime])
+
+AC_CHECK_FUNCS([alloca fgetln memcmp memset strtol strdup strndup strlcpy strlcat stpncpy vsnprintf vsprintf snprintf getdtablesize])
+
+NL_DEBUG
+NL_CHECK_DBUS
+NL_CHECK_BOOST_SIGNALS2([],[AC_MSG_ERROR(Unable to find a usable implementation of boost::signals2. You probabbly need to install Boost.)])
+NL_CHECK_READLINE
+NL_CHECK_PTS
+
+AC_DEFINE_UNQUOTED([__STDC_LIMIT_MACROS], [1], [Needed by C++])
+AC_DEFINE_UNQUOTED([__STDC_CONSTANT_MACROS], [1], [Needed by C++])
+
+AC_DEFINE_UNQUOTED([ASSERT_MACROS_USE_SYSLOG], 1, [Define to 1 to have assertmacros.h use syslog])
+AC_DEFINE_UNQUOTED([ASSERT_MACROS_SQUELCH], 0, [Define to 0 to explicitly prevent squelching assert printouts])
+
+NL_CHECK_CONNMAN([
+	NL_CHECK_GLIB(
+		[],
+		[AC_MSG_ERROR(["Found ConnMan headers, but couldn't find GLIB"])]
+	)
+])
+AM_CONDITIONAL([BUILD_CONNMAN_PLUGIN],[test "x${with_connman}" = "xyes"])
+
+building_ncp_plugins=""
+
+m4_foreach_w(THIS_PLUGIN,AVAILABLE_NCP_PLUGINS,[
+	if test "x${enable_ncp_[]m4_translit(THIS_PLUGIN,[-],[_])}" = "xyes"
+	then building_ncp_plugins=`echo ${building_ncp_plugins} []THIS_PLUGIN`
+	fi
+])
+
+if test "x$building_ncp_plugins" != "x"
+then
+	if test "x$default_ncp_plugin" = "xnone"
+	then default_ncp_plugin=$(IFS=" " ; set -- $building_ncp_plugins ; echo $1)
+	fi
+fi
+
+if test "x${enable_static_link_ncp_plugin-no}" != "xno"
+then {
+	if test "x${enable_static_link_ncp_plugin-no}" != "xyes"
+	then {
+		# TODO: Extract statically linked plugin here, make sure it is enabled.
+		default_ncp_plugin=${enable_static_link_ncp_plugin}
+		enable_static_link_ncp_plugin=yes
+	} fi
+
+	if test "x${default_ncp_plugin-none}" = "xnone"
+	then AC_MSG_ERROR(["You must enable one NCP plugin when statically linking"])
+	fi
+
+	building_ncp_plugins=${default_ncp_plugin}
+
+	m4_foreach_w(THIS_PLUGIN,AVAILABLE_NCP_PLUGINS,[
+		if test "[]THIS_PLUGIN" != "${default_ncp_plugin}"
+		then enable_ncp_[]m4_translit(THIS_PLUGIN,[-],[_])=no
+		fi
+	])
+
+	AC_DEFINE_UNQUOTED([WPANTUND_PLUGIN_STATICLY_LINKED], [1], [Set to 1 if we are statically linking the plugin/])
+}
+else {
+	NL_CHECK_LIBDL
+	NL_EXPORT_DYNAMIC
+}
+fi
+
+AC_DEFINE_UNQUOTED([WPANTUND_DEFAULT_NCP_PLUGIN], ["${default_ncp_plugin}"], [Set to the name of the default NCP plugin])
+
+AM_CONDITIONAL(STATIC_LINK_NCP_PLUGIN,[test "x${enable_static_link_ncp_plugin}" = "xyes"])
+
+building_ncp_plugins=""
+
+
+m4_foreach_w(THIS_PLUGIN,AVAILABLE_NCP_PLUGINS,[
+	if test "x${enable_ncp_[]m4_translit(THIS_PLUGIN,[-],[_])}" = "xyes"
+	then building_ncp_plugins=`echo ${building_ncp_plugins} []THIS_PLUGIN`
+	fi
+
+	m4_ifdef([m4_do(WPANTUND_PLUGIN_NCP_[]m4_toupper(m4_translit(THIS_PLUGIN,[-],[_]))],
+		[WPANTUND_PLUGIN_NCP_[]m4_toupper(m4_translit(THIS_PLUGIN,[-],[_])]
+	)
+
+	AM_CONDITIONAL(BUILD_PLUGIN_NCP_[]m4_toupper(m4_translit(THIS_PLUGIN,[-],[_])),[test "x${enable_ncp_[]m4_translit(THIS_PLUGIN,[-],[_])}" = "xyes"])
+])
+
+m4_foreach_w(THIS_PLUGIN,RESTRICTED_USE_NCP_PLUGINS,[
+	if test "x${enable_ncp_[]m4_translit(THIS_PLUGIN,[-],[_])}" = "xyes"
+	then restricted_use_ncp_plugins=`echo ${restricted_use_ncp_plugins} []THIS_PLUGIN`
+	fi
+])
+
+AC_SUBST(default_ncp_plugin)
+
+AC_OUTPUT
+
+echo ""
+echo "= Summary ====================================================================="
+echo ""
+echo "ConnMan Plugin ................. ${with_connman-no}"
+echo "Using libreadline .............. ${with_readline-???}"
+echo "NCP plugins to build ........... ${building_ncp_plugins-none}"
+echo "Default NCP Plugin ............. ${default_ncp_plugin}"
+echo "Static link NCP plugin ......... ${enable_static_link_ncp_plugin-no}"
+echo ""
+
+if test "x${with_readline}" != "xyes"
+then {
+	echo "warning: libreadline was disabled or not found, interactive"
+	echo "         wpanctl command line interface will not be enabled!"
+	echo ""
+}
+fi
+
+if test "x$restricted_use_ncp_plugins" != "x"
+then {
+	echo "ATTENTION: Some of the plugins you are building have proprietary licenses which"
+	echo "           are more restrictive than the Apache 2.0 license that covers wpantund"
+	echo "           in general. If you are reading this message, it means you have manually"
+	echo "           enabled at least one plug-in that has a proprietary license. Please"
+	echo "           make sure that you understand the usage restrictions before using"
+	echo "           wpantund with these plugins: "
+	echo ""
+	echo "           $restricted_use_ncp_plugins"
+	echo ""
+}
+fi
+
+if test "x$building_ncp_plugins" = "x"
+then {
+	echo " *** WARNING: The current configuration is NOT BUILDING ANY PLUGINS! wpantund won't"
+	echo "              be very useful in this state. Note that plugins with proprietary"
+	echo "              licenses are no longer built by default: you must either enable them"
+	echo '              individually (using `--enable-ncp-*` configuration options) or by using'
+	echo '              the `--enable-all-restricted-plugins` configuration option.'
+	echo "              In any case, you can still make and install wpantund without plugins"
+	echo "              if you really know what you are doing."
+	echo ""
+}
+fi
+
+if test "x$building_ncp_plugins" = "xdummy"
+then {
+	echo " *** WARNING: The current configuration is only building the dummy plugin! wpantund"
+	echo "              won't be very useful in this state. Note that plugins with proprietary"
+	echo "              licenses are no longer built by default: you must either enable them"
+	echo '              individually (using `--enable-ncp-*` configuration options) or by using'
+	echo '              the `--enable-all-restricted-plugins` configuration option.'
+	echo ""
+}
+fi
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644
index 0000000..d8d8258
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,29 @@
+#
+# Copyright (c) 2016 Nest Labs, Inc.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+man_MANS =                   \
+	wpantund.1               \
+	wpanctl.1                \
+	$(NULL)
+
+EXTRA_DIST =                 \
+	wpantund-oss-plan.md     \
+	wpan-dbus-protocol.md    \
+	wpantund.1               \
+	wpanctl.1                \
+	$(NULL)
diff --git a/doc/wpan-dbus-protocol.md b/doc/wpan-dbus-protocol.md
new file mode 100644
index 0000000..13bf867
--- /dev/null
+++ b/doc/wpan-dbus-protocol.md
@@ -0,0 +1,136 @@
+wpantund DBus Protocol v1
+=========================
+
+This document outlines the DBus protocol used to communicate
+with `wpantund` to manage a low-power wireless network interface.
+
+It is a work in progress.
+
+daemon id: `org.wpantund`
+
+# Interface: `org.wpantund`
+
+## Path `/org/wpantund/`
+
+### Command: `GetInterfaces`
+### Command: `GetVersion`
+### Signal: `InterfaceAdded`
+### Signal: `InterfaceRemoved`
+
+## Path `/org/wpantund/<iface-name>`
+
+### Signal: `NetScanBeacon`
+### Signal: `NetScanComplete`
+
+### Command: `NetScanStart`
+### Command: `NetScanStop`
+
+### Command: `Status`
+### Command: `Reset`
+
+### Command: `BeginLowPower`
+### Command: `HostDidWake`
+
+### Command: `Attach`
+### Command: `Join`
+### Command: `Form`
+### Command: `Leave`
+
+### Command: `ConfigGateway`
+### Command: `DataPoll`
+
+## Path `/org/wpantund/<iface-name>/Properties/<property-name>`
+
+### Signal: "Changed"
+### Command: "Set"
+### Command: "Get"
+
+# Interface: `com.nestlabs.wpantund.internal`
+
+## Path `/org/wpantund/<iface-name>`
+
+### Command: `PermitJoin`
+### Command: `BeginNetWake`
+
+
+
+-------------------------------------------------------------------------------
+
+# Configuration Properties
+
+## `Config:NCP:SocketPath`
+## `Config:NCP:SocketBaud`
+## `Config:NCP:DriverName`
+## `Config:NCP:HardResetPath`
+## `Config:NCP:PowerPath`
+## `Config:NCP:ReliabilityLayer`
+## `Config:NCP:FirmwareCheckCommand`
+## `Config:NCP:FirmwareUpgradeCommand`
+## `Config:TUN:InterfaceName`
+## `Config:Daemon:PIDFile`
+## `Config:Daemon:PrivDropToUser`
+## `Config:Daemon:Chroot`
+
+## `Daemon:Version`
+## `Daemon:Enabled`
+## `Daemon:SyslogMask`
+## `Daemon:TerminateOnFault`
+
+
+## `Daemon:ReadyForHostSleep`
+Read only. Set to `true` if `wpantund` is busy. Set to `false` if it is idle.
+Primarily used for determining if it is a good opportunity to attempt to
+put the host into a low-power state.
+
+## `Daemon:AutoAssociateAfterReset` (Was AutoResume)
+If the NCP persistently stores network association and the NCP
+comes up in the `offline:commissioned` state, this option being set
+will cause `wpantund` to automatiaclly attempt to associate.
+
+## `Daemon:AutoFirmwareUpdate`
+## `Daemon:AutoDeepSleep`
+
+## `NCP:Version`
+## `NCP:State`
+## `NCP:HardwareAddress`
+## `NCP:Channel`
+## `NCP:TXPower`
+## `NCP:TXPowerLimit`
+## `NCP:CCAThreshold`
+## `NCP:DefaultChannelMask`
+## `NCP:SleepyPollInterval`
+
+## `Network:Name`
+## `Network:XPANID`
+## `Network:PANID`
+## `Network:NodeType`
+## `Network:Key`
+## `Network:KeyIndex`
+## `Network:IsAssociated`
+
+## `IPv6:LinkLocalAddress`
+## `IPv6:MeshLocalAddress`
+## `IPv6:AllAddresses`
+
+
+
+
+
+
+
+
+
+# NCP States
+
+## "uninitialized"                  (same)
+## "uninitialized:fault"            (same)
+## "uninitialized:upgrading"        (same)
+## "offline:deep-sleep"             (was "deep-sleep")
+## "offline"                        (was "disconnected")
+## "offline:commissioned"           (was "saved")
+## "associating"                    (was "joining")
+## "associating:credentials-needed" (was "credentials-needed")
+## "associated"                     (was "joined")
+## "associated:no-parent"           (was "joined-no-parent")
+## "associated:netwake-sleeping"    (was "joined-lurking")
+## "associated:netwake-waking"      (was "net-wake")
diff --git a/doc/wpanctl.1 b/doc/wpanctl.1
new file mode 100644
index 0000000..ac4cea2
--- /dev/null
+++ b/doc/wpanctl.1
@@ -0,0 +1,377 @@
+.TH wpanctl 1 "" "" "USER COMMANDS"
+
+.SH NAME
+wpanctl \- command line interfase for wpantund
+
+.SH SYNOPSIS
+.B wpanctl
+[\fIOPTIONS\fR] <\fICOMMANDS\fR> [args]
+
+.SH DESCRIPTION
+.B wpanctl
+is itself a command-line interface. You can specify an individual single command
+directly on the \fBwpanctl\fR command line, or run \fBwpanctl\fR without any arguments
+to enter the command-line interface shell.
+
+.SH OPTIONS
+
+.TP
+\fB\-h\fP, \fB\-\-help\fp
+Print help.
+
+.TP
+\fB\-v\fP, \fB\-\-version\fp
+Print version.
+
+.TP
+\fB\-f\fP \fIFILE
+Read commands from a file.
+
+.TP
+\fB\-I\fP, \fB\-\-interface\fp \fIINTERFACE\fR
+Set interface to use (e.g. wpan0).
+
+.TP
+\fB\-i\fP, \fB\-\-ignore-mismatch\fP
+Ignore driver version mismatch.
+
+.SH COMMANDS
+
+.TP
+\fBbegin-low-power\fR [\fBargs\fR]
+Enter low-power mode.
+
+  \fB\-h\fP, \fB\-\-help\fR
+  Print join help.
+
+  \fB\-t\fP, \fB\-\-timeout\fR [\fBTIMEOUT\fR]
+  Set timeout period.
+
+.TP
+\fBbegin-net-wake\fR [\fBargs\fR]
+Initiate a network wakeup.
+
+  \fB\-h\fP, \fB\-\-help\fR
+  Print join help.
+
+  \fB\-t\fP, \fB\-\-timeout\fR [\fBTIMEOUT\fR]
+  Set timeout period.
+
+.TP
+\fBcd\fR
+Change current interface (command mode).
+
+.TP
+\fBconfig-gateway\fR [\fBargs\fR] <\fBPREFIX\fR>
+Configure gateway.
+
+  \fB\-h\fP, \fB\-\-help\fR
+  Print join help.
+
+  \fB\-t\fP, \fB\-\-timeout\fR [\fBTIMEOUT\fR]
+  Set timeout period.
+
+  \fB\-p\fP, \fB\-\-preferred-lifetime\fR
+  Set the \fBPREFERRED_LIFETIME\fR.
+
+  \fB\-v\fP, \fB\-\-valid-lifetime\fR
+  Set the \fBVALID_LIFETIME\fR.
+
+  \fB\-d\fP, \fB\-\-default\fR
+  Indicates that we can be a default route.
+
+.TP
+\fBform\fR [\fBargs\fR] [\fBNETWORK_NAME\fR]
+Form a new WPAN.
+
+  \fB\-h\fP, \fB\-\-help\fR
+  Print join help.
+
+  \fB\-t\fP, \fB\-\-timeout\fR [\fBTIMEOUT\fR]
+  Set timeout period.
+
+  \fB\-u\fP, \fB\-\-ula-prefix\fR [\fBULA_PREFIX\fR]
+  Specify a specific ULA prefix.
+
+  \fB\-c\fP, \fB\-\-channel\fR [\fBCHANNEL\fR]
+  Specify a specific radio channel.
+
+.TP
+\fBgetprop\fR, \fBget\fR [\fBargs\fR] <\fBproperty-name\fR>
+Get a property.
+
+  \fB\-h\fP, \fB\-\-help\fR
+  Print join help.
+
+  \fB\-t\fP, \fB\-\-timeout\fR [\fBTIMEOUT\fR]
+  Set timeout period.
+
+  \fB\-a\fP, \fB\-\-all\fR
+  Print all supported properties.
+
+  \fB\-v\fP, \fB\-\-value-only\fR
+  Print only the value of the property.
+
+.TP
+\fBhelp\fR
+Display help.
+
+.TP
+\fBhost-did-wake\fR [\fBargs\fR]
+Perform any host-wakeup related tasks.
+
+  \fB\-h\fP, \fB\-\-help\fR
+  Print join help.
+
+  \fB\-t\fP, \fB\-\-timeout\fR [\fBTIMEOUT\fR]
+  Set timeout period.
+
+.TP
+\fBjoin\fR [\fBargs\fR] [\fBNETWORK_NAME\fR]
+Join a WPAN.
+
+  \fB\-h\fP, \fB\-\-help\fR
+  Print join help.
+
+  \fB\-t\fP, \fB\-\-timeout\fR [\fBTIMEOUT\fR]
+  Set timeout period.
+
+  \fB\-T\fP, \fB\-\-type\fR [\fBNODE_TYPE\fR]
+  Join as a specific node type.
+
+  \fB\-p\fP, \fB\-\-panid\fR [\fBPAN_ID\fR]
+  Specify a specific PAN ID.
+
+  \fB\-x\fP, \fB\-\-xpanid\fR [\fBExtended_PAN_ID\fR]
+  Specify a specific extended PAN ID.
+
+  \fB\-c\fP, \fB\-\-channel\fR [\fBCHANNEL\fR]
+  Specify a specific radio channel.
+
+.TP
+\fBleave\fR [\fBargs\fR]
+Abandon the currently connected WPAN.
+
+  \fB\-h\fP, \fB\-\-help\fR
+  Print join help.
+
+  \fB\-t\fP, \fB\-\-timeout\fR [\fBTIMEOUT\fR]
+  Set timeout period.
+
+.TP
+\fBlist\fR [\fBargs\fR]
+List available interfaces.
+
+  \fB\-h\fP, \fB\-\-help\fR
+  Print join help.
+
+  \fB\-t\fP, \fB\-\-timeout\fR [\fBTIMEOUT\fR]
+  Set timeout period.
+
+.TP
+\fBpermit-join\fR [\fBargs\fR] <\fBPERMIT_JOIN_DURATION\fR> [\fBCOMMISSIONING_PORT\fR]
+Permit other devices to join the current network.
+
+  \fB\-h\fP, \fB\-\-help\fR
+  Print join help.
+
+  \fB\-t\fP, \fB\-\-timeout\fR [\fBTIMEOUT\fR]
+  Set timeout period.
+
+  \fB\-n\fP, \fB\-\-network-wide\fR
+  Permit joining network-wide.
+
+  \fB\-c\fP, \fB\-\-tcp\fR
+  Permit only TCP for commissioning traffic.
+
+  \fB\-d\fP, \fB\-\-udp\fR
+  Permit only UDP for commissioning traffic.
+
+.TP
+\fBping\fR [\fBargs\fR]
+Ping the NCP.
+
+  \fB\-h\fP, \fB\-\-help\fR
+  Print join help.
+
+  \fB\-t\fP, \fB\-\-timeout\fR [\fBTIMEOUT\fR]
+  Set timeout period.
+
+.TP
+\fBpoll\fR [\fBargs\fR]
+Poll the parent immediately to see if there is IP traffic.
+
+  \fB\-h\fP, \fB\-\-help\fR
+  Print join help.
+
+  \fB\-t\fP, \fB\-\-timeout\fR [\fBTIMEOUT\fR]
+  Set timeout period.
+
+.TP
+\fBquit\fR
+Terminate command line mode.
+
+.TP
+\fBreset\fR [\fBargs\fR]
+Reset the NCP.
+
+  \fB\-h\fP, \fB\-\-help\fR
+  Print join help.
+
+  \fB\-t\fP, \fB\-\-timeout\fR [\fBTIMEOUT\fR]
+  Set timeout period.
+
+.TP
+\fBresume\fR [\fBargs\fR]
+Resume a previously connected network.
+
+  \fB\-h\fP, \fB\-\-help\fR
+  Print join help.
+
+  \fB\-t\fP, \fB\-\-timeout\fR [\fBTIMEOUT\fR]
+  Set timeout period.
+
+.TP
+\fBscan\fR [\fBargs\fR]
+Scan for nearby networks.
+
+  \fB\-h\fP, \fB\-\-help\fR
+  Print join help.
+
+  \fB\-t\fP, \fB\-\-timeout\fR [\fBTIMEOUT\fR]
+  Set timeout period.
+
+  \fB\-c\fP, \fB\-\-channel\fR [\fBCHANNEL\fR]
+  Specify a specific radio channel.
+
+.TP
+\fBsetprop\fR, \fBset\fR \fBget\fR [\fBargs\fR] <\fBproperty-name\fR> <\fBproperty-value\fR>
+Set a property.
+
+  \fB\-h\fP, \fB\-\-help\fR
+  Print join help.
+
+  \fB\-t\fP, \fB\-\-timeout\fR [\fBTIMEOUT\fR]
+  Set timeout period.
+
+  \fB\-d\fP, \fB\-\-data\fR
+  Value is binary data (in hex).
+
+  \fB\-s\fP, \fB\-\-string\fR
+  Value is a string.
+
+  \fB\-i\fP, \fB\-\-integer\fR
+  Value is an integer.
+
+  \fB\-v\fP, \fB\-\-value\fR
+  Useful when the value starts with a '-'.
+
+.TP
+\fBstatus\fR [\fBargs\fR]
+Retrieve the status of the interface.
+
+  \fB\-h\fP, \fB\-\-help\fR
+  Print join help.
+
+  \fB\-t\fP, \fB\-\-timeout\fR [\fBTIMEOUT\fR]
+  Set timeout period.
+
+
+.SH EXAMPLES
+
+.TP
+$ sudo wpanctl
+Enter wpanctl command line interface that starts with prompt \fBwpanctl:[Iinterface_name]>\fR
+
+.TP
+$ sudo wpanctl -I <interface-name>
+Enter wpanctl command line and connect to specified interface, e.g.
+\fI$ sudo wpanctl -I wpan0\fR
+results with the following wpanctl shell prompt:
+\fBwpanctl:wpan0>\fR
+
+.TP
+$ sudo wpanctl scan
+Sends \fIscan\fR command to wpantund.
+The wpantund output is printed in the shell.
+
+.TP
+$ sudo wpanctl scan -c25-26,14,18-16
+Requests wpantund to scan for networks on channels 14,16,17,18,25,26.
+
+.SH DEFINITIONS
+
+.TP
+\fBCHANNEL\fR
+Integer in range [11-26] indicating one of the IEEE 802.15.4 channels.
+
+.TP
+\fBCOMMISSIONING_PORT\fR
+???
+
+.TP
+\fBEXTENDED_PAN_ID\fR
+???
+
+.TP
+\fBNCP\fR
+Network Control Processor.
+
+.TP
+\fBNETWORK_NAME\fR
+A string identifier of a network, e.g. "HomeNetwork".
+
+.TP
+\fBNODE_TYPE\fR
+WPAN type as one of the following:
+  - 0 (unknown): will starts as 2
+  - 1 (coordinator)
+  - 2 (router)
+  - 3 (end | end-device)
+  - 4 (sleepy | sleepy-end-device)
+  - 5 (mobile | mobile-end-device)
+  - 6 (lurker)
+
+.TP
+\fBPAN_ID\fR
+???
+
+.TP
+\fBPERMIT_JOIN_DURATION\fR
+???unit??
+unit: seconds (sec)
+
+.TP
+\fBPREFERRED_LIFETIME\fR
+???
+unit: seconds (sec)
+(Default: infinite)
+
+.TP
+\fBTIMEOUT\fR
+???
+unit: milliseconds (msec)
+
+.TP
+\fBULA_PREFIX\fR
+A unique local address (ULA) in the block fc00::/7.
+
+.TP
+\fBVALID_LIFETIME\fR
+???
+(Default: infinite)
+
+\" .SH FILES
+\" .TP
+\" .I /etc/wpantund.conf
+\" The default wpantund configuration file.
+
+.SH SEE ALSO
+wpantund(1)
+.SH BUGS
+Bug tracker:
+.IP
+http://...
+.PP
+
+.\" .SH HISTORY
diff --git a/doc/wpantund-oss-plan.md b/doc/wpantund-oss-plan.md
new file mode 100644
index 0000000..e2d7003
--- /dev/null
+++ b/doc/wpantund-oss-plan.md
@@ -0,0 +1,235 @@
+# "wpantund" Open-Source Plan #
+
+This document outlines the resources, processes, and procedures for
+the launch and ongoing operation of the "wpantund" open-source
+project.
+
+## Online Resources ##
+
+ *  Github: <https://github.com/openthread/wpantund/>
+     *  Public Wiki: <https://github.com/openthread/wpantund/wiki>
+ *  Front-end URL: <http://wpantund.org>
+     *  Will initially redirect to
+        <https://github.com/openthread/wpantund/#readme>
+     *  Content eventually maintained in gh-pages branch.
+ *  Mailing Lists
+     *  <wpantund-announce@googlegroups.com>
+         *  Read-only. Announces releases, security issues, etc.
+     *  <wpantund-user@googlegroups.com>
+         *  Read-write. For people who are using wpantund.
+     *  <wpantund-devel@googlegroups.com>
+         *  Read-write. For people who are contributing to wpantund.
+
+## Continuous Integration ##
+
+"wpantund" will use [Travis](https://travis-ci.org/) for continuous
+integration testing. All pull requests MUST pass the travis build test
+before being considered for merging into the master branch.
+
+## Versoning ##
+
+"wpantund" follows the Semantic Versioning guidelines, with one
+exception: the minor and patch versions use a minimum of two digits to
+facilitate better sorting.
+
+In short, the version format is of the form `M.mm.pp`, where `M` is
+the major version, `mm` is the minor version, and `pp` is the patch
+level. Additional suffixes can be appended according to the release
+type, as described in the following section.
+
+## Releases ##
+
+A "release" denotes a specific state of the code base that has been
+officially designated a unique identifier called the version
+(Depending on context, it is sometimes called the release tag). Note
+that this section refers to a several git branches which are defined
+in later sections.
+
+Broadly, there are three types of releases:
+
+**Primary Releases** â€” Any release in which the release tag takes the
+form of `M.mm.pp`, without any additional annotations. These are the
+canonical releases. There are generally two categories of primary
+releases:
+
+ *  **Patch Releases** â€” Increment only the patch version. Made
+    periodically as needed.
+ *  **Major and Minor Releases** â€” Patch version is always `00`. Used
+    as feature milestones and are planned well in advance.
+
+**Pre-Releases** â€” Any release leading up to a primary release. There
+are three different types of pre-releases:
+
+ *  **Alpha Releases** â€” which have the suffix `a#`, e.g.: `1.02.03a4`
+ *  **Beta Releases** â€” which have the suffix `b#`, e.g.: `1.02.03b4`
+ *  **Release Candidates** â€” which have the suffix `rc#`, e.g.:
+    `1.02.03rc4`
+
+**Special Releases** â€” Any release that is intended for a special
+purpose or task. Denoted by the suffix `-*`, e.g. `1.02.03-special`
+or `4.56.78a9-foobar`.
+
+### Release Process Output ###
+
+"Cutting a release" involves the creation and updating of tarballs,
+branches, and tags. The following git tags are created or updated:
+
+ *  `<VERSION>` â€” The identifying tag of the release, pointing to
+    the commit that is being released. The contents of this tag
+    contain the information contained in the CHANGELOG which has been
+    added since the previous release this release was based on. This
+    tag is cryptographically signed with the OpenPGP signature of a
+    project maintainer.  Ex: `1.02.03a4`
+ *  `full/<VERSION>` - A merge commit between the release tag and the
+    corresponding commit from `autoconf/master` (See the git
+    repository plan later in this document). In other words, this tag
+    points to the actual files that are released in the official
+    tarballs. Created for each of the two above tags (contains
+    autoconf files). This tag is cryptographically signed with the
+    OpenPGP signature of a project maintainer.  Ex: `full/1.02.03a4`
+ *  `latest-unstable` â€” Updated to point to the release tag ONLY if
+    the version of this release is larger than the previous version
+    this tag points to. This is a pointer-only tag.
+ *  `latest-release` â€” Updated to point to the release tag ONLY if
+    this release is a primary release and ONLY if version of this
+    release is larger than the previous version this tag points to.
+    This is a pointer-only tag, and MUST NOT contain a message.
+ *  `full/latest-unstable` / `full/latest-release` â€” Created for
+    each of the two above tags (contains autoconf files).
+
+Tarballs are created by checking out the `full/<VERSION>` tag (created
+above) and performing a `./configure && make distcheck`. The resulting
+files are the official tarballs. All released tarballs are
+cryptographically signed with the OpenPGP signature of a maintainer.
+
+### Release Checklist ###
+
+The following is the checklist followed by a project maintainer to cut
+an official release from the top of the master branch:
+
+1.  Clone a clean copy of the repository from the official repository
+    on GitHub. If this fails, STOP.
+2.  Do a `git checkout master`. If this fails, STOP.
+3.  Do a `git show`. Verify that Travis has passed the given commit.
+    If this commit has not passed, STOP.
+4.  Collect the changes that have been included since the last release
+    and update the contents of the CHANGELOG accordingly.
+5.  Verify that the project version in `.default-version` is set to
+    the version that is about to be released. This should already be
+    the case, but if it isn’t go ahead and update `.default-version`
+     to the version of the release.
+6.  Commit the changes made to the working directory from steps 2 and
+    3\. Use a commit message like `Updated CHANGELOG for release
+    <VERSION>` or `Updated CHANGELOG and configure.ac for release
+    <VERSION>`.
+7.  If this is a prerelease, copy the section that you just added to
+    the CHANGELOG to the clipboard. If this is a primary release,
+    copy the contents of the CHANGELOG added after the last primary
+    release to the clipboard.
+8.  Create a signed tag for the release named `<VERSION>`, making sure
+    pasting in the contents we copied to the clipboard as the tag
+    message. This is performed with the command `git tag -s
+    <VERSION>`. If this command fails, STOP.
+9.  Check out the `origin/scripts` branch: `git checkout
+    origin/scripts`. If this command fails, STOP.
+10. If this is a primary release execute the command
+    `./primary-release-from-master-helper`; or if this is a
+    prerelease execute the command
+    `./pre-release-from-master-helper`. These commands update
+    `autoconf/master`, `full/<VERSION>`, `latest-unstable`,
+    `full/latest-unstable` and (if this is a primary release)
+    `latest-release`, `full/latest-release`. If the command fails,
+    STOP. Note that the tag `full/<VERSION>` will be signed by this
+    process, so you may be asked for your GPG keychain credentials or
+    your token PIN.
+11. Check out the `full/<VERSION>` branch: `git checkout
+    full/<VERSION>`. If this command fails, STOP.
+12. Execute the command `./configure && make distcheck`. If this
+    command fails, STOP.
+13. There should now be at least one tarball (maybe more) in the
+    current directory. If this is not the case, STOP.
+14. Use GPG to sign the tarballs. If this process fails, STOP.
+15. Type `git show master`. Review the commit to make sure it looks
+    sane. If you notice anything wrong, STOP.
+16. Type `git show <VERSION>`. Review the tag notes make they look
+    properly formatted. If you notice anything wrong, STOP.
+17. Push the branches and tags to the origin server: `git push origin
+    master autoconf/master <VERSION> full/<VERSION>`. If this command
+    fails, STOP.
+18. Update the `latest-*` tags on the origin server: `git push origin
+    latest-release latest-unstable full/latest-release
+    full/latest-unstable`
+19. Upload the tarballs and their signatures to the designated
+    location. (Currently TBD)
+20. ???
+21. Pr0fit!!1
+
+Note that the above checklist is only appropriate for cutting releases
+from the master branch! Cutting releases from non-master branches
+would follow a similar (but different) process that is not yet
+defined.
+
+## Public Git Repository Branch and Tag Plan ##
+
+### Branches ###
+
+ *  `master` â€” Main development branch. Acceptable commits are
+    generally limited to those intended for the next major/minor
+    release. Does not contain any autoconf-generated files!
+ *  `autoconf/master` â€” All of (and only!) the autoconf-generated
+    files associated with master. Automatically maintained by a script
+    that checks if a commit to master has invalidated any of these
+    files and commits updates.
+ *  `scripts` â€” A collection of repository maintenance scripts.
+ *  `gh-pages` â€” Will eventually contain the content of
+    <http://wpantund.org/>.
+ *  `feature/*` â€” Feature-specific branches that are currently
+    under development but not yet appropriate for inclusion in master.
+ *  `release/M.mm/master` â€” A separate branch maintained for each
+    minor version release, for tracking things like security updates
+    and back-ported changes. These branches are created when
+    master is opened to taking commits for the next major/minor
+    release.
+
+### Tags ###
+
+ *  `M.mm.pp` â€” Each primary release is tagged with the bare
+    version number of the release.
+ *  `M.mm.ppx#` â€” Each pre-release is tagged with the version of
+    the upcoming release with a suffix `x` denoting the type of
+    pre-release and `#` denoting the count.
+ *  `M.mm.pp-x` â€” Each special-purpose release is tagged with the
+    version of the most recent official release (for any other type),
+    suffixed with a dash and a short descriptor of the release.
+ *  `full/*` â€” For each release (primary, pre, or
+    special-purpose), the tag that is the name of the release prefixed
+    with `full/` points to a merge commit between the release tag and
+    the corresponding commit from `autoconf/master`. In other words,
+    this tag points to the actual files that are released in the
+    official tarballs.
+ *  `latest-release` â€” A tag with no message that points to the tag
+    of the most recent primary release.
+ *  `full/latest-release` â€” A tag with no message that points to
+    the tag of the most recent primary release in `full/*`.
+ *  `latest-unstable` â€” A tag with no message that points to the
+    tag of the most recent primary release or pre-release.
+ *  `full/latest-unstable` â€” A tag with no message that points to
+    the tag of the most recent primary release or pre-release in
+    `full/*`.
+
+### Regarding Autotools ###
+
+No generated autotools files are allowed in master, or in any branch
+that doesn’t begin with `autoconf/` or `full/`.
+
+If you are working from the top of master and don’t have a working
+copy of autotools, the following command will populate your working
+tree with all of the appropriate files and is the functional
+equivalent of typing `./bootstrap.sh` on a machine with autotools
+installed:
+
+	git archive origin/autoconf/master | tar xv
+
+Note that the `full/*` branches contain the full set of files,
+including the sources and autotools files. The `autoconf/*` branches
+contain ONLY the autoconf files.
diff --git a/doc/wpantund.1 b/doc/wpantund.1
new file mode 100644
index 0000000..28b56ea
--- /dev/null
+++ b/doc/wpantund.1
@@ -0,0 +1,97 @@
+.TH wpantund 1 "" "" "USER COMMANDS"
+
+.SH NAME
+wpantund \- interface to 6LoWPAN communication
+
+.SH SYNOPSIS
+.B wpantund
+[\fIOPTIONS\fR]
+
+.SH DESCRIPTION
+.B wpantund
+is a userspace driver which uses the Linux TUN driver (or the UTUN driver on OS X)
+to present a network interface for 6LoWPAN communication. It communicates with the
+NCP via a socket, which can be any of the following:
+.TP
+\- a TCP socket
+
+.TP
+\- a serial port
+
+.TP
+\- stdin and stdout from a subprocess
+
+.SH OPTIONS
+
+.TP
+\fB\-h\fP, \fB\-\-help\fp
+Print help.
+
+.TP
+\fB\-v\fP, \fB\-\-version\fp
+Print version.
+
+.TP
+\fB\-d\fP, \fB\-\-debug\fp \fILEVEL
+Enable debugging mode (see \fBDEBUGGING\fP below).
+
+.TP
+\fB\-c\fP, \fB\-\-config\fp \fIFILE
+Specify config file (default /etc/wpantund.conf).
+
+.TP
+\fB\-s\fP, \fB\-\-socket\fp \fIFILE
+Specify socket file; same as \fI-o NCPSocketName\fR.
+
+.TP
+\fB\-b\fP, \fB\-\-baudrate\fp \fIFILE
+Specify baudrate; same as \fI-o NCPSocketBaud\fR.
+
+.TP
+\fB\-I\fP, \fB\-\-interface\fp \fIFILE
+Specify network interface; same as \fI-o WPANInterfaceName\fR.
+
+.TP
+\fB\-o\fP, \fB\-\-option\fp \fIWPAN_OPTIONS
+Overwrites config file options.
+
+  -- NCPSocketName \fIFILE\fR
+     Specify socket location (e.g. /dev/ttyUSB0).
+
+  -- WPANInterfaceName \fIINTERFACE\fR
+     Specify the name of the created network interface (e.g. wpan0).
+
+  -- SyslogMask \fIMASK\fR
+     To add more logs, start \fBwpantund\fP with SyslogMask all.
+
+  -- PIDFile \fIFILE\fR
+     Specify location of the file with the wpan-tunnel-daemon PID.
+
+  -- NCPReliabilityLayer
+     Specify a serial reliability layer, \fBlibsoot\fP or \fBnone\fP.
+
+  -- NCPSocketBaud
+
+.SH DEBUGGING
+
+
+.SH EXAMPLES
+
+.TP
+$ sudo wpantund -o NCPSocketName /dev/ttyUSB0 -o WPANInterfaceName wpan0
+Starts wpantund on /dev/ttyUSB0 and create network intarfeca called wpan0.
+
+.SH FILES
+.TP
+.I /etc/wpantund.conf
+The default wpantund configuration file.
+
+.SH SEE ALSO
+wpanctl(1)
+.SH BUGS
+Bug tracker:
+.IP
+http://...
+.PP
+
+.\" .SH HISTORY           \" Document history if command behaves in a unique manner
diff --git a/doxygen.cfg.in b/doxygen.cfg.in
new file mode 100644
index 0000000..a301174
--- /dev/null
+++ b/doxygen.cfg.in
@@ -0,0 +1,1808 @@
+# Doxyfile 1.8.2
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or sequence of words) that should
+# identify the project. Note that if you do not use Doxywizard you need
+# to put quotes around the project name if it contains spaces.
+
+PROJECT_NAME           = "@PACKAGE@"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER         =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          = "Wireless Network Interface Daemon for Low-Power Wireless SoCs"
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = doc
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip. Note that you specify absolute paths here, but also
+# relative paths, which will be relative from the directory where doxygen is
+# started.
+
+STRIP_FROM_PATH        = @top_srcdir@
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding
+# "class=itcl::class" will allow you to use the command class in the
+# itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension,
+# and language is one of the parsers supported by doxygen: IDL, Java,
+# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C,
+# C++. For instance to make doxygen treat .inc files as Fortran files (default
+# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note
+# that for custom extensions you also need to set FILE_PATTERNS otherwise the
+# files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
+# comments according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you
+# can mix doxygen, HTML, and XML commands with Markdown formatting.
+# Disable only in case of backward compatibilities issues.
+
+MARKDOWN_SUPPORT       = YES
+
+# When enabled doxygen tries to link words that correspond to documented classes,
+# or namespaces to their corresponding documentation. Such a link can be
+# prevented in individual cases by by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter and setter methods for a property. Setting this option to YES (the default) will make doxygen replace the get and set methods by a property in the documentation. This will only work if the methods are indeed getting or setting a simple type. If this is not the case, or you want to show the methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
+# unions with only public data fields will be shown inline in the documentation
+# of the scope in which they are defined (i.e. file, namespace, or group
+# documentation), provided this scope is documented. If set to NO (the default),
+# structs, classes, and unions are shown on a separate page (for HTML and Man
+# pages) or section (for LaTeX and RTF).
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+SYMBOL_CACHE_SIZE      = 0
+
+# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
+# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
+# their name and scope. Since this can be an expensive process and often the
+# same symbol appear multiple times in the code, doxygen keeps a cache of
+# pre-resolved symbols. If the cache is too small doxygen will become slower.
+# If the cache is too large, memory is wasted. The cache size is given by this
+# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files
+# containing the references data. This must be a list of .bib files. The
+# .bib extension is automatically appended if omitted. Using this command
+# requires the bibtex tool to be installed. See also
+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
+# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
+# feature you need bibtex and perl available in the search path.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT                  = @top_srcdir@ @top_srcdir@/src/wpantund @top_srcdir@/src/wpanctl @top_srcdir@/src/ncp-spinel @top_srcdir@/src/ncp-dummy @top_srcdir@/src/ipc-dbus @top_srcdir@/src/utils
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS          =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C, C++ and Fortran comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+#  for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when
+# changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If left blank doxygen will
+# generate a default style sheet. Note that it is recommended to use
+# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this
+# tag will in the future become obsolete.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional
+# user-defined cascading style sheet that is included after the standard
+# style sheets created by doxygen. Using this option one can overrule
+# certain style aspects. This is preferred over using HTML_STYLESHEET
+# since it does not replace the standard style sheet and is therefor more
+# robust against future updates. Doxygen will copy the style sheet file to
+# the output directory.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the style sheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP         = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of
+# entries shown in the various tree structured indices initially; the user
+# can expand and collapse entries dynamically later on. Doxygen will expand
+# the tree to such a level that at most the specified number of entries are
+# visible (unless a fully collapsed tree already exceeds this amount).
+# So setting the number of entries 1 will produce a full collapsed tree by
+# default. 0 is a special value representing an infinite number of entries
+# and will result in a full expanded tree by default.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely
+# identify the documentation publisher. This should be a reverse domain-name
+# style string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE               =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+#  will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
+# at top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it. Since the tabs have the same information as the
+# navigation tree you can set this option to NO if you already set
+# GENERATE_TREEVIEW to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+# Since the tree basically has the same information as the tab index you
+# could consider to set DISABLE_INDEX to NO when enabling this option.
+
+GENERATE_TREEVIEW      = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you may also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to
+# the MathJax Content Delivery Network so you can quickly see the result without
+# installing MathJax.
+# However, it is strongly recommended to install a local
+# copy of MathJax from http://www.mathjax.org before deployment.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
+# names that should be enabled during MathJax rendering.
+
+MATHJAX_EXTENSIONS     =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX         = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER           =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
+# http://en.wikipedia.org/wiki/BibTeX for more info.
+
+LATEX_BIB_STYLE        = plain
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load style sheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED             =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. For each
+# tag file the location of the external documentation should be added. The
+# format of a tag file without this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths
+# or URLs. Note that each tag file must have a unique name (where the name does
+# NOT include the path). If a tag file is not located in the directory in which
+# doxygen is run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS        = 0
+
+# By default doxygen will use the Helvetica font for all dot files that
+# doxygen generates. When you want a differently looking font you can specify
+# the font name using DOT_FONTNAME. You need to make sure dot is able to find
+# the font, which can be done by putting it in a standard location or by setting
+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# directory containing the font.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the Helvetica font.
+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
+# set the path where dot can find it.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside
+# the class node. If there are many fields or methods and many nodes the
+# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
+# threshold limits the number of items for each type to make the size more
+# managable. Set this to 0 for no limit. Note that the threshold may be
+# exceeded by 50% before the limit is enforced.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used. If you choose svg you need to set
+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible in IE 9+ (other browsers do not have this requirement).
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+# Note that this requires a modern browser other than Internet Explorer.
+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible. Older versions of IE do not have SVG support.
+
+INTERACTIVE_SVG        = NO
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP            = YES
diff --git a/etc/Dockerfile b/etc/Dockerfile
new file mode 100644
index 0000000..aacc0d1
--- /dev/null
+++ b/etc/Dockerfile
@@ -0,0 +1,45 @@
+#
+# Copyright (c) 2016 Nest Labs, Inc.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+FROM gcc
+
+ENV CONNMAN_INCLUDE_ARCHIVE=https://gist.github.com/darconeous/d1d9bc39e0758e45a1d7/raw/ef9b01ac378a9b2e92031c846c4f6b5f94abab53/connman-include.tar.bz2
+
+RUN apt-get -y update \
+	&& DEBIAN_FRONTEND=noninteractive \
+			apt-get install -y -q --no-install-recommends \
+				libglib2.0-dev \
+				libdbus-1-dev \
+				libdbus-1-dev \
+				libboost-dev \
+				libboost-signals-dev \
+				libreadline-dev \
+				libtool \
+				autoconf-archive \
+				dbus \
+				xtables-addons-source \
+				net-tools \
+				usbutils \
+				vim \
+				man \
+				bsdtar \
+				gcc g++ \
+				pkg-config
+
+ADD $CONNMAN_INCLUDE_ARCHIVE /
+
+RUN tar xvjf /connman-include.tar.bz2 -C / && rm /connman-include.tar.bz2
diff --git a/etc/build-in-docker.sh b/etc/build-in-docker.sh
new file mode 100755
index 0000000..c6d3083
--- /dev/null
+++ b/etc/build-in-docker.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+#
+# Copyright (c) 2016 Nest Labs, Inc.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+DIR="`dirname $0`"
+
+"${DIR}"/run-in-docker.sh -i 'DIR="`pwd`" &&
+mkdir -p /build &&
+cd /build && "${DIR}"/configure --enable-all-restricted-plugins --with-connman --with-readline &&
+make -j `nproc` distcheck AM_DEFAULT_VERBOSITY=1
+' || exit 1
+
+"${DIR}"/run-in-docker.sh -i 'DIR="`pwd`" &&
+mkdir -p /build &&
+cd /build && "${DIR}"/configure --enable-ncp-spinel --enable-static-link-ncp-plugin=spinel &&
+make -j `nproc` check AM_DEFAULT_VERBOSITY=1 &&
+make -j `nproc` install DESTDIR=/destdir AM_DEFAULT_VERBOSITY=1 &&
+cd /destdir &&
+find .
+' || exit 1
diff --git a/etc/run-in-docker.sh b/etc/run-in-docker.sh
new file mode 100755
index 0000000..fe8829d
--- /dev/null
+++ b/etc/run-in-docker.sh
@@ -0,0 +1,161 @@
+#!/bin/bash
+#
+# Copyright (c) 2016 Nest Labs, Inc.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+set -e
+
+path_to_dir ()
+{
+    f=$@;
+    if [ -d "$f" ]; then
+        base="";
+        dir="$f";
+    else
+        base="/$(basename "$f")";
+        dir=$(dirname "$f");
+    fi;
+    dir=$(cd "$dir" && /bin/pwd);
+    echo "$dir$base"
+}
+
+usage ()
+{
+  echo "usage: "
+  echo "$0 [-i] <command to exec inside docker>"
+  echo "$0 -c|--clear"
+  echo "$0 -a|--clear-all"
+  echo -n
+  echo "-c, --clear              delete all docker images including the current one"
+  echo "--priv                   run container with privileges"
+  echo "-p, --prune              delete all docker images except the current one"
+  echo "-i, --interactive        run docker with -it"
+}
+
+calc_sha1 ()
+{
+	# Figure out which program to use to calculate
+	# the SHA1 hash and execute it.
+	if ( which shasum > /dev/null 2>&1 )
+	then shasum "$1"
+	elif ( which sha1sum > /dev/null 2>&1 )
+	then sha1sum "$1"
+	elif ( which openssl > /dev/null 2>&1 )
+	then openssl dgst -sha1 < "$1"
+	fi
+}
+
+SCRIPT_DIR="$(path_to_dir "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )")"
+PROJECT_DIR="$(pwd)"
+SRC_DIR="/src"
+
+# 1. Calculate the SHA1 hash of the Dockerfile
+DOCKER_SHA=$(calc_sha1 $SCRIPT_DIR/Dockerfile)
+DOCKER_SHORT_SHA=${DOCKER_SHA:0:8}
+DOCKER_IMAGE_NAME_BASE="nestlabs/wpantund-build"
+DOCKER_IMAGE_NAME="$DOCKER_IMAGE_NAME_BASE:$DOCKER_SHORT_SHA"
+
+prune_unused_dockers ()
+{
+  docker images | grep $DOCKER_IMAGE_NAME_BASE | awk 'BEGIN{OFS = ":"}{print $1,$2}' | grep -v $DOCKER_IMAGE_NAME | xargs -I {} docker rmi {}
+  return 0
+}
+
+delete_current_docker ()
+{
+  docker rmi $DOCKER_IMAGE_NAME
+}
+
+delete_all_dockers ()
+{
+  prune_unused_dockers
+  delete_current_docker
+  rm -rf "$SCRIPT_DIR/hostinfo"
+}
+
+if [[ $# -eq 0 ]]
+then
+  echo "Give me something to do."
+  usage
+  exit 0
+fi
+
+OTHER_OPTS=""
+
+# Check to see if we have any arguments to work on.
+while [[ $# > 0 ]]
+do
+  key="$1"
+
+  case $key in
+    -h|--help)
+      usage
+     exit 0;
+      ;;
+    -c|--clear)
+      delete_all_dockers
+      exit $?
+      ;;
+    -p|--prune)
+      prune_unused_dockers
+      exit $?
+      ;;
+    --priv)
+      OTHER_OPTS="${OTHER_OPTS} --privileged"
+      ;;
+    -i|--it)
+      OTHER_OPTS="${OTHER_OPTS} -it"
+      ;;
+    *)
+      if [ ${key:0:1} ==  '-' ]
+      then
+        echo "Unknown option $key"
+        usage
+        exit 1
+      fi
+      break
+      ;;
+  esac
+  shift
+done
+
+# 2. Check to see if that docker image exists.
+RESULT=$(docker images | awk 'BEGIN{OFS = ":"}{print $1,$2}' |grep -c $DOCKER_IMAGE_NAME) || true
+
+if [ $RESULT -eq 1 ]; then
+  echo "Docker image $DOCKER_IMAGE_NAME already exists"
+else
+  echo "Creating new docker image: $DOCKER_IMAGE_NAME"
+  docker build -t $DOCKER_IMAGE_NAME $SCRIPT_DIR
+fi
+
+COMMAND="$@"
+TMPFILE="$(mktemp ./tmp.XXXXXXXXXX)"
+echo "$COMMAND" > "$TMPFILE"
+chmod 0755 "$TMPFILE"
+trap "rm -f '$TMPFILE'" EXIT
+echo "Executing '$COMMAND' as $USERNAME"
+
+CMD="docker run \
+  -v=$PROJECT_DIR:$SRC_DIR \
+  -w='$SRC_DIR' \
+  --hostname=`hostname`\
+  --rm \
+  $OTHER_OPTS \
+  $DOCKER_IMAGE_NAME \
+  bash --login -ci '$TMPFILE'"
+
+$CMD
diff --git a/etc/wpantund.rb b/etc/wpantund.rb
new file mode 100644
index 0000000..dda68a3
--- /dev/null
+++ b/etc/wpantund.rb
@@ -0,0 +1,57 @@
+# Homebrew recipe for wpantund.
+
+#
+# Copyright (c) 2016 Nest Labs, Inc.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'formula'
+
+class Wpantund < Formula
+  homepage 'https://github.com/openthread/wpantund'
+  head 'https://github.com/openthread/wpantund.git', :using => :git, :branch => 'master'
+  url 'https://github.com/openthread/wpantund.git', :using => :git, :tag => 'full/0.07.00'
+  version '0.07.00'
+
+  devel do
+    url 'https://github.com/openthread/wpantund.git', :using => :git, :tag => 'full/latest-unstable'
+  end
+
+  depends_on 'pkg-config' => :build
+  depends_on 'd-bus'
+  depends_on 'boost'
+
+  depends_on 'autoconf' => :build
+  depends_on 'automake' => :build
+  depends_on 'libtool' => :build
+  depends_on 'autoconf-archive' => :build
+
+  def install
+    system "[ -x configure ] || PATH=\"#{HOMEBREW_PREFIX}/bin:$PATH\" ./bootstrap.sh"
+
+    system "./configure",
+      "--disable-dependency-tracking",
+      "--without-connman",
+      "--enable-all-restricted-plugins",
+      "--prefix=#{prefix}"
+
+    system "make check"
+    system "make install"
+  end
+
+  def test
+    system "wpanctl"
+  end
+end
diff --git a/m4/nl.m4 b/m4/nl.m4
new file mode 100644
index 0000000..ee69c05
--- /dev/null
+++ b/m4/nl.m4
@@ -0,0 +1,305 @@
+dnl #
+dnl # Copyright (c) 2016 Nest Labs, Inc.
+dnl # All rights reserved.
+dnl #
+dnl # Licensed under the Apache License, Version 2.0 (the "License");
+dnl # you may not use this file except in compliance with the License.
+dnl # You may obtain a copy of the License at
+dnl #
+dnl #    http://www.apache.org/licenses/LICENSE-2.0
+dnl #
+dnl # Unless required by applicable law or agreed to in writing, software
+dnl # distributed under the License is distributed on an "AS IS" BASIS,
+dnl # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl # See the License for the specific language governing permissions and
+dnl # limitations under the License.
+dnl #
+
+AC_DEFUN([NL_DEBUG], [
+	AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug[=verbose|symbols]],
+				[enable compiling with debugging information and symbols]),
+				[],
+				[enable_debug=no])
+
+	if test "x$enable_debug" '!=' "xno"
+	then
+		CXXFLAGS="$CXXFLAGS -g"
+		CFLAGS="$CFLAGS -g"
+
+		if test "x$enable_debug" '!=' "xsymbols"
+		then CPPFLAGS="$CPPFLAGS -DDEBUG=1 -DDEBUG_LEVEL=2"
+		fi
+
+		if test "x$enable_debug" = "xverbose"
+		then CPPFLAGS="$CPPFLAGS -DVERBOSE_DEBUG=1"
+		fi
+	fi
+
+
+	AM_CONDITIONAL([DEBUG],[test "x$enable_debug" '!=' "xno" && test "x$enable_debug" '!=' "xsymbols"])
+	AM_CONDITIONAL([VERBOSE_DEBUG],[test "x$enable_debug" = "xverbose"])
+])
+
+AC_DEFUN([NL_EXPORT_DYNAMIC], [
+	prev_LDFLAGS="${LDFLAGS}"
+	LDFLAGS="-Wl,--export-dynamic"
+	AC_LANG_PUSH([C])
+	AC_MSG_CHECKING([linker flags needed for programs to export symbols])
+	AC_LINK_IFELSE(
+		AC_LANG_PROGRAM,
+		[EXPORT_DYNAMIC_LDFLAGS="$LDFLAGS"],
+		[EXPORT_DYNAMIC_LDFLAGS=""]
+	)
+	LDFLAGS="${prev_LDFLAGS}"
+	unset prev_LDFLAGS
+	AC_MSG_RESULT([\"$EXPORT_DYNAMIC_LDFLAGS\"])
+	AC_SUBST(EXPORT_DYNAMIC_LDFLAGS)
+	AC_LANG_POP([C])
+])
+
+AC_DEFUN([NL_CHECK_BOOST_NO_CXX11_RVALUE_REFERENCES], [
+	AC_MSG_CHECKING([necessity of BOOST_NO_CXX11_RVALUE_REFERENCES])
+	BOOST_NO_CXX11_RVALUE_REFERENCES=false
+	AC_PREPROC_IFELSE([AC_LANG_SOURCE([[
+#if __APPLE__ && (__GNUC_LIBSTD__ <= 4) && (__GNUC_LIBSTD_MINOR__ <= 2)
+#error BOOST_NO_CXX11_RVALUE_REFERENCES needs to be set
+#endif
+	]])], [BOOST_NO_CXX11_RVALUE_REFERENCES=false], [BOOST_NO_CXX11_RVALUE_REFERENCES=true])
+	AM_CONDITIONAL([BOOST_NO_CXX11_RVALUE_REFERENCES],[$BOOST_NO_CXX11_RVALUE_REFERENCES])
+	AC_MSG_RESULT([$BOOST_NO_CXX11_RVALUE_REFERENCES])
+])
+
+AC_DEFUN([NL_CHECK_BOOST_SIGNALS2], [
+	AC_LANG_PUSH([C++])
+
+	AC_ARG_VAR([BOOST_CXXFLAGS], [C compiler flags for boost])
+	AC_ARG_VAR([BOOST_LIBS], [linker flags for boost])
+
+	# If BOOST_CFLAGS was set for some reason, merge them into BOOST_CXXFLAGS.
+	test -n "${BOOST_CFLAGS}" && BOOST_CXXFLAGS="${BOOST_CXXFLAGS} ${BOOST_CFLAGS}"
+
+	# Go ahead and add the BOOST_CXXFLAGS into CXXFLAGS for now.
+	nl_check_boost_signals2_CXXFLAGS="${CXXFLAGS}"
+	nl_check_boost_signals2_CPPFLAGS="${CPPFLAGS}"
+	CXXFLAGS="${BOOST_CXXFLAGS}"
+	CPPFLAGS="${BOOST_CXXFLAGS}"
+
+	NL_CHECK_BOOST_NO_CXX11_RVALUE_REFERENCES
+	AC_CHECK_HEADERS([boost/signals2/signal.hpp], [$1],[
+
+		# Sometimes boost explicitly needs this flag to work.
+		AX_CHECK_COMPILE_FLAG([-std=c++11], [
+			CXXFLAGS="$CXXFLAGS -std=c++11"
+			CPPFLAGS="$CPPFLAGS -std=c++11"
+			BOOST_CXXFLAGS="$BOOST_CXXFLAGS -std=c++11"
+		], [$2])
+
+		## Clear the cache entry we that we try again
+		unset ac_cv_header_boost_signals2_signal_hpp
+
+		AC_CHECK_HEADERS([boost/signals2/signal.hpp], [$1], [$2])
+	])
+	AC_SUBST(BOOST_CXXFLAGS)
+	AC_SUBST(BOOST_LIBS)
+
+	CXXFLAGS="${nl_check_boost_signals2_CXXFLAGS}"
+	unset nl_check_boost_signals2_CXXFLAGS
+
+	CPPFLAGS="${nl_check_boost_signals2_CPPFLAGS}"
+	unset nl_check_boost_signals2_CPPFLAGS
+
+	AC_LANG_POP([C++])
+])
+
+AC_DEFUN([NL_CHECK_READLINE], [
+	AC_LANG_PUSH([C])
+
+	readline_required=no
+
+	AC_ARG_WITH(
+		[readline],
+		AC_HELP_STRING([--without-readline], [Don't use libreadline or libedit])
+	)
+
+	if test "${with_readline}" '!=' "no"
+	then
+		if test "${with_readline}" '=' "yes"
+		then unset with_readline;
+			readline_required=yes
+		fi
+
+		temp_LIBS="${LIBS}"
+		temp_CPPFLAGS="${CPPFLAGS}"
+		LIBS="${LIBREADLINE_LIBS}"
+		CPPFLAGS="${LIBREADLINE_CPPFLAGS} ${LIBREADLINE_CFLAGS}"
+
+		AC_CHECK_HEADER(
+			[readline/readline.h],
+			[
+				AC_SEARCH_LIBS(waddstr, [ncurses cursesX curses])
+				AC_SEARCH_LIBS(tgetstr, [tinfo])
+				AC_SEARCH_LIBS(
+					[readline],
+					[${with_readline-readline edit}],
+					[with_readline=yes]
+				)
+			]
+		)
+
+		if test "x$with_readline" = "xyes"
+		then
+			LIBREADLINE_LIBS="${LIBS}"
+			LIBREADLINE_CPPFLAGS="${CPPFLAGS}"
+			AC_DEFINE([HAVE_LIBREADLINE], [1], [Define to 1 if we have libreadline or libedit])
+		fi
+
+		CPPFLAGS="${temp_CPPFLAGS}"
+		LIBS="${temp_LIBS}"
+	else
+		LIBREADLINE_LIBS=""
+		LIBREADLINE_CPPFLAGS=""
+	fi
+
+	AC_SUBST(LIBREADLINE_LIBS)
+	AC_SUBST(LIBREADLINE_CPPFLAGS)
+	AM_CONDITIONAL([HAVE_LIBREADLINE],[test "x$with_readline" = "xyes"])
+
+	if test "x$with_readline" = "xyes"
+	then true; $1
+	elif test "x$readline_required" = "xyes"
+	then false; AC_MSG_ERROR(["libreadline or libedit was explicitly requested but was unable to figure out how to use either"])
+	else false; $2
+	fi
+
+	AC_LANG_POP([C])
+])
+
+AC_DEFUN([NL_CHECK_DBUS], [
+	PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.4, [$1], [$2])
+	AC_SUBST(DBUS_CFLAGS)
+	AC_SUBST(DBUS_LIBS)
+
+	AC_ARG_WITH(dbusconfdir,
+		AC_HELP_STRING([--with-dbusconfdir=PATH], [path to D-Bus config directory]),
+		[path_dbusconf=${withval}],
+		[
+			if test "$prefix" = "`$PKG_CONFIG --variable=prefix dbus-1`"
+			then path_dbusconf="`$PKG_CONFIG --variable=sysconfdir dbus-1`"
+			fi
+		]
+	)
+	if (test -z "${path_dbusconf}"); then
+		if test "${prefix}" = "/usr/local" && test "${sysconfdir}" = '${prefix}/etc' && test -d /etc/dbus-1/system.d
+		then DBUS_CONFDIR='/etc/dbus-1/system.d'
+		else DBUS_CONFDIR='${sysconfdir}/dbus-1/system.d'
+		fi
+	else
+		[path_dbusconf="$(echo ${path_dbusconf} | sed 's:^'"${prefix}"':${prefix}:')" ; ]
+		[path_dbusconf="$(echo ${path_dbusconf} | sed 's:^'"${sysconfdir}"':${sysconfdir}:')" ; ]
+		DBUS_CONFDIR="${path_dbusconf}/dbus-1/system.d"
+	fi
+	AC_SUBST(DBUS_CONFDIR)
+
+	AC_ARG_WITH(dbusdatadir, AC_HELP_STRING([--with-dbusdatadir=PATH],
+		[path to D-Bus data directory]), [path_dbusdata=${withval}],
+		[
+			if test "$prefix" = "`$PKG_CONFIG --variable=prefix dbus-1`"
+			then path_dbusdata="`$PKG_CONFIG --variable=datadir dbus-1`"
+			fi
+		]
+	)
+	if (test -z "${path_dbusdata}"); then
+		DBUS_DATADIR='${datadir}/dbus-1/system-services'
+	else
+		[path_dbusconf="$(echo ${path_dbusdata} | sed 's:^'"${prefix}"':${prefix}:')" ; ]
+		[path_dbusconf="$(echo ${path_dbusdata} | sed 's:^'"${datadir}"':${datadir}:')" ; ]
+		DBUS_DATADIR="${path_dbusdata}/dbus-1/system-services"
+	fi
+	AC_SUBST(DBUS_DATADIR)
+])
+
+AC_DEFUN([NL_CHECK_LIBDL], [
+	HAVE_LIBDL=false
+	AC_ARG_WITH(libdl,AC_HELP_STRING([--without-libdl], [Do not use libdl]))
+	if test "${with_libdl-yes}" '!=' 'no'; then :
+		AC_CHECK_HEADER(
+			[dlfcn.h],
+			AC_CHECK_LIB([dl], [dlsym], [
+				HAVE_LIBDL=true
+				LIBDL_LIBS="-ldl"
+			],
+				AC_CHECK_LIB([ltdl], [dlsym], [
+					HAVE_LIBDL=true
+					LIBDL_LIBS="-lltdl"
+				])
+			)
+		)
+	fi
+	AC_SUBST(LIBDL_LIBS)
+])
+
+
+AC_DEFUN([NL_CHECK_CONNMAN], [
+	AC_ARG_WITH(
+		[connman],
+		[AC_HELP_STRING([--without-connman], [Don't build connman plugin])],
+		[
+			if test "x${with_connman}" '=' "xyes"
+			then require_connman=yes
+			fi
+		]
+	)
+
+	if test "x${with_connman}" '!=' "xno"
+	then
+		if test "x${CONNMAN_CFLAGS}" '==' "x"
+		then
+			PKG_CHECK_MODULES(
+				[CONNMAN],
+				[connman >= 1.0],
+				[with_connman=yes],
+				[with_connman=no]
+			)
+		else
+			# CONNMAN_CFLAGS was given manually.
+			prev_CPPFLAGS="${CPPFLAGS}"
+			CPPFLAGS="${CPPFLAGS} ${CONNMAN_CFLAGS}"
+			AC_CHECK_HEADERS([connman/version.h],[with_connman=yes],[with_connman=no])
+			CPPFLAGS="${prev_CPPFLAGS}"
+			unset prev_CPPFLAGS
+
+			if test "x${CONNMAN_LIBS}" == "x"
+			then CONNMAN_LIBS="-module -avoid-version -export-symbols-regex connman_plugin_desc"
+			fi
+		fi
+	fi
+
+	AC_SUBST(CONNMAN_CFLAGS)
+	AC_SUBST(CONNMAN_LIBS)
+
+	if test "x${with_connman}" = "xyes"
+	then {
+		$1
+	}
+	elif test "x$require_connman" = "xyes"
+	then false; AC_MSG_ERROR(["ConnMan plugin was explicitly requested, but can't find ConnMan headers"])
+	else false; $2
+	fi
+])
+
+AC_DEFUN([NL_CHECK_GLIB], [
+	PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.28, [$1], [$2])
+
+	GLIB_CFLAGS="${GLIB_CFLAGS} -DGLIB_VERSION_MAX_ALLOWED=138240 -DGLIB_VERSION_MIN_REQUIRED=138240"
+
+	AC_SUBST(GLIB_CFLAGS)
+	AC_SUBST(GLIB_LIBS)
+])
+
+dnl Unix Pseudoterminal Support
+AC_DEFUN([NL_CHECK_PTS], [
+	AC_CHECK_LIB([util], [forkpty])
+	AC_CHECK_HEADERS([pty.h util.h phy.h])
+	AM_CONDITIONAL([_XOPEN_SOURCE],[true])
+	AC_CHECK_FUNCS([forkpty ptsname], [$1], [$2])
+])
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..fdd975c
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,46 @@
+#
+# Copyright (c) 2016 Nest Labs, Inc.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+SUBDIRS =                \
+	util                 \
+	wpanctl              \
+	ipc-dbus             \
+	connman-plugin       \
+	@PLUGIN_SUBDIRS@     \
+	wpantund             \
+	$(NULL)
+
+DISTCLEANFILES =         \
+	.deps                \
+	Makefile             \
+	$(NULL)
+
+EXTRA_DIST =             \
+	version.c.in         \
+	version.h            \
+	$(NULL)
+
+SOURCE_VERSION=$(shell                                            \
+	git describe --dirty --always --match "[0-9].*" 2> /dev/null  \
+)
+
+CLEANFILES    = $(top_builddir)/$(subdir)/version.c
+BUILT_SOURCES = $(top_builddir)/$(subdir)/version.c
+.INTERMEDIATE:  $(top_builddir)/$(subdir)/version.c
+
+$(top_builddir)/$(subdir)/version.c: version.c.in Makefile
+	sed 's/SOURCE_VERSION/"$(SOURCE_VERSION)"/' < $< > $@
diff --git a/src/connman-plugin/Makefile.am b/src/connman-plugin/Makefile.am
new file mode 100644
index 0000000..e8a5ee1
--- /dev/null
+++ b/src/connman-plugin/Makefile.am
@@ -0,0 +1,50 @@
+#
+# Copyright (c) 2016 Nest Labs, Inc.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src \
+	-I$(top_srcdir)/src/ipc-dbus \
+	-I$(top_srcdir)/src/wpantund \
+	-I$(top_srcdir)/src/util \
+	-I$(top_srcdir)/third_party/assert-macros \
+	$(NULL)
+
+DISTCLEANFILES = .deps Makefile
+
+if BUILD_CONNMAN_PLUGIN
+
+connman_plugindir = ${libdir}/connman/plugins
+
+connman_plugin_LTLIBRARIES = wpan-tunnel-plugin.la
+
+wpan_tunnel_plugin_la_SOURCES = \
+	wpan-connman-plugin.c \
+	../util/string-utils.c \
+	$(NULL)
+
+wpan_tunnel_plugin_la_CFLAGS = \
+	$(CONNMAN_CFLAGS) \
+	$(GLIB_CFLAGS) \
+	$(DBUS_CFLAGS) \
+	$(NULL)
+
+wpan_tunnel_plugin_la_LDFLAGS = \
+	$(CONNMAN_LIBS) \
+	-shared \
+	$(NULL)
+
+endif
diff --git a/src/connman-plugin/wpan-connman-plugin.c b/src/connman-plugin/wpan-connman-plugin.c
new file mode 100644
index 0000000..e273559
--- /dev/null
+++ b/src/connman-plugin/wpan-connman-plugin.c
@@ -0,0 +1,2632 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define ASSERT_MACROS_USE_SYSLOG 1
+#include "assert-macros.h"
+#include <errno.h>
+#include <net/if.h>
+
+#define _GNU_SOURCE 1
+#include <stdio.h>
+
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP    0x10000
+#endif
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/technology.h>
+#include <connman/plugin.h>
+#include <connman/device.h>
+#include <connman/inet.h>
+#include <connman/ipaddress.h>
+#include <connman/log.h>
+#include <connman/dbus.h>
+#include <syslog.h>
+#include <dbus/dbus.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <string.h>
+#include <stdio.h>
+#include <syslog.h>
+#include "wpan-dbus-v0.h"
+#include "wpan-properties.h"
+#include "wpan-error.h"
+#include <stdbool.h>
+#include "string-utils.h"
+
+static struct connman_technology *lowpan_tech;
+static DBusConnection *connection;
+
+//#define DEBUG 1
+
+#define LOWPAN_AUTH_KEY     "WiFi.Passphrase"
+#define LOWPAN_SECURITY_KEY     "WiFi.Security"
+#define LOWPAN_PERMIT_JOINING_KEY   "LoWPAN.PermitJoining"
+#define LOWPAN_PARENT_ADDRESS_KEY   "LoWPAN.ParentAddress"
+
+#if DEBUG
+#undef DBG
+#define DBG(fmt, arg ...) \
+	connman_debug("%s:%d:%s() " fmt, \
+	              "wpan-connman-plugin.c", __LINE__, __FUNCTION__, ## arg);
+#endif
+
+static GHashTable* devices;
+static GHashTable* networks;
+
+// Simplified state representation
+typedef enum {
+	NCP_STATE_UNINITIALIZED,
+	NCP_STATE_UPGRADING,
+	NCP_STATE_OFFLINE,
+	NCP_STATE_COMMISSIONED,
+	NCP_STATE_ASSOCIATING,
+	NCP_STATE_CREDENTIALS_NEEDED,
+	NCP_STATE_ASSOCIATED,
+	NCP_STATE_NET_WAKE_ASLEEP,
+} ncp_state_t;
+
+struct wpan_network_info_s {
+	char network_name[17];
+	dbus_bool_t allowing_join;
+	uint16_t pan_id;
+	int16_t channel;
+	uint64_t xpanid;
+	int8_t rssi;
+	uint8_t hwaddr[8];
+	uint8_t prefix[8];
+};
+
+struct lowpan_device_s {
+	struct wpan_network_info_s current_network_info;
+	struct connman_network*         current_network;
+	uint8_t hwaddr[8];
+	ncp_state_t ncp_state;
+};
+
+struct lowpan_network_s {
+	struct wpan_network_info_s network_info;
+	int16_t node_type;
+};
+
+/* -------------------------------------------------------------------------- */
+// MARK: - Other Helpers
+
+
+static bool
+ncp_state_is_initializing(ncp_state_t ncp_state)
+{
+	switch (ncp_state) {
+	case NCP_STATE_UNINITIALIZED:
+	case NCP_STATE_UPGRADING:
+		return true;
+		break;
+
+	default:
+		break;
+	}
+	return false;
+}
+
+static bool
+ncp_state_is_not_associated(ncp_state_t ncp_state)
+{
+	switch(ncp_state) {
+	case NCP_STATE_OFFLINE:
+	case NCP_STATE_UPGRADING:
+	case NCP_STATE_UNINITIALIZED:
+		return true;
+
+	default:
+		break;
+	}
+	return false;
+}
+
+
+static bool
+ncp_state_is_has_joined(ncp_state_t ncp_state)
+{
+	switch(ncp_state) {
+	case NCP_STATE_ASSOCIATED:
+	case NCP_STATE_NET_WAKE_ASLEEP:
+		return true;
+
+	default:
+		break;
+	}
+	return false;
+}
+
+
+static ncp_state_t
+string_to_ncp_state(const char* new_state, ncp_state_t ncp_state) {
+	DBG("string_to_ncp_state: %s",new_state);
+
+	if(new_state == NULL) {
+		DBG("Bad association state");
+	} else if (0 == strcmp(new_state, kWPANTUNDStateFault)) {
+		ncp_state = NCP_STATE_UNINITIALIZED;
+
+	} else if (0 == strcmp(new_state, kWPANTUNDStateUpgrading)) {
+		ncp_state = NCP_STATE_UPGRADING;
+
+	} else if (0 == strcmp(new_state, kWPANTUNDStateCommissioned)) {
+		ncp_state = NCP_STATE_COMMISSIONED;
+
+	} else if (0 == strcmp(new_state, kWPANTUNDStateCredentialsNeeded)) {
+		ncp_state = NCP_STATE_CREDENTIALS_NEEDED;
+
+	} else if (0 == strcmp(new_state, kWPANTUNDStateNetWake_Asleep)) {
+		ncp_state = NCP_STATE_NET_WAKE_ASLEEP;
+
+	} else if (0 == strcmp(new_state, kWPANTUNDStateIsolated)) {
+		ncp_state = NCP_STATE_ASSOCIATING;
+
+	} else if (0 == strncmp(new_state, kWPANTUNDStateOffline, sizeof(kWPANTUNDStateOffline)-1)) {
+		ncp_state = NCP_STATE_OFFLINE;
+
+	} else if (0 == strncmp(new_state, kWPANTUNDStateAssociating, sizeof(kWPANTUNDStateAssociating)-1)) {
+		ncp_state = NCP_STATE_ASSOCIATING;
+
+	} else if (0 == strncmp(new_state, kWPANTUNDStateAssociated, sizeof(kWPANTUNDStateAssociated)-1)) {
+		ncp_state = NCP_STATE_ASSOCIATED;
+
+	} else if (0 == strncmp(new_state, kWPANTUNDStateUninitialized, sizeof(kWPANTUNDStateUninitialized)-1)) {
+		ncp_state = NCP_STATE_UNINITIALIZED;
+	}
+	return ncp_state;
+}
+
+static int
+encode_data_into_b16_string(
+    const uint8_t*  buffer,
+    size_t len,
+    char*                   c_str,
+    size_t c_str_max_len,
+    int pad_to
+    )
+{
+	int ret = 0;
+
+	while (len && (c_str_max_len > 2)) {
+		uint8_t byte = *buffer++;
+		len--;
+		pad_to--;
+		*c_str++ = int_to_hex_digit(byte >> 4);
+		*c_str++ = int_to_hex_digit(byte & 0xF);
+		c_str_max_len -= 2;
+		ret += 2;
+	}
+
+	while (pad_to > 0 && (c_str_max_len > 2)) {
+		pad_to--;
+		*c_str++ = '0';
+		*c_str++ = '0';
+		c_str_max_len -= 2;
+		ret += 2;
+	}
+
+	*c_str++ = 0;
+	return ret;
+}
+
+static int
+encode_ipv6_address_from_prefx_and_hwaddr(
+    char*                   c_str,
+    size_t c_str_max_len,
+    const uint8_t prefix[8],
+    const uint8_t hwaddr[8]
+    )
+{
+	return snprintf(c_str, c_str_max_len,
+	                "%02X%02X:%02X%02X:%02X%02X:%02X%02X:"
+	                "%02X%02X:%02X%02X:%02X%02X:%02X%02X",
+	                prefix[0], prefix[1], prefix[2], prefix[3],
+	                prefix[4], prefix[5], prefix[6], prefix[7],
+	                hwaddr[0] ^ 0x02, hwaddr[1], hwaddr[2], hwaddr[3],
+	                hwaddr[4], hwaddr[5], hwaddr[6], hwaddr[7]
+	                );
+}
+
+/* -------------------------------------------------------------------------- */
+// MARK: - Static Declarations
+
+static void lowpan_device_leave(struct connman_device *device);
+static int lowpan_driver_setprop_data(struct connman_device *device, const char* key, const uint8_t* data, size_t size);
+static int lowpan_driver_getprop_data(struct connman_device *device, const char* key, void(*callback)(void* context, int error, const uint8_t* data, size_t len), void* context);
+
+static void lowpan_device_set_network(struct connman_device *device, struct connman_network *network);
+
+static int lowpan_device_update_status(struct connman_device *device);
+
+
+/* -------------------------------------------------------------------------- */
+// MARK: - DBus Helpers
+
+uint8_t
+CalculateStrengthFromRSSI(int8_t rssi)
+{
+	uint8_t ret = 0;
+
+	if (rssi > -120) {
+		ret = 120 + rssi;
+	}
+
+	if (ret > 100) {
+		ret = 100;
+	}
+	return ret;
+}
+
+static void
+dump_info_from_iter(
+    FILE* file, DBusMessageIter *iter, int indent, bool bare
+    )
+{
+	DBusMessageIter sub_iter;
+	int i;
+
+	if (!bare) for (i = 0; i < indent; i++) fprintf(file, "\t");
+
+	switch (dbus_message_iter_get_arg_type(iter)) {
+	case DBUS_TYPE_DICT_ENTRY:
+		dbus_message_iter_recurse(iter, &sub_iter);
+		dump_info_from_iter(file, &sub_iter, indent + 1, true);
+		fprintf(file, " => ");
+		dbus_message_iter_next(&sub_iter);
+		dump_info_from_iter(file, &sub_iter, indent + 1, true);
+		break;
+	case DBUS_TYPE_ARRAY:
+		dbus_message_iter_recurse(iter, &sub_iter);
+		if (dbus_message_iter_get_arg_type(&sub_iter) == DBUS_TYPE_BYTE ||
+		    dbus_message_iter_get_arg_type(&sub_iter) == DBUS_TYPE_INVALID) {
+			fprintf(file, "[");
+			indent = 0;
+		} else {
+			fprintf(file, "[\n");
+		}
+
+		for (;
+		     dbus_message_iter_get_arg_type(&sub_iter) != DBUS_TYPE_INVALID;
+		     dbus_message_iter_next(&sub_iter)) {
+			dump_info_from_iter(file,
+			                    &sub_iter,
+			                    indent + 1,
+			                    dbus_message_iter_get_arg_type(&sub_iter) == DBUS_TYPE_BYTE);
+		}
+		for (i = 0; i < indent; i++) fprintf(file, "\t");
+		fprintf(file, "]");
+
+		break;
+	case DBUS_TYPE_VARIANT:
+		dbus_message_iter_recurse(iter, &sub_iter);
+		dump_info_from_iter(file, &sub_iter, indent, true);
+		break;
+	case DBUS_TYPE_STRING:
+	{
+		const char* string;
+		dbus_message_iter_get_basic(iter, &string);
+		fprintf(file, "\"%s\"", string);
+	}
+	break;
+
+	case DBUS_TYPE_BYTE:
+	{
+		uint8_t v;
+		dbus_message_iter_get_basic(iter, &v);
+		fprintf(file, "%02X", v);
+	}
+	break;
+	case DBUS_TYPE_UINT16:
+	{
+		uint16_t v;
+		dbus_message_iter_get_basic(iter, &v);
+		fprintf(file, "0x%04X", v);
+	}
+	break;
+	case DBUS_TYPE_INT16:
+	{
+		int16_t v;
+		dbus_message_iter_get_basic(iter, &v);
+		fprintf(file, "%d", v);
+	}
+	break;
+	case DBUS_TYPE_UINT32:
+	{
+		uint32_t v;
+		dbus_message_iter_get_basic(iter, &v);
+		fprintf(file, "%d", v);
+	}
+	break;
+	case DBUS_TYPE_BOOLEAN:
+	{
+		dbus_bool_t v;
+		dbus_message_iter_get_basic(iter, &v);
+		fprintf(file, "%s", v ? "true" : "false");
+	}
+	break;
+	case DBUS_TYPE_INT32:
+	{
+		int32_t v;
+		dbus_message_iter_get_basic(iter, &v);
+		fprintf(file, "%d", v);
+	}
+	break;
+	case DBUS_TYPE_UINT64:
+	{
+		uint64_t v;
+		dbus_message_iter_get_basic(iter, &v);
+		fprintf(file, "0x%016llX", (unsigned long long)v);
+	}
+	break;
+	default:
+		fprintf(file, "<%s>",
+		        dbus_message_type_to_string(dbus_message_iter_get_arg_type(iter)));
+		break;
+	}
+	if (!bare)
+		fprintf(file, "\n");
+}
+
+static int
+parse_network_info_from_iter(
+    struct wpan_network_info_s *network_info, DBusMessageIter *iter
+    )
+{
+	int ret = 0;
+	DBusMessageIter outer_iter;
+	DBusMessageIter dict_iter;
+
+	if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY) {
+		dbus_message_iter_recurse(iter, &outer_iter);
+		iter = &outer_iter;
+	}
+
+	memset(network_info, 0, sizeof(*network_info));
+
+	for (;
+	     dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INVALID;
+	     dbus_message_iter_next(iter)) {
+		DBusMessageIter value_iter;
+		char* key;
+
+		if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) {
+			DBG(
+			    "error: Bad type for network (%c)\n",
+			    dbus_message_iter_get_arg_type(iter));
+			ret = -1;
+			goto bail;
+		}
+
+		dbus_message_iter_recurse(iter, &dict_iter);
+
+		if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) {
+			DBG(
+			    "error: Bad type for network list (%c)\n",
+			    dbus_message_iter_get_arg_type(&dict_iter));
+			ret = -1;
+			goto bail;
+		}
+
+		// Get the key
+		dbus_message_iter_get_basic(&dict_iter, &key);
+		dbus_message_iter_next(&dict_iter);
+
+		if (key == NULL) {
+			ret = -EINVAL;
+			goto bail;
+
+		}
+
+		if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_VARIANT) {
+			DBG(
+			    "error: Bad type for network list (%c)\n",
+			    dbus_message_iter_get_arg_type(&dict_iter));
+			ret = -1;
+			goto bail;
+		}
+
+		dbus_message_iter_recurse(&dict_iter, &value_iter);
+
+		if (strcmp(key, kWPANTUNDProperty_NetworkName) == 0 || strcmp(key, "NetworkName") == 0) {
+			char* network_name = NULL;
+			dbus_message_iter_get_basic(&value_iter, &network_name);
+			snprintf(network_info->network_name,
+			         sizeof(network_info->network_name), "%s", network_name);
+		} else if ((strcmp(key, kWPANTUNDProperty_NCPChannel) == 0) || strcmp(key, "Channel") == 0) {
+			dbus_message_iter_get_basic(&value_iter, &network_info->channel);
+		} else if ((strcmp(key, kWPANTUNDProperty_NetworkPANID) == 0) || strcmp(key, "PanId") == 0) {
+			dbus_message_iter_get_basic(&value_iter, &network_info->pan_id);
+		} else if ((strcmp(key, kWPANTUNDProperty_NetworkXPANID) == 0) || strcmp(key, "XPanId") == 0) {
+			dbus_message_iter_get_basic(&value_iter, &network_info->xpanid);
+		} else if ((strcmp(key, kWPANTUNDProperty_NestLabs_NetworkAllowingJoin) == 0) || strcmp(key, "AllowingJoin") == 0) {
+			dbus_message_iter_get_basic(&value_iter,
+			                            &network_info->allowing_join);
+		} else if ((strcmp(key, kWPANTUNDProperty_NCPHardwareAddress) == 0) || strcmp(key, "BeaconHWAddr") == 0) {
+			DBusMessageIter array_iter;
+			dbus_message_iter_recurse(&value_iter, &array_iter);
+			const uint8_t* value = NULL;
+			int nelements = 0;
+			dbus_message_iter_get_fixed_array(&array_iter, &value, &nelements);
+			if (value != NULL && nelements == 8)
+				memcpy(network_info->hwaddr, value, 8);
+		} else if (strcmp(key, kWPANTUNDProperty_IPv6MeshLocalPrefix) == 0) {
+			DBusMessageIter array_iter;
+			dbus_message_iter_recurse(&value_iter, &array_iter);
+			const uint8_t* value = NULL;
+			int nelements = 0;
+			dbus_message_iter_get_fixed_array(&array_iter, &value, &nelements);
+			if (value != NULL && nelements == 8) {
+				memcpy(network_info->prefix, value, 8);
+			}
+		} else if (strcmp(key, "RSSI") == 0) {
+			int8_t rssi;
+			dbus_message_iter_get_basic(&value_iter, &rssi);
+			network_info->rssi = rssi;
+		} else {
+#if DEBUG
+			DBG(
+			    "info: %s -> (%c)\n",
+			    key,
+			    dbus_message_iter_get_arg_type(&value_iter));
+#endif
+		}
+	}
+
+bail:
+	if (ret) DBG("Network parse failed.\n");
+	return ret;
+}
+
+/* -------------------------------------------------------------------------- */
+// MARK: - LoWPAN Network
+
+static struct connman_network*
+get_network_from_iter(
+    struct connman_device* device, DBusMessageIter *iter
+    )
+{
+	const char* new_state = NULL;
+	struct connman_network *network = NULL;
+	struct wpan_network_info_s network_info = {};
+	char network_identifier[256];
+	const char * group_identifier = network_identifier;
+	char hwaddr_str[256];
+	struct lowpan_network_s *network_data = NULL;
+
+	if (parse_network_info_from_iter(&network_info, iter) != 0) {
+		goto bail;
+	}
+
+	if (!network_info.network_name[0]) {
+		goto bail;
+	}
+
+	if (network_info.xpanid == 0) {
+		goto bail;
+	}
+
+	encode_data_into_b16_string(
+	    (const uint8_t*)network_info.network_name,
+	    strlen(network_info.network_name),
+	    network_identifier,
+	    sizeof(network_identifier),
+	    0
+    );
+
+	encode_data_into_b16_string(
+	    (const uint8_t*)network_info.hwaddr,
+	    8,
+	    hwaddr_str,
+	    sizeof(hwaddr_str),
+	    0
+    );
+
+	snprintf(network_identifier + strlen(network_identifier),
+	         sizeof(network_identifier) - strlen(
+	             network_identifier), "_x%016llX", (unsigned long long)network_info.xpanid);
+
+	network = connman_device_get_network(device, network_identifier);
+
+	if (!network) {
+		network = connman_network_create(
+		    network_identifier,
+		    CONNMAN_NETWORK_TYPE_LOWPAN
+	    );
+		network_data = (struct lowpan_network_s *)calloc(1,sizeof(struct lowpan_network_s));
+		network_data->network_info = network_info;
+		network_data->node_type = WPAN_IFACE_ROLE_ROUTER;
+		connman_network_set_data(network, network_data);
+		connman_network_set_string(network, LOWPAN_SECURITY_KEY, "psk");
+
+		connman_device_add_network(device, network);
+
+		connman_network_unref(network);
+
+		connman_network_set_strength(network,
+		                             CalculateStrengthFromRSSI(network_info.rssi));
+		connman_network_set_name(network, network_info.network_name);
+
+		// Set network extended ID before a service is created from network
+		// since this information is needed when service loads provision.
+		connman_network_set_lowpan_xpan_id(network, network_data->network_info.xpanid);
+
+		connman_network_set_group(network, group_identifier);
+		connman_network_set_index(network, -1);
+
+		DBG("New Network: %p ident:%s group:%s", network, network_identifier, group_identifier);
+	} else {
+		network_data = connman_network_get_data(network);
+		network_data->network_info = network_info;
+	}
+
+	connman_network_set_bool(network, LOWPAN_PERMIT_JOINING_KEY, network_data->network_info.allowing_join);
+	connman_network_set_string(network, LOWPAN_PARENT_ADDRESS_KEY, hwaddr_str);
+
+bail:
+
+	DBG("%p NetworkFromIter: %p", device, network);
+	return network;
+}
+
+
+static void
+network_unref_callback(void* user_data)
+{
+	struct connman_network *network = (struct connman_network *)user_data;
+
+	connman_network_unref(network);
+}
+
+static int
+lowpan_network_probe(struct connman_network *network)
+{
+	DBG("%p %s", network, connman_network_get_identifier(network));
+
+	return 0;
+}
+
+static void
+lowpan_network_remove(struct connman_network *network)
+{
+	DBG("%p %s", network, connman_network_get_identifier(network));
+
+	struct lowpan_network_s *network_data =
+	    (struct lowpan_network_s *)connman_network_get_data(network);
+
+	if(network_data) {
+		connman_network_set_name(network, "X");
+		connman_network_set_group(network, "X");
+
+		free(network_data);
+		connman_network_set_data(network, NULL);
+	}
+
+}
+
+static void
+join_finished_callback(
+    DBusPendingCall *pending, void* user_data
+    )
+{
+	int32_t ret = 0;
+	struct connman_network *network = (struct connman_network *)user_data;
+	struct connman_device* device = NULL;
+	struct lowpan_device_s *device_info = NULL;
+	DBusMessage* reply = dbus_pending_call_steal_reply(pending);
+	DBusMessageIter iter;
+	DBusMessageIter list_iter;
+
+	// network will never be NULL in this callback.
+
+	DBG("%p %s", network, connman_network_get_identifier(network));
+
+	device = connman_network_get_device(network);
+
+	require_action(device != NULL, bail, ret = -ENODEV);
+
+	device_info = (struct lowpan_device_s *)connman_device_get_data(device);
+
+	require_action(device_info != NULL, bail, ret = -ENODEV);
+	require_action(reply != NULL, bail, ret = -EINVAL);
+
+	dbus_message_iter_init(reply, &iter);
+
+	// Get return code
+	dbus_message_iter_get_basic(&iter, &ret);
+
+bail:
+	if (reply) {
+		dbus_message_unref(reply);
+	}
+	if (pending) {
+		dbus_pending_call_unref(pending);
+	}
+
+	if ((ret != 0) && (ret != -EINPROGRESS) && (ret != kWPANTUNDStatus_InProgress)) {
+		DBG("%p Join/Resume returned failure: %d", network, ret);
+
+		if((device_info != NULL) && ncp_state_is_has_joined(device_info->ncp_state)) {
+			DBG("%p ... But we seem to have connected anyway. Ignoring the error.", network);
+			ret = 0;
+		} else {
+			connman_network_set_error(network, CONNMAN_NETWORK_ERROR_INVALID_KEY);
+		}
+	}
+
+	if (ret == 0) {
+		connman_network_set_connected(network, TRUE);
+	}
+
+	return;
+}
+
+static int
+lowpan_network_connect_using_join(struct connman_network *network)
+{
+	int ret = -EINVAL;
+	DBG("%p %s", network, connman_network_get_identifier(network));
+	struct connman_device* device = connman_network_get_device(network);
+	DBusPendingCall *pending_call = NULL;
+	DBusMessage *message = NULL;
+	char dbus_path[DBUS_MAXIMUM_NAME_LENGTH];
+	struct lowpan_network_s *network_data =
+	    (struct lowpan_network_s *)connman_network_get_data(network);
+
+	require_action(device != NULL, bail, ret = -ENODEV);
+
+	require_action(network_data != NULL, bail, ret = -ENODEV);
+
+	struct lowpan_device_s *device_info =
+	    (struct lowpan_device_s *)connman_device_get_data(device);
+
+	require_action(device_info != NULL, bail, ret = -ENODEV);
+
+	snprintf(dbus_path,
+	         sizeof(dbus_path),
+	         "%s/%s",
+	         WPAN_TUNNEL_DBUS_PATH,
+	         connman_device_get_ident(device));
+
+	message = dbus_message_new_method_call(
+	    WPAN_TUNNEL_DBUS_NAME,
+	    dbus_path,
+	    WPAN_TUNNEL_DBUS_INTERFACE,
+	    WPAN_IFACE_CMD_JOIN
+	    );
+	const char* network_name = network_data->network_info.network_name;
+	dbus_message_append_args(
+	    message,
+	    DBUS_TYPE_STRING, &network_name,
+	    DBUS_TYPE_INVALID
+	    );
+
+	dbus_message_append_args(
+	    message,
+	    DBUS_TYPE_INT16, &network_data->node_type,
+	    DBUS_TYPE_INVALID
+	    );
+
+	dbus_message_append_args(
+	    message,
+	    DBUS_TYPE_UINT64, &network_data->network_info.xpanid,
+	    DBUS_TYPE_INVALID
+	    );
+
+	dbus_message_append_args(
+	    message,
+	    DBUS_TYPE_UINT16, &network_data->network_info.pan_id,
+	    DBUS_TYPE_INVALID
+	    );
+
+	dbus_message_append_args(
+	    message,
+	    DBUS_TYPE_BYTE, &network_data->network_info.channel,
+	    DBUS_TYPE_INVALID
+	    );
+
+
+	if (!dbus_connection_send_with_reply(
+	        connection,
+	        message,
+	        &pending_call,
+	        45000
+	        )
+	    ) {
+		ret = -EINVAL;
+		goto bail;
+	}
+
+	connman_network_ref(network);
+
+	if (!dbus_pending_call_set_notify(
+	        pending_call,
+	        &join_finished_callback,
+	        (void*)network,
+	        (DBusFreeFunction) & network_unref_callback
+	        )
+	    ) {
+		dbus_pending_call_cancel(pending_call);
+		connman_network_unref(network);
+		ret = -EINVAL;
+		goto bail;
+	}
+
+	ret = -EINPROGRESS;
+
+
+bail:
+	if(message)
+		dbus_message_unref(message);
+	return ret;
+}
+
+static int
+lowpan_network_connect_using_resume(struct connman_network *network)
+{
+	int ret = -EINVAL;
+
+	DBG("%p %s", network, connman_network_get_identifier(network));
+	struct connman_device* device = connman_network_get_device(network);
+	DBusPendingCall *pending_call = NULL;
+	DBusMessage *message = NULL;
+	char dbus_path[DBUS_MAXIMUM_NAME_LENGTH];
+
+	require_action(device != NULL, bail, ret = -ENODEV);
+
+	struct lowpan_device_s *device_info =
+	    (struct lowpan_device_s *)connman_device_get_data(device);
+
+	require_action(device_info != NULL, bail, ret = -ENODEV);
+
+	snprintf(dbus_path,
+	         sizeof(dbus_path),
+	         "%s/%s",
+	         WPAN_TUNNEL_DBUS_PATH,
+	         connman_device_get_ident(device));
+
+	message = dbus_message_new_method_call(
+	    WPAN_TUNNEL_DBUS_NAME,
+	    dbus_path,
+	    WPAN_TUNNEL_DBUS_INTERFACE,
+	    WPAN_IFACE_CMD_RESUME
+	    );
+
+	if (!dbus_connection_send_with_reply(
+	        connection,
+	        message,
+	        &pending_call,
+	        30000 // Thirty seconds
+	        )) {
+		ret = -1;
+		goto bail;
+	}
+
+	connman_network_ref(network);
+
+	if (!dbus_pending_call_set_notify(
+	        pending_call,
+	        &join_finished_callback,
+	        (void*)network,
+	        (DBusFreeFunction) & network_unref_callback
+	        )
+	    ) {
+		dbus_pending_call_cancel(pending_call);
+		connman_network_unref(network);
+		ret = -EINVAL;
+		return -1;
+	}
+
+	DBG("Now waiting for resume to complete...");
+
+	ret = -EINPROGRESS;
+
+bail:
+	if(message)
+		dbus_message_unref(message);
+	return ret;
+}
+
+static void
+lowpan_network_update_key(struct connman_network *network, const uint8_t* data, size_t len)
+{
+	char key_cstr[16*2+1];
+
+	DBG("%p %s", network, connman_network_get_identifier(network));
+
+	if (len != 0) {
+		encode_data_into_b16_string(data, len, key_cstr, sizeof(key_cstr), 0);
+		connman_network_set_string(network, LOWPAN_AUTH_KEY, key_cstr);
+		connman_network_update(network);
+	}
+}
+
+static void
+_lowpan_network_update_key_from_ncp_callback(void* context, int error, const uint8_t* data, size_t len)
+{
+	struct connman_network *network = context;
+	DBG("%p %s", network, connman_network_get_identifier(network));
+	if (error == 0) {
+		lowpan_network_update_key(network, data, len);
+	}
+
+	struct connman_device* device = connman_network_get_device(network);
+
+	if (!device) {
+		DBG("NO DEVICE!");
+		return;
+	}
+
+	struct lowpan_device_s *device_info =
+	    (struct lowpan_device_s *)connman_device_get_data(device);
+
+	if (!device_info) {
+		DBG("NO DEVICE INFO!");
+		return;
+	}
+
+	if (ncp_state_is_has_joined(device_info->ncp_state)
+		&& !connman_network_get_connected(network)
+		&& !connman_network_get_connecting(network)
+		&& !connman_network_get_associating(network)
+	) {
+		DBG("We got the network key, asking connman to connect...");
+		connman_service_connect(connman_service_lookup_from_network(network),
+                CONNMAN_SERVICE_CONNECT_REASON_USER);
+	}
+
+	connman_network_unref(network);
+}
+
+static int
+lowpan_network_update_key_from_ncp(struct connman_network *network)
+{
+	int ret = 0;
+	struct connman_device* device = connman_network_get_device(network);
+	if (!device) {
+		ret = -ENODEV;
+		goto bail;
+	}
+
+	DBG("%p %s", network, connman_network_get_identifier(network));
+
+	connman_network_ref(network);
+
+	ret = lowpan_driver_getprop_data(
+		device,
+		kWPANTUNDProperty_NetworkKey,
+		&_lowpan_network_update_key_from_ncp_callback,
+		network
+	);
+
+	if(ret != 0)
+		goto bail;
+
+bail:
+	return ret;
+}
+
+static int
+lowpan_network_set_key_on_ncp(struct connman_network *network)
+{
+	int ret = 0;
+	struct connman_device* device = connman_network_get_device(network);
+	DBG("%p %s", network, connman_network_get_identifier(network));
+	if (!device) {
+		ret = -ENODEV;
+		goto bail;
+	}
+	uint8_t key[16];
+	unsigned int size = sizeof(key);
+	const char* key_cstr = connman_network_get_string(network, LOWPAN_AUTH_KEY);
+
+	if (key_cstr) {
+		size = parse_string_into_data(key, size, key_cstr);
+
+		if (size > sizeof(key)) {
+			connman_warn("Key is too large: %d (max %d)", size, (int)sizeof(key));
+			ret = -EINVAL;
+		} else if (sizeof(key) != size) {
+			connman_warn("Key-size mismatch (Expecting %d, but key was %d bytes long)", (int)sizeof(key), size);
+		}
+	} else {
+#if 0
+		size = 16;
+		memset(key,0,sizeof(key));
+		size = sizeof(key);
+		connman_warn("No valid key specified for %s, using all-zeros...", connman_network_get_identifier(network));
+#else
+		ret = -ENOKEY;
+		DBG("No key to set!");
+#endif
+	}
+
+	if (ret == 0) {
+		DBG("Setting the key at %p for the service...", key);
+		ret = lowpan_driver_setprop_data(device, kWPANTUNDProperty_NetworkKey, key, size);
+	}
+
+bail:
+	return ret;
+}
+
+static int
+lowpan_network_connect(struct connman_network *network)
+{
+	int ret = -EINVAL;
+	struct connman_device* device = connman_network_get_device(network);
+
+	DBG("%p %s", network, connman_network_get_identifier(network));
+
+	if (!device) {
+		ret = -ENODEV;
+		goto bail;
+	}
+
+	struct lowpan_device_s *device_info =
+	    (struct lowpan_device_s *)connman_device_get_data(device);
+
+	if (!device_info) {
+		ret = -ENODEV;
+		goto bail;
+	}
+
+	if (device_info->ncp_state == NCP_STATE_UPGRADING) {
+		// We can't connect while upgrading.
+		ret = -EBUSY;
+		goto bail;
+	} else if (ncp_state_is_not_associated(device_info->ncp_state)) {
+		lowpan_device_set_network(device, network);
+		ret = lowpan_network_connect_using_join(network);
+	} else if (device_info->ncp_state == NCP_STATE_COMMISSIONED) {
+		if (device_info->current_network == network) {
+			// Do not auto-resume, this is performed automatically by wpantund.
+			// TODO: Check if Autoresume is enabled and allow this if not.
+			// ret = lowpan_network_connect_using_resume(network);
+
+			// Since we know Autoresume is on, we return -EINPROGRESS.
+			ret = -EINPROGRESS;
+		} else {
+			ret = -EBUSY;
+		}
+	} else if (device_info->ncp_state == NCP_STATE_CREDENTIALS_NEEDED) {
+		if (device_info->current_network == network) {
+			ret = lowpan_network_set_key_on_ncp(network);
+
+			if (-ENOKEY == ret) {
+				ret = connman_network_needs_input(network);
+				if (0 != ret) {
+					DBG("connman_network_needs_input(network) failed with %d", ret);
+				}
+			}
+
+			if ((0 != ret) && (-EINPROGRESS != ret)) {
+				connman_network_set_associating(network, false);
+				lowpan_device_leave(device);
+			}
+
+			if (0 == ret) {
+				ret = -EINPROGRESS;
+			}
+		} else {
+			DBG("%p Aborting connection in progress", network);
+			lowpan_device_leave(device);
+			ret = -EAGAIN;
+		}
+	} else if (device_info->ncp_state == NCP_STATE_ASSOCIATING) {
+		if (device_info->current_network == network) {
+			DBG("%p Already connecting to THIS network!", network);
+			ret = -EINPROGRESS;
+		} else {
+			DBG("%p Already connecting to a different network!", network);
+			ret = -EINVAL;
+		}
+	} else if (ncp_state_is_has_joined(device_info->ncp_state)) {
+		if (device_info->current_network == network) {
+			DBG("%p Already connected to THIS network!", network);
+			ret = 0;
+			connman_network_set_connected(network, TRUE);
+		} else if(device_info->current_network && 0 == strcmp(connman_network_get_group(device_info->current_network),connman_network_get_group(network))) {
+			DBG("%p Already connected to THIS service!", network);
+			ret = 0;
+			connman_network_unref(device_info->current_network);
+			device_info->current_network = network;
+			connman_network_ref(device_info->current_network);
+			connman_network_set_connected(network, TRUE);
+		} else {
+			DBG("%p Already connected to an entirely different network, %s!", network, connman_network_get_identifier(device_info->current_network));
+			ret = -EINVAL;
+		}
+	}
+
+bail:
+	DBG("%p ret=%d", network, ret);
+	return ret;
+}
+
+static void
+disconnect_finished_callback(
+    DBusPendingCall *pending, void* user_data
+    )
+{
+}
+
+
+void
+lowpan_device_leave(struct connman_device *device)
+{
+	DBusMessage *message = NULL;
+	char dbus_path[DBUS_MAXIMUM_NAME_LENGTH];
+
+	snprintf(dbus_path,
+	         sizeof(dbus_path),
+	         "%s/%s",
+	         WPAN_TUNNEL_DBUS_PATH,
+	         connman_device_get_ident(device));
+
+	message = dbus_message_new_method_call(
+	    WPAN_TUNNEL_DBUS_NAME,
+	    dbus_path,
+	    WPAN_TUNNEL_DBUS_INTERFACE,
+	    WPAN_IFACE_CMD_LEAVE
+	    );
+
+	dbus_connection_send(connection, message, NULL);
+
+	if(message)
+		dbus_message_unref(message);
+}
+
+static int
+lowpan_network_disconnect(struct connman_network *network, bool user_initiated)
+{
+	int ret = -EINVAL;
+	struct connman_device* device = connman_network_get_device(network);
+	struct lowpan_device_s *device_info =
+	    (struct lowpan_device_s *)connman_device_get_data(device);
+
+	DBG("%p %s", network, connman_network_get_identifier(network));
+
+	if (!device) {
+		ret = -ENODEV;
+		goto bail;
+	}
+
+	if (!device_info) {
+		ret = -ENODEV;
+		goto bail;
+	}
+
+	if (device_info->current_network != network) {
+		ret = -EINVAL;
+		goto bail;
+	}
+
+	connman_network_set_connected(network, FALSE);
+
+	switch(device_info->ncp_state) {
+	case NCP_STATE_ASSOCIATING:
+	case NCP_STATE_CREDENTIALS_NEEDED:
+	case NCP_STATE_OFFLINE:
+		user_initiated = TRUE;
+		break;
+	default:
+		break;
+	}
+
+	if (user_initiated) {
+		lowpan_device_leave(device);
+	}
+
+	ret = 0;
+
+bail:
+	DBG("%p ret=%d", network, ret);
+	return ret;
+}
+
+int
+lowpan_driver_setprop_int32(struct connman_device *device, const char* key, int32_t value)
+{
+	int ret = 0;
+	DBusMessage *message = NULL;
+	char dbus_path[128];
+
+	snprintf(dbus_path,
+	         sizeof(dbus_path),
+	         "%s/%s",
+	         WPAN_TUNNEL_DBUS_PATH,
+	         connman_device_get_ident(device));
+
+	message = dbus_message_new_method_call(
+	    WPAN_TUNNEL_DBUS_NAME,
+	    dbus_path,
+	    WPAN_TUNNEL_DBUS_INTERFACE,
+	    WPAN_IFACE_CMD_SET_PROP
+	    );
+
+	dbus_message_append_args(
+	    message,
+	    DBUS_TYPE_STRING, &key,
+	    DBUS_TYPE_INT32, &value,
+	    DBUS_TYPE_INVALID
+	    );
+
+	if (!dbus_connection_send(
+	        connection,
+	        message,
+	        NULL)) {
+		dbus_message_unref(message);
+		ret = -ENOMEM;
+		goto bail;
+	}
+
+bail:
+	if(message)
+		dbus_message_unref(message);
+	return ret;
+}
+
+int
+lowpan_driver_setprop_data(struct connman_device *device, const char* key, const uint8_t* data, size_t size)
+{
+	int ret = 0;
+	DBusMessage *message = NULL;
+	char dbus_path[128];
+
+	snprintf(dbus_path,
+	         sizeof(dbus_path),
+	         "%s/%s",
+	         WPAN_TUNNEL_DBUS_PATH,
+	         connman_device_get_ident(device));
+
+	message = dbus_message_new_method_call(
+	    WPAN_TUNNEL_DBUS_NAME,
+	    dbus_path,
+	    WPAN_TUNNEL_DBUS_INTERFACE,
+	    WPAN_IFACE_CMD_SET_PROP
+	    );
+
+	dbus_message_append_args(
+	    message,
+	    DBUS_TYPE_STRING, &key,
+	    DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &data, size,
+	    DBUS_TYPE_INVALID
+	    );
+
+	if (!dbus_connection_send(
+	        connection,
+	        message,
+	        NULL)) {
+		dbus_message_unref(message);
+		ret = -ENOMEM;
+		goto bail;
+	}
+
+bail:
+	if(message)
+		dbus_message_unref(message);
+	return ret;
+}
+
+struct lowpan_driver_getprop_state_s {
+	void(*callback)(void* context, int err, const uint8_t* data, size_t len);
+	void* context;
+	struct connman_device* device;
+};
+
+static void
+_lowpan_driver_getprop_data_free(void* user_data)
+{
+	struct lowpan_driver_getprop_state_s* state = user_data;
+	DBG("%p device:%p", state, state->device);
+//	DBG("%p %s", state->device, connman_device_get_ident(state->device));
+
+	if (NULL != state->callback) {
+		(*state->callback)(state->context, -1, NULL, 0);
+		state->callback = NULL;
+	}
+	if(state->device)
+		connman_device_unref(state->device);
+	free(state);
+}
+
+static void
+_lowpan_driver_getprop_data_callback(
+    DBusPendingCall *pending, void* user_data
+    )
+{
+	int32_t ret = 0;
+	struct lowpan_driver_getprop_state_s* state = user_data;
+	DBusMessage* reply = dbus_pending_call_steal_reply(pending);
+	DBusMessageIter iter;
+	const uint8_t* value = NULL;
+	int nelements = 0;
+
+	if (!state) {
+		goto bail;
+	}
+
+	DBG("%p device:%p", state, state->device);
+//	DBG("%p %s", state->device, connman_device_get_ident(state->device));
+
+	if (!reply) {
+		DBG("No reply...?");
+		goto bail;
+	}
+
+	dbus_message_iter_init(reply, &iter);
+
+	// Get return code
+	dbus_message_iter_get_basic(&iter, &ret);
+	dbus_message_iter_next(&iter);
+
+	if (ret == 0) {
+		DBusMessageIter array_iter;
+		dbus_message_iter_recurse(&iter, &array_iter);
+		dbus_message_iter_get_fixed_array(&array_iter, &value, &nelements);
+	}
+
+	if (NULL != state->callback) {
+		(*state->callback)(state->context, ret, value, nelements);
+		state->callback = NULL;
+	}
+
+bail:
+	if (reply) {
+		dbus_message_unref(reply);
+	}
+	if (pending) {
+		dbus_pending_call_unref(pending);
+	}
+
+	DBG("%p ret = %d", state->device, ret);
+	return;
+}
+
+int
+lowpan_driver_getprop_data(struct connman_device *device, const char* key, void(*callback)(void* context, int err, const uint8_t* data, size_t len), void* context)
+{
+	int ret = 0;
+	DBusMessage *message = NULL;
+	DBusPendingCall *pending_call = NULL;
+	struct lowpan_driver_getprop_state_s* state = NULL;
+	char dbus_path[128];
+
+	DBG("%p %s key:%s", device, connman_device_get_ident(device),key);
+
+	snprintf(dbus_path,
+	         sizeof(dbus_path),
+	         "%s/%s",
+	         WPAN_TUNNEL_DBUS_PATH,
+	         connman_device_get_ident(device));
+
+	message = dbus_message_new_method_call(
+	    WPAN_TUNNEL_DBUS_NAME,
+	    dbus_path,
+	    WPAN_TUNNEL_DBUS_INTERFACE,
+	    WPAN_IFACE_CMD_GET_PROP
+	    );
+
+	state = calloc(sizeof(*state),1);
+	if(!state) {
+		ret = -ENOMEM;
+		goto bail;
+	}
+	state->context = context;
+	state->callback = callback;
+	state->device = device;
+	connman_device_ref(device);
+
+	dbus_message_append_args(
+	    message,
+	    DBUS_TYPE_STRING, &key,
+	    DBUS_TYPE_INVALID
+	    );
+
+	if (!dbus_connection_send_with_reply(
+	        connection,
+	        message,
+	        &pending_call,
+	        10000 // Ten seconds
+	        )
+	) {
+		ret = -ENOMEM;
+		goto bail;
+	}
+
+	if (!dbus_pending_call_set_notify(
+	        pending_call,
+	        &_lowpan_driver_getprop_data_callback,
+	        (void*)state,
+	        &_lowpan_driver_getprop_data_free
+		)
+	) {
+		ret = -EINVAL;
+		goto bail;
+	}
+
+	DBG("state:%p device:%p ret = %d",state, state->device, ret);
+
+	pending_call = NULL;
+	state = NULL;
+
+bail:
+	DBG("device:%p ret = %d",device, ret);
+
+	if(state)
+		_lowpan_driver_getprop_data_free((void*)state);
+	if(message)
+		dbus_message_unref(message);
+	if(pending_call)
+		dbus_pending_call_cancel(pending_call);
+
+	return ret;
+}
+
+
+static struct connman_network_driver lowpan_network_driver = {
+	.name           = "lowpan",
+	.type           = CONNMAN_NETWORK_TYPE_LOWPAN,
+	.priority       = CONNMAN_NETWORK_PRIORITY_LOW,
+	.probe          = lowpan_network_probe,
+	.remove         = lowpan_network_remove,
+	.connect        = lowpan_network_connect,
+	.disconnect = lowpan_network_disconnect,
+};
+
+/* -------------------------------------------------------------------------- */
+// MARK: - LoWPAN Device
+
+void
+lowpan_device_set_network(struct connman_device *device, struct connman_network *network)
+{
+	struct lowpan_device_s *device_info =
+	    (struct lowpan_device_s *)connman_device_get_data(device);
+
+	if (network != device_info->current_network) {
+		DBG("%p Request to change current network from %p (\"%s\") to %p (\"%s\").", device, device_info->current_network,device_info->current_network?connman_network_get_identifier(device_info->current_network):NULL,network,network?connman_network_get_identifier(network):NULL);
+		if(device_info->current_network
+		   && network
+		   && 0 == strcmp(connman_network_get_group(device_info->current_network),connman_network_get_group(network)))
+		{
+			if(connman_network_get_connected(device_info->current_network)
+			   || connman_network_get_connecting(device_info->current_network)
+			   || connman_network_get_associating(device_info->current_network)
+			   ) {
+				DBG("%p Networks are a part of the same group and a connection is in progress. Network change aborted.", device);
+
+				return;
+			}
+		}
+
+		if (device_info->current_network != NULL) {
+			connman_network_set_index(device_info->current_network, -1);
+			connman_network_unref(device_info->current_network);
+		}
+		device_info->current_network = network;
+		if (device_info->current_network != NULL)
+			connman_network_ref(device_info->current_network);
+		DBG("%p Network change complete.", device);
+	}
+}
+
+int
+lowpan_device_handle_state_change(
+    struct connman_device*  device,
+    ncp_state_t new_state,
+    struct connman_network* network
+    )
+{
+	int ret;
+	struct lowpan_device_s *device_info =
+	    (struct lowpan_device_s *)connman_device_get_data(device);
+
+	if (!ncp_state_is_not_associated(new_state) && !network) {
+		network = device_info->current_network;
+	}
+
+	DBG("%s %d network %p", connman_device_get_ident(device), new_state, network);
+
+	if (network == device_info->current_network
+		&& (new_state == device_info->ncp_state)
+		&& (new_state != NCP_STATE_ASSOCIATED)
+	) {
+		DBG("%s State was already %d", connman_device_get_ident(device), new_state);
+
+		// Skip when nothing has really changed.
+		return 0;
+	}
+
+	if (ncp_state_is_not_associated(new_state)) {
+		// If the previous NCP state is also 'disconnected' or 'uninitialized' ignore the new state change
+		if (ncp_state_is_not_associated(device_info->ncp_state)) {
+			// In case of connecting to a network from 'deep-sleep' we get a 'disconnected' state before change
+			// to 'joining'. In this case, we should ignore the 'disconnected' state change as to not remove the
+			// current_network we are attempting to connect to and fail to connect/join.
+			device_info->ncp_state = new_state;
+			DBG("%s State was already effectively %d", connman_device_get_ident(device), new_state);
+			return 0;
+		}
+
+		connman_device_set_disconnected(device, TRUE);
+
+		if (device_info->current_network != NULL) {
+			memset(&device_info->current_network_info,0,sizeof(device_info->current_network_info));
+			if (connman_network_get_connecting(device_info->current_network))
+				connman_network_set_error(device_info->current_network, CONNMAN_NETWORK_ERROR_CONNECT_FAIL);
+			else if (connman_network_get_associating(device_info->current_network))
+				connman_network_set_error(device_info->current_network, CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+			else
+				connman_network_set_connected(device_info->current_network, FALSE);
+		}
+		network = NULL;
+	}
+
+	lowpan_device_set_network(device, network);
+	network = device_info->current_network;
+
+	if (ncp_state_is_initializing(new_state)) {
+		return 0;
+	}
+
+	device_info->ncp_state = new_state;
+
+	if (network) {
+		if (new_state == NCP_STATE_COMMISSIONED) {
+			if (connman_network_get_connected(network)) {
+				// Do not auto-resume, this is performed automatically by wpantund.
+				// TODO: Check if Autoresume is enabled and allow this if not.
+				// lowpan_network_connect_using_resume(network);
+			} else if (connman_network_get_connecting(network)
+			           || connman_network_get_associating(network)
+			           ) {
+				connman_network_set_error(network, CONNMAN_NETWORK_ERROR_CONNECT_FAIL);
+			} else {
+				connman_service_connect(connman_service_lookup_from_network(network),
+                        CONNMAN_SERVICE_CONNECT_REASON_USER);
+			}
+		} else if (new_state == NCP_STATE_ASSOCIATING) {
+
+		} else if (new_state == NCP_STATE_CREDENTIALS_NEEDED) {
+			if (connman_network_get_associating(network)) {
+				int err;
+
+				connman_service_create_ip6config(connman_service_lookup_from_network(network), connman_device_get_index(device));
+				connman_network_set_index(network, connman_device_get_index(device));
+				connman_network_set_ipv4_method(network, CONNMAN_IPCONFIG_METHOD_OFF);
+				connman_network_set_ipv6_method(network, CONNMAN_IPCONFIG_METHOD_FIXED);
+
+				err = lowpan_network_set_key_on_ncp(network);
+				if (-ENOKEY == err) {
+					err = connman_network_needs_input(network);
+					if (0 != err) {
+						DBG("connman_network_needs_input(network) failed with %d", err);
+					}
+				}
+
+				if ((0 != err) && (-EINPROGRESS != err)) {
+					connman_network_set_associating(network, false);
+					lowpan_device_leave(device);
+				}
+			}
+
+
+		} else if (new_state == NCP_STATE_NET_WAKE_ASLEEP) {
+			// Don't do anything special when we are in the lurking state.
+
+		} else if (new_state == NCP_STATE_ASSOCIATED) {
+
+			connman_service_create_ip6config(connman_service_lookup_from_network(network), connman_device_get_index(device));
+			connman_network_set_index(network, connman_device_get_index(device));
+			connman_network_set_ipv4_method(network, CONNMAN_IPCONFIG_METHOD_OFF);
+			connman_network_set_ipv6_method(network, CONNMAN_IPCONFIG_METHOD_FIXED);
+
+#if 0
+			/* This part is removed as a solution for COM-1070 (Need temporary workaround for ConnMan supporting only one IPv6 address)
+			 * We'd like the connman to store just the Thread ULA address (which is set by client through network manager)
+			 */
+
+			if (buffer_is_nonzero(device_info->current_network_info.prefix, sizeof(device_info->current_network_info.prefix))) {
+				struct connman_ipaddress *ip_address;
+				char v6_addr_str[56];
+
+				ip_address = connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV6);
+				encode_ipv6_address_from_prefx_and_hwaddr(v6_addr_str,
+														  sizeof(v6_addr_str),
+														  device_info->current_network_info.prefix,
+														  device_info->hwaddr);
+
+				connman_ipaddress_set_ipv6(ip_address, v6_addr_str, 64, "::");
+
+				connman_network_set_ipaddress(network, ip_address);
+
+				connman_ipaddress_free(ip_address);
+			} else {
+				struct connman_ipaddress *ip_address;
+
+				// If there is no prefix, set an empty address.
+				ip_address = connman_ipaddress_alloc(CONNMAN_IPCONFIG_TYPE_IPV6);
+				connman_ipaddress_set_ipv6(ip_address, "::", 128, "::");
+				connman_network_set_ipaddress(network, ip_address);
+				connman_ipaddress_free(ip_address);
+			}
+
+#endif       /*  End of change for COM-1070 */
+
+			lowpan_network_update_key_from_ncp(network);
+
+			if (!connman_network_get_connected(network)) {
+				if (!connman_network_get_connecting(network)
+				    && !connman_network_get_associating(network)
+				    ) {
+					if(connman_network_get_string(network, LOWPAN_AUTH_KEY)) {
+						DBG("We need to get connman to connect...");
+						connman_service_connect(connman_service_lookup_from_network(network),
+                                CONNMAN_SERVICE_CONNECT_REASON_USER);
+					} else {
+						DBG("Waiting to get network key before asking connman to connect...");
+						// This should happen in a few moments, initiated
+						// by the call to lowpan_network_update_key_from_ncp, above.
+					}
+					return 0;
+				} else {
+					DBG("Marking Network as connected.");
+					connman_network_set_connected(network, TRUE);
+				}
+			} else {
+				DBG("Service/Network already connected.");
+			}
+		}
+	}
+
+	return 0;
+}
+
+
+DBusHandlerResult
+lowpan_device_signal_handler(
+    DBusConnection *connection,
+    DBusMessage *   message,
+    void *                  user_data
+    )
+{
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	struct connman_device* device = user_data;
+	struct lowpan_device_s *device_info =
+	    (struct lowpan_device_s *)connman_device_get_data(device);
+	const char* msg_path = dbus_message_get_path(message);
+	const char* interface_name = connman_device_get_ident(device);
+	char* path = NULL;
+
+	asprintf(&path, "%s/%s", WPAN_TUNNEL_DBUS_PATH, interface_name);
+
+
+	if (!msg_path || (0 != strcmp(path, msg_path)))
+		goto bail;
+
+	if (dbus_message_is_signal(message, WPAN_TUNNEL_DBUS_INTERFACE,
+	                           WPAN_IFACE_SIGNAL_STATE_CHANGED)) {
+		DBusMessageIter iter;
+		const char* new_state = NULL;
+		struct connman_network *network = NULL;
+		struct wpan_network_info_s network_info;
+		bool should_change_device_power_state = false;
+		bool new_device_power_state = false;
+		char network_identifier[256];
+
+		ret = DBUS_HANDLER_RESULT_HANDLED;
+
+		dbus_message_iter_init(message, &iter);
+
+		dbus_message_iter_get_basic(&iter, &new_state);
+
+		if (new_state == NULL) {
+			ret = -EINVAL;
+			goto bail;
+		}
+
+		if (ncp_state_is_initializing(device_info->ncp_state)) {
+			// Make sure that we start up in a powered state.
+			should_change_device_power_state = true;
+			new_device_power_state = true;
+		}
+
+		DBG("AssociationStateChanged: %s", new_state);
+
+		dbus_message_iter_next(&iter);
+
+		network = get_network_from_iter(device, &iter);
+		parse_network_info_from_iter(&device_info->current_network_info, &iter);
+		ncp_state_t ncp_state;
+
+		ncp_state = string_to_ncp_state(new_state, device_info->ncp_state);
+
+
+		DBusMessageIter outer_iter;
+		DBusMessageIter dict_iter;
+		if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) {
+			dbus_message_iter_recurse(&iter, &outer_iter);
+			iter = outer_iter;
+		}
+
+		for (;
+			 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID;
+			 dbus_message_iter_next(&iter)) {
+			DBusMessageIter value_iter;
+			char* key = NULL;
+
+			if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
+				ret = -1;
+				goto bail;
+			}
+
+			dbus_message_iter_recurse(&iter, &dict_iter);
+
+			if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) {
+				ret = -1;
+				goto bail;
+			}
+
+			// Get the key
+			dbus_message_iter_get_basic(&dict_iter, &key);
+			dbus_message_iter_next(&dict_iter);
+
+			if(key == NULL) {
+				ret = -EINVAL;
+				goto bail;
+			}
+
+			if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_VARIANT) {
+				ret = -1;
+				goto bail;
+			}
+
+			dbus_message_iter_recurse(&dict_iter, &value_iter);
+
+			if (strcmp(key, "Enabled") == 0) {
+				dbus_bool_t enabled = 0;
+				bool device_power_state = (should_change_device_power_state?
+						new_device_power_state : connman_device_get_powered(device) );
+
+				dbus_message_iter_get_basic(&value_iter, &enabled);
+
+				DBG("NCP IS %s",enabled?"ENABLED":"DISABLED");
+				if (enabled != device_power_state) {
+					if (ncp_state_is_initializing(device_info->ncp_state)) {
+						// If this connman_device is uninitialized/initializing, then we need to make
+						// sure that the NCP is in our current power state.
+						enabled = !enabled;
+						DBG("%sABLING NCP",enabled?"EN":"DIS");
+						lowpan_driver_setprop_int32(device, kWPANTUNDProperty_DaemonEnabled, enabled);
+					} else {
+						// If this connman_device is initialized, then we need to make
+						// sure that our power state matches the power state of the NCP.
+						should_change_device_power_state = true;
+						new_device_power_state = enabled;
+					}
+				}
+			}
+		}
+
+
+		lowpan_device_handle_state_change(device, ncp_state, network);
+
+		if (should_change_device_power_state) {
+			DBG("%sABLING CONNMAN DEVICE",new_device_power_state?"EN":"DIS");
+			connman_device_set_powered(device, new_device_power_state);
+		}
+	}
+
+bail:
+	free(path);
+	return ret;
+}
+
+
+static int
+lowpan_device_probe(struct connman_device *device)
+{
+	DBG("%p %s", device, connman_device_get_ident(device));
+
+	if (dbus_connection_add_filter(connection,
+	                           lowpan_device_signal_handler,
+	                           (void*)device,
+	                           NULL) == FALSE) {
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void
+lowpan_device_remove(struct connman_device *device)
+{
+	DBG("%p %s", device, connman_device_get_ident(device));
+
+	(void)dbus_connection_remove_filter(connection, lowpan_device_signal_handler,
+	                              (void*)device);
+}
+
+
+static int
+lowpan_device_enable(struct connman_device *device)
+{
+	DBG("%p %s", device, connman_device_get_ident(device));
+	lowpan_driver_setprop_int32(device, kWPANTUNDProperty_DaemonEnabled, 1);
+	return 0;
+}
+
+static int
+lowpan_device_disable(struct connman_device *device)
+{
+	DBG("%p %s", device, connman_device_get_ident(device));
+
+	struct lowpan_device_s *device_info = (struct lowpan_device_s *)connman_device_get_data(device);
+	if (device_info) {
+		lowpan_device_set_network(device, NULL);
+		connman_device_set_disconnected(device, TRUE);
+	}
+
+	lowpan_driver_setprop_int32(device, kWPANTUNDProperty_DaemonEnabled, 0);
+	return 0;
+}
+
+static void
+status_finished_callback(
+    DBusPendingCall *pending, void* user_data
+    )
+{
+	int32_t ret = 0;
+	struct connman_device *device = (struct connman_device *)user_data;
+	struct lowpan_device_s *device_info =
+	    (struct lowpan_device_s *)connman_device_get_data(device);
+	struct connman_network *network = NULL;
+	DBusMessage* reply = dbus_pending_call_steal_reply(pending);
+	DBusMessageIter iter;
+	ncp_state_t ncp_state;
+	bool should_change_device_power_state = false;
+	bool new_device_power_state = false;
+
+	DBG("%p %s", device, connman_device_get_ident(device));
+
+	if (!reply) {
+		DBG("%p Status callback failed", device);
+		ret = -1;
+		goto bail;
+	}
+
+	dbus_message_iter_init(reply, &iter);
+	dump_info_from_iter(stderr, &iter, 1, false);
+
+	if(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) {
+		char* cstr = NULL;
+		dbus_message_iter_get_basic(&iter,&cstr);
+		DBG("%p Status callback failed: %s", device, cstr);
+		ret = -1;
+		goto bail;
+	}
+
+	parse_network_info_from_iter(&device_info->current_network_info, &iter);
+	network = get_network_from_iter(device, &iter);
+
+	DBusMessageIter outer_iter;
+	DBusMessageIter dict_iter;
+	if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) {
+		dbus_message_iter_recurse(&iter, &outer_iter);
+		iter = outer_iter;
+	}
+
+	if (ncp_state_is_initializing(device_info->ncp_state)) {
+		// Make sure that we start up in a powered state.
+		should_change_device_power_state = true;
+		new_device_power_state = true;
+	}
+
+	ncp_state = device_info->ncp_state;
+
+	for (;
+	     dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID;
+	     dbus_message_iter_next(&iter)) {
+		DBusMessageIter value_iter;
+		char* key = NULL;
+
+		if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
+			ret = -1;
+			goto bail;
+		}
+
+		dbus_message_iter_recurse(&iter, &dict_iter);
+
+		if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) {
+			ret = -1;
+			goto bail;
+		}
+
+		// Get the key
+		dbus_message_iter_get_basic(&dict_iter, &key);
+		dbus_message_iter_next(&dict_iter);
+
+		if(key == NULL) {
+			ret = -EINVAL;
+			goto bail;
+		}
+
+		if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_VARIANT) {
+			ret = -1;
+			goto bail;
+		}
+
+		dbus_message_iter_recurse(&dict_iter, &value_iter);
+
+		if (strcmp(key, kWPANTUNDProperty_NCPHardwareAddress) == 0) {
+			DBusMessageIter array_iter;
+			dbus_message_iter_recurse(&value_iter, &array_iter);
+			const uint8_t* value = NULL;
+			int nelements = 0;
+			dbus_message_iter_get_fixed_array(&array_iter, &value, &nelements);
+			if (nelements == 8)
+				memcpy(device_info->hwaddr, value, 8);
+			else
+				DBG("%p Bad HWAddr length: %d", device, nelements);
+
+			char hwaddr_str[64];
+			snprintf(hwaddr_str,
+			         sizeof(hwaddr_str),
+			         "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",
+			         device_info->hwaddr[0],
+			         device_info->hwaddr[1],
+			         device_info->hwaddr[2],
+			         device_info->hwaddr[3],
+			         device_info->hwaddr[4],
+			         device_info->hwaddr[5],
+			         device_info->hwaddr[6],
+			         device_info->hwaddr[7]);
+			DBG("%p HWAddr set to %s", device, hwaddr_str);
+			connman_device_set_string(device, "Address", hwaddr_str);
+
+			struct connman_ipdevice *ipdevice = NULL;
+			ipdevice = connman_ipdevice_lookup_from_index(connman_device_get_index(device));
+
+			if (ipdevice) {
+				connman_ipdevice_set_address(ipdevice, hwaddr_str);
+				DBG("Set HWAddr on ipdevice! GOOD");
+			} else {
+				DBG("Can't set HWAddr on ipdevice because we can't find the ipdevice!");
+			}
+
+		} else if (strcmp(key, kWPANTUNDProperty_DaemonEnabled) == 0) {
+			dbus_bool_t enabled = FALSE;
+			bool device_power_state = (should_change_device_power_state?
+					new_device_power_state : connman_device_get_powered(device) );
+
+			dbus_message_iter_get_basic(&value_iter, &enabled);
+
+			DBG("NCP IS %s",enabled?"ENABLED":"DISABLED");
+			if (enabled != device_power_state) {
+				if (ncp_state_is_initializing(device_info->ncp_state)) {
+					// If this connman_device is UNinitialized, then we need to make
+					// sure that the NCP is in our current power state.
+					enabled = !enabled;
+					DBG("%sABLING NCP",enabled?"EN":"DIS");
+					lowpan_driver_setprop_int32(device, kWPANTUNDProperty_DaemonEnabled, enabled);
+				} else {
+					// If this connman_device is initialized, then we need to make
+					// sure that our power state matches the power state of the NCP.
+					should_change_device_power_state = true;
+					new_device_power_state = enabled;
+				}
+			}
+
+		} else if (strcmp(key, kWPANTUNDProperty_NetworkKey) == 0) {
+			DBusMessageIter array_iter;
+			dbus_message_iter_recurse(&value_iter, &array_iter);
+			const uint8_t* value = NULL;
+			int nelements = 0;
+			dbus_message_iter_get_fixed_array(&array_iter, &value, &nelements);
+
+			lowpan_network_update_key(network, value, nelements);
+		} else if (strcmp(key, kWPANTUNDProperty_NCPState) == 0) {
+			const char* new_state = NULL;
+			dbus_message_iter_get_basic(&value_iter, &new_state);
+
+			ncp_state = string_to_ncp_state(new_state, ncp_state);
+		} else {
+#if DEBUG
+			DBG(
+			    "info: %s -> (%c)\n",
+			    key,
+			    dbus_message_iter_get_arg_type(&value_iter));
+#endif
+		}
+	}
+
+	lowpan_device_handle_state_change(device, ncp_state, network);
+
+	if (should_change_device_power_state) {
+		DBG("%sABLING CONNMAN DEVICE",new_device_power_state?"EN":"DIS");
+		connman_device_set_powered(device, new_device_power_state);
+	}
+
+bail:
+	if (reply) {
+		dbus_message_unref(reply);
+	}
+	if (pending) {
+		dbus_pending_call_unref(pending);
+	}
+
+	return;
+}
+
+static void
+scan_free_callback(void* user_data)
+{
+	struct connman_device *device = (struct connman_device *)user_data;
+
+	connman_device_unref(device);
+}
+
+
+static int
+lowpan_device_update_status(struct connman_device *device)
+{
+	DBusPendingCall *pending_status = NULL;
+	DBusMessage *message = NULL;
+	char dbus_path[128];
+
+	snprintf(dbus_path,
+	         sizeof(dbus_path),
+	         "%s/%s",
+	         WPAN_TUNNEL_DBUS_PATH,
+	         connman_device_get_ident(device));
+
+	message = dbus_message_new_method_call(
+	    WPAN_TUNNEL_DBUS_NAME,
+	    dbus_path,
+	    WPAN_TUNNEL_DBUS_INTERFACE,
+	    WPAN_IFACE_CMD_STATUS
+	);
+
+	if (!dbus_connection_send_with_reply(
+	        connection,
+	        message,
+	        &pending_status,
+	        3000
+		)
+	) {
+		dbus_message_unref(message);
+		return -1;
+	}
+
+	dbus_message_unref(message);
+
+	if (!dbus_pending_call_set_notify(
+	        pending_status,
+	        &status_finished_callback,
+	        (void*)device,
+	        (DBusFreeFunction) & scan_free_callback
+		)
+	) {
+		dbus_pending_call_cancel(pending_status);
+		return -1;
+	}
+
+	connman_device_ref(device);
+
+	return 0;
+}
+
+static void
+scan_finished_callback(
+    DBusPendingCall *pending, void* user_data
+    )
+{
+	int32_t ret = 0;
+	struct connman_device *device = (struct connman_device *)user_data;
+	struct lowpan_device_s *device_info = (struct lowpan_device_s *)connman_device_get_data(device);
+	DBusMessage* reply = dbus_pending_call_steal_reply(pending);
+	DBusMessageIter iter;
+	DBusMessageIter list_iter;
+
+	DBG("%p SCAN CALLBACK", device);
+
+	if (!reply) {
+		DBG("%p Scan reply was empty?", device);
+		ret = -1;
+		goto bail;
+	}
+
+	dbus_message_iter_init(reply, &iter);
+	dump_info_from_iter(stderr, &iter, 1, false);
+
+	if(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) {
+		char* cstr = NULL;
+		dbus_message_iter_get_basic(&iter,&cstr);
+		DBG("%p Scan failed: %s", device, cstr);
+		ret = -1;
+		connman_device_reset_scanning(device);
+		goto bail;
+	}
+
+
+	// Get return code
+	dbus_message_iter_get_basic(&iter, &ret);
+
+	if (ret) {
+		DBG("%p Scan failed: %d", device, ret);
+		connman_device_reset_scanning(device);
+		goto bail;
+	}
+
+	// Move to the list of networks.
+	dbus_message_iter_next(&iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+		ret = -1;
+		goto bail;
+	}
+
+	dbus_message_iter_recurse(&iter, &list_iter);
+
+	for (;
+	     dbus_message_iter_get_arg_type(&list_iter) == DBUS_TYPE_ARRAY;
+	     dbus_message_iter_next(&list_iter)) {
+		struct connman_network *network = NULL;
+		char network_name[128];
+
+		network = get_network_from_iter(device, &list_iter);
+
+		if(network == NULL)
+			continue;
+
+		connman_network_set_available(network, TRUE);
+		connman_network_update(network);
+	}
+
+
+bail:
+	if (reply) {
+		dbus_message_unref(reply);
+	}
+	if (pending) {
+		dbus_pending_call_unref(pending);
+	}
+
+	if ((device_info != NULL) && (device_info->current_network != NULL)) {
+		// Always make sure that the current network is marked as
+		// available, so that we don't end up accidentally
+		// disconnecting from it.
+		connman_network_set_available(device_info->current_network, TRUE);
+	}
+
+	connman_device_set_scanning(device, CONNMAN_SERVICE_TYPE_LOWPAN, FALSE);
+	return;
+}
+
+static int
+lowpan_device_scan(enum connman_service_type type,
+    struct connman_device *device,
+    const char *ssid, unsigned int ssid_len,
+    const char *identity, const char* passphrase,
+    const char *security, void* user_data
+    )
+{
+	int status = 0;
+	DBusPendingCall *pending_scan = NULL;
+	DBusMessage *message = NULL;
+	char *dbus_path = NULL;
+	struct lowpan_device_s *device_info =
+	    (struct lowpan_device_s *)connman_device_get_data(device);
+
+	DBG("%p ssid=%s, id=%s, passphrate=%s", device, ssid, identity, passphrase);
+
+	require_action(
+		connman_device_get_scanning(device) == FALSE,
+		bail,
+		status = -EALREADY
+	);
+
+	if (device_info->current_network != NULL) {
+		require_action(
+			connman_network_get_associating(device_info->current_network) == FALSE,
+			bail,
+			status = -EBUSY
+		);
+	}
+
+	require_action(device_info->ncp_state != NCP_STATE_ASSOCIATING, bail, status = -EBUSY);
+	require_action(device_info->ncp_state != NCP_STATE_COMMISSIONED, bail, status = -EBUSY);
+	require_action(!ncp_state_is_initializing(device_info->ncp_state), bail, status = -EBUSY);
+
+	asprintf(&dbus_path,
+	         "%s/%s",
+	         WPAN_TUNNEL_DBUS_PATH,
+	         connman_device_get_ident(device));
+
+	message = dbus_message_new_method_call(
+	    WPAN_TUNNEL_DBUS_NAME,
+	    dbus_path,
+	    WPAN_TUNNEL_DBUS_INTERFACE,
+	    WPAN_IFACE_CMD_SCAN
+	);
+
+	require_action(message != NULL, bail, status = -ENOMEM);
+
+	require_action(message != NULL, bail, status = -ENOMEM);
+
+	require_action(
+		dbus_connection_send_with_reply(
+	        connection,
+	        message,
+	        &pending_scan,
+	        45000 // Timeout, in ms
+		) == TRUE,
+		bail,
+		status = -EIO
+	);
+
+	require_action(
+		dbus_pending_call_set_notify(
+	        pending_scan,
+	        &scan_finished_callback,
+	        (void*)device,
+	        (DBusFreeFunction) & scan_free_callback
+		) == TRUE,
+		bail,
+		status = -EIO
+	);
+
+	pending_scan = NULL;
+	connman_device_ref(device);
+	connman_device_set_scanning(device, CONNMAN_SERVICE_TYPE_LOWPAN, TRUE);
+
+bail:
+	free(dbus_path);
+
+	if (pending_scan != NULL) {
+		dbus_pending_call_cancel(pending_scan);
+	}
+
+	if (message != NULL) {
+		dbus_message_unref(message);
+	}
+	return status;
+}
+
+
+static int lowpan_device_set_regdom(
+    struct connman_device *device, const char *alpha2
+    )
+{
+	DBG("%p", device);
+	connman_device_regdom_notify(device, 0, alpha2);
+	return 0;
+}
+
+static struct connman_device_driver lowpan_device_driver = {
+	.name           = "lowpan",
+	.type           = CONNMAN_DEVICE_TYPE_LOWPAN,
+	.priority       = CONNMAN_DEVICE_PRIORITY_LOW,
+	.probe          = lowpan_device_probe,
+	.remove         = lowpan_device_remove,
+	.enable         = lowpan_device_enable,
+	.disable        = lowpan_device_disable,
+	.scan           = lowpan_device_scan,
+	.set_regdom = lowpan_device_set_regdom,
+};
+
+static struct connman_device*
+lowpan_device_create(const char* interface_name)
+{
+	struct connman_device *device = NULL;
+
+	device = g_hash_table_lookup(devices, interface_name);
+	if (!device) {
+		struct lowpan_device_s *device_info =
+		    calloc(1, sizeof(struct lowpan_device_s));
+
+		device = connman_device_create(
+		    "lowpan",
+		    lowpan_device_driver.type
+		    );
+
+		connman_device_set_data(device, device_info);
+
+		g_hash_table_replace(devices, g_strdup(interface_name), device);
+
+		connman_device_set_index(device, connman_inet_ifindex(interface_name));
+		connman_device_set_ident(device, interface_name);
+		connman_device_set_interface(device, interface_name);
+
+		if(connman_device_register(device)<0) {
+			g_hash_table_remove(devices, device);
+			device = NULL;
+		}
+		DBG("device created: %p", device);
+	}
+
+	if(device)
+		lowpan_device_update_status(device);
+
+	return device;
+}
+
+static void
+lowpan_device_finalize(gpointer data)
+{
+	struct connman_device *device = data;
+	struct lowpan_device_s *device_info =
+	    (struct lowpan_device_s *)connman_device_get_data(device);
+
+	DBG("%p %s", device, connman_device_get_ident(device));
+	if (device_info) {
+		if (device_info->current_network) {
+			connman_network_set_connected(device_info->current_network, FALSE);
+		}
+
+		connman_device_remove_network(device, device_info->current_network);
+
+		lowpan_device_set_network(device, NULL);
+
+		free(device_info);
+		connman_device_set_data(device, NULL);
+
+		connman_device_unregister(device);
+
+		connman_device_set_ident(device, "X");
+		connman_device_set_interface(device, "X");
+
+		connman_device_unref(device);
+	}
+}
+
+/* -------------------------------------------------------------------------- */
+// MARK: - LoWPAN Technology
+
+static int
+lowpan_tech_probe(struct connman_technology *technology)
+{
+	DBG("%s: %p", __FUNCTION__, technology);
+	lowpan_tech = technology;
+
+	return 0;
+}
+
+static void
+lowpan_tech_remove(struct connman_technology *technology)
+{
+	DBG("");
+	lowpan_tech = NULL;
+}
+
+static int
+lowpan_tech_set_regdom(
+    struct connman_technology *technology, const char *alpha2
+    )
+{
+	return 0;
+}
+
+static int
+lowpan_tech_set_tethering(
+    struct connman_technology *technology,
+    const char *identifier, const char *passphrase,
+    const char *bridge, bool enabled
+    )
+{
+	return 0;
+}
+
+static struct connman_technology_driver lowpan_tech_driver = {
+	.name           = "lowpan",
+	.type           = CONNMAN_SERVICE_TYPE_LOWPAN,
+	.probe          = lowpan_tech_probe,
+	.remove         = lowpan_tech_remove,
+	.set_regdom = lowpan_tech_set_regdom,
+};
+
+/* -------------------------------------------------------------------------- */
+// MARK: - LoWPAN DBus
+
+static void
+lowpan_dbus_init_interfaces(void)
+{
+	DBusMessage *message = NULL;
+	DBusMessage *reply = NULL;
+	DBusError error = DBUS_ERROR_INIT;
+	DBusMessageIter iter;
+	DBusMessageIter list_iter;
+
+	DBG("%s", __FUNCTION__);
+
+	message = dbus_message_new_method_call(
+	    WPAN_TUNNEL_DBUS_NAME,
+	    WPAN_TUNNEL_DBUS_PATH,
+	    WPAN_TUNNEL_DBUS_INTERFACE,
+	    WPAN_TUNNEL_CMD_GET_INTERFACES
+	    );
+
+	if (!message) {
+		DBG("%s: Unable to create dbus message.", __FUNCTION__);
+		goto bail;
+	}
+
+	reply = dbus_connection_send_with_reply_and_block(
+	    connection,
+	    message,
+	    5000,
+	    &error
+	    );
+
+	if (!reply) {
+		DBG("%s: DBus call to GetInterfaces failed: %s", __FUNCTION__, error.message);
+		goto bail;
+	}
+
+	dbus_message_iter_init(reply, &iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+		DBG("%s: Bad return type for GetInterfaces", __FUNCTION__);
+		goto bail;
+	}
+
+	dbus_message_iter_recurse(&iter, &list_iter);
+
+	for (;
+	     dbus_message_iter_get_arg_type(&list_iter) != DBUS_TYPE_INVALID;
+	     dbus_message_iter_next(&list_iter)) {
+		DBusMessageIter tmp_iter;
+		DBusMessageIter *this_iter = &list_iter;
+		char *interface_name = NULL;
+
+		if (dbus_message_iter_get_arg_type(this_iter) == DBUS_TYPE_ARRAY) {
+			dbus_message_iter_recurse(this_iter, &tmp_iter);
+			this_iter = &tmp_iter;
+		}
+
+		if (dbus_message_iter_get_arg_type(this_iter) == DBUS_TYPE_STRING) {
+			dbus_message_iter_get_basic(this_iter, &interface_name);
+		}
+
+		if (interface_name != NULL) {
+			DBG("%s: Interface: \"%s\"", __FUNCTION__, interface_name);
+			lowpan_device_create(interface_name);
+		} else {
+			DBG("%s: Unable to extract interface name", __FUNCTION__);
+		}
+	}
+
+bail:
+	if (message)
+		dbus_message_unref(message);
+
+	if (reply)
+		dbus_message_unref(reply);
+
+	dbus_error_free(&error);
+}
+
+DBusHandlerResult
+lowpan_signal_handler(
+    DBusConnection *connection,
+    DBusMessage *   message,
+    void *                  user_data
+    )
+{
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	if (dbus_message_is_signal(message, WPAN_TUNNEL_DBUS_INTERFACE,
+	                           WPAN_TUNNEL_SIGNAL_INTERFACE_ADDED)) {
+		const char* interface_name = NULL;
+		DBG("%s", dbus_message_get_path(message));
+
+		dbus_message_get_args(
+		    message, NULL,
+		    DBUS_TYPE_STRING, &interface_name,
+		    DBUS_TYPE_INVALID
+		    );
+
+		DBG("%s: InterfaceAdded: %s", __FUNCTION__, interface_name);
+		lowpan_device_create(interface_name);
+
+		ret = DBUS_HANDLER_RESULT_HANDLED;
+	} else if (dbus_message_is_signal(message, WPAN_TUNNEL_DBUS_INTERFACE,
+	                                  WPAN_TUNNEL_SIGNAL_INTERFACE_REMOVED)) {
+		const char* interface_name = NULL;
+		DBG("%s", dbus_message_get_path(message));
+		dbus_message_get_args(
+		    message, NULL,
+		    DBUS_TYPE_STRING, &interface_name,
+		    DBUS_TYPE_INVALID
+		    );
+
+		DBG("%s: InterfaceRemoved: %s", __FUNCTION__, interface_name);
+
+		struct connman_device *device = g_hash_table_lookup(devices, interface_name);
+
+		g_hash_table_remove(devices, device);
+
+		ret = DBUS_HANDLER_RESULT_HANDLED;
+	} else if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS,
+	                                  "NameOwnerChanged")) {
+		const char* name = NULL;
+		const char* oldOwner = NULL;
+		const char* newOwner = NULL;
+
+		dbus_message_get_args(
+		    message, NULL,
+		    DBUS_TYPE_STRING, &name,
+		    DBUS_TYPE_STRING, &oldOwner,
+		    DBUS_TYPE_STRING, &newOwner,
+		    DBUS_TYPE_INVALID
+		    );
+		if (name && (0 == strcmp(name, WPAN_TUNNEL_DBUS_INTERFACE))) {
+			if(newOwner[0]!=0 && oldOwner[0]==0) {
+				DBG("%s is now ONLINE: \"%s\" (was \"%s\")",name, newOwner, oldOwner);
+			}
+			if(newOwner[0]!=0) {
+				lowpan_dbus_init_interfaces();
+			}
+			if(oldOwner[0]!=0 && newOwner[0]==0) {
+				DBG("%s is now OFFLINE: \"%s\" (was \"%s\")",name, newOwner, oldOwner);
+				g_hash_table_remove_all(devices);
+			}
+		}
+	}
+
+	return ret;
+}
+
+
+static int
+lowpan_dbus_init(void)
+{
+	int ret = 0;
+	DBG("%s", __FUNCTION__);
+
+	// TODO: Add free function!
+	if (dbus_connection_add_filter(connection, lowpan_signal_handler, NULL, NULL) == FALSE) {
+		ret = -EIO;
+		goto bail;
+	}
+
+	static const char *lowpan_dbus_rule0 = "type=signal,"
+	                                       "path=" DBUS_PATH_DBUS ","
+	                                       "sender=" DBUS_SERVICE_DBUS ","
+	                                       "interface=" DBUS_INTERFACE_DBUS ","
+	                                       "member=NameOwnerChanged,"
+	                                       "arg0=" WPAN_TUNNEL_DBUS_NAME;
+	static const char *lowpan_dbus_rule1 = "type=signal,"
+	                                       "interface=" WPAN_TUNNEL_DBUS_INTERFACE;
+
+	dbus_bus_add_match(connection, lowpan_dbus_rule0, NULL);
+	dbus_bus_add_match(connection, lowpan_dbus_rule1, NULL);
+
+	lowpan_dbus_init_interfaces();
+
+bail:
+	return ret;
+}
+
+/* -------------------------------------------------------------------------- */
+// MARK: - LoWPAN Plugin
+
+#include <mcheck.h>
+static int
+lowpan_tunnel_init(void)
+{
+	DBG("%s", __FUNCTION__);
+
+	int err = -1;
+
+	connection = connman_dbus_get_connection();
+	if (connection == NULL) {
+		DBG("%s: No DBUS connection...?", __FUNCTION__);
+		err = -EIO;
+
+		goto out;
+	}
+
+	devices = g_hash_table_new_full(g_str_hash,
+	                                g_str_equal,
+	                                g_free,
+	                                lowpan_device_finalize);
+
+	err = connman_technology_driver_register(&lowpan_tech_driver);
+	if (err < 0)
+		goto out;
+
+	err = connman_network_driver_register(&lowpan_network_driver);
+	if (err < 0) {
+		connman_technology_driver_unregister(&lowpan_tech_driver);
+		goto out;
+	}
+
+	err = connman_device_driver_register(&lowpan_device_driver);
+	if (err < 0) {
+		connman_network_driver_unregister(&lowpan_network_driver);
+		connman_technology_driver_unregister(&lowpan_tech_driver);
+		goto out;
+	}
+
+	err = lowpan_dbus_init();
+	if (err < 0) {
+		connman_device_driver_unregister(&lowpan_device_driver);
+		connman_network_driver_unregister(&lowpan_network_driver);
+		connman_technology_driver_unregister(&lowpan_tech_driver);
+		goto out;
+	}
+
+#if 0
+
+
+	err = connman_device_driver_register(&ethernet_driver);
+	if (err < 0) {
+		connman_network_driver_unregister(&cable_driver);
+		goto out;
+	}
+
+	err = connman_technology_driver_register(&tech_driver);
+	if (err < 0) {
+		connman_device_driver_unregister(&ethernet_driver);
+		connman_network_driver_unregister(&cable_driver);
+		goto out;
+	}
+#endif
+
+	return 0;
+out:
+
+	return err;
+}
+
+static void
+lowpan_tunnel_exit(void)
+{
+	DBG("%s", __FUNCTION__);
+
+	connman_network_driver_unregister(&lowpan_network_driver);
+
+	connman_technology_driver_unregister(&lowpan_tech_driver);
+
+	connman_device_driver_unregister(&lowpan_device_driver);
+
+#if 0
+	connman_technology_driver_unregister(&tech_driver);
+
+	connman_network_driver_unregister(&cable_driver);
+
+	connman_device_driver_unregister(&ethernet_driver);
+#endif
+}
+
+CONNMAN_PLUGIN_DEFINE(
+    lowpan_tunnel,
+    "LoWPAN tunnel plugin",
+    CONNMAN_VERSION,
+    CONNMAN_PLUGIN_PRIORITY_DEFAULT,
+    lowpan_tunnel_init,
+    lowpan_tunnel_exit
+    )
diff --git a/src/ipc-dbus/DBUSIPCServer.cpp b/src/ipc-dbus/DBUSIPCServer.cpp
new file mode 100644
index 0000000..c7f14f8
--- /dev/null
+++ b/src/ipc-dbus/DBUSIPCServer.cpp
@@ -0,0 +1,414 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      Implementation of the DBus IPCServer subclass.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef ASSERT_MACROS_USE_SYSLOG
+#define ASSERT_MACROS_USE_SYSLOG 1
+#endif
+
+#include <stdio.h>
+#include "DBUSIPCServer.h"
+#include <dbus/dbus.h>
+#if USING_GLIB
+#include <glib.h>
+#include <glib-object.h>
+#endif
+#include <string.h>
+#include "NCPControlInterface.h"
+#include "NCPMfgInterface.h"
+#include "assert-macros.h"
+
+#include <boost/bind.hpp>
+#include "DBUSHelpers.h"
+#include <errno.h>
+#include <algorithm>
+#include "any-to.h"
+#include "wpan-dbus-v0.h"
+
+using namespace DBUSHelpers;
+using namespace nl;
+using namespace nl::wpantund;
+
+static const char gDBusObjectManagerMatchString[] =
+	"type='signal'"
+	",interface='" WPAN_TUNNEL_DBUS_INTERFACE "'"
+	;
+
+static DBusConnection *
+get_dbus_connection()
+{
+	static DBusConnection* connection = NULL;
+	DBusError error;
+	dbus_error_init(&error);
+
+	if (connection == NULL) {
+		syslog(LOG_DEBUG, "Getting DBus connection");
+
+		connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
+
+		if (!connection) {
+			dbus_error_free(&error);
+			dbus_error_init(&error);
+			connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+		}
+
+		require_string(connection != NULL, bail, error.message);
+
+		syslog(LOG_DEBUG, "Registering DBus connection");
+
+		dbus_bus_register(connection, &error);
+
+		require_string(error.name == NULL, bail, error.message);
+
+		syslog(LOG_DEBUG, "Requesting DBus connection name %s", WPAN_TUNNEL_DBUS_NAME);
+
+		dbus_bus_request_name(
+			connection,
+			WPAN_TUNNEL_DBUS_NAME,
+			0,
+			&error
+		);
+
+		require_string(error.name == NULL, bail, error.message);
+	}
+
+bail:
+	if (error.message) {
+		throw std::runtime_error(error.message);
+	}
+	dbus_error_free(&error);
+
+	return connection;
+}
+
+DBUSIPCServer::DBUSIPCServer():
+	mConnection(get_dbus_connection()),
+	mAPI_v0(mConnection),
+	mAPI_v1(mConnection)
+{
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	static const DBusObjectPathVTable ipc_interface_vtable = {
+		NULL,
+		&DBUSIPCServer::dbus_message_handler,
+	};
+
+	require(
+		dbus_connection_register_object_path(
+			mConnection,
+			WPAN_TUNNEL_DBUS_PATH,
+			&ipc_interface_vtable,
+			(void*)this
+		),
+		bail
+	);
+
+	dbus_bus_add_match(
+		mConnection,
+		gDBusObjectManagerMatchString,
+		&error
+	);
+	require_string(error.name == NULL, bail, error.message);
+
+	dbus_connection_add_filter(mConnection, &DBUSIPCServer::dbus_message_handler, (void*)this, NULL);
+
+	syslog(LOG_NOTICE, "Ready. Using DBUS bus \"%s\"", dbus_bus_get_unique_name(mConnection));
+
+bail:
+	if (error.message) {
+		throw std::runtime_error(error.message);
+	}
+	dbus_error_free(&error);
+}
+
+DBUSIPCServer::~DBUSIPCServer()
+{
+	dbus_bus_remove_match(
+		mConnection,
+		gDBusObjectManagerMatchString,
+		NULL
+	);
+	dbus_connection_unref(mConnection);
+}
+
+cms_t
+DBUSIPCServer::get_ms_to_next_event()
+{
+	cms_t ret = CMS_DISTANT_FUTURE;
+
+	/* We could set up some sort of complicated mechanism
+	 * using the dbus_timer objects to actually calculate this
+	 * correctly, however we aren't really using any of the
+	 * timer-dependent stuff in DBus. As such, the following
+	 * seems to suffice. If we want wpantund to do things
+	 * like handle response timeouts in a timely manner then
+	 * we will need to go ahead and fully implement such a
+	 * mechanism. */
+
+	if (dbus_connection_get_dispatch_status(mConnection) == DBUS_DISPATCH_DATA_REMAINS) {
+		ret = 0;
+	}
+
+	if (dbus_connection_has_messages_to_send(mConnection)) {
+		ret = 0;
+	}
+
+	return ret;
+}
+
+void
+DBUSIPCServer::process(void)
+{
+	dbus_connection_read_write_dispatch(mConnection, 0);
+}
+
+int
+DBUSIPCServer::update_fd_set(fd_set *read_fd_set, fd_set *write_fd_set, fd_set *error_fd_set, int *max_fd, cms_t *timeout)
+{
+	int ret = -1;
+	int unix_fd = -1;
+
+	require(dbus_connection_get_unix_fd(mConnection, &unix_fd), bail);
+
+	if (read_fd_set != NULL) {
+		FD_SET(unix_fd, read_fd_set);
+	}
+
+	if (error_fd_set != NULL) {
+		FD_SET(unix_fd, error_fd_set);
+	}
+
+	if ((write_fd_set != NULL) && dbus_connection_has_messages_to_send(mConnection)) {
+		FD_SET(unix_fd, write_fd_set);
+	}
+
+	if ((max_fd != NULL)) {
+		*max_fd = std::max(*max_fd, unix_fd);
+	}
+
+	if (timeout != NULL) {
+		*timeout = std::min(*timeout, get_ms_to_next_event());
+	}
+
+	ret = 0;
+bail:
+	return ret;
+}
+
+
+
+void
+DBUSIPCServer::interface_added(const std::string& interface_name)
+{
+	DBusMessage* signal;
+	const char *interface_name_cstr = interface_name.c_str();
+
+	syslog(LOG_DEBUG,
+	       "DBus Sending Interface Added Signal for %s",
+	       interface_name_cstr);
+	signal = dbus_message_new_signal(
+	    WPAN_TUNNEL_DBUS_PATH,
+	    WPAN_TUNNEL_DBUS_INTERFACE,
+	    WPAN_TUNNEL_SIGNAL_INTERFACE_ADDED
+	    );
+	dbus_message_append_args(
+	    signal,
+	    DBUS_TYPE_STRING, &interface_name_cstr,
+	    DBUS_TYPE_INVALID
+	    );
+
+	dbus_connection_send(mConnection, signal, NULL);
+	dbus_message_unref(signal);
+}
+
+void
+DBUSIPCServer::interface_removed(const std::string& interface_name)
+{
+	DBusMessage* signal;
+	const char *interface_name_cstr = interface_name.c_str();
+
+	syslog(LOG_DEBUG,
+	       "DBus Sending Interface Added Signal for %s",
+	       interface_name_cstr);
+	signal = dbus_message_new_signal(
+	    WPAN_TUNNEL_DBUS_PATH,
+	    WPAN_TUNNEL_DBUS_INTERFACE,
+	    "InterfaceRemoved"
+	    );
+	dbus_message_append_args(
+	    signal,
+	    DBUS_TYPE_STRING, &interface_name_cstr,
+	    DBUS_TYPE_INVALID
+	    );
+	dbus_connection_send(mConnection, signal, NULL);
+	dbus_message_unref(signal);
+}
+
+int
+DBUSIPCServer::add_interface(NCPControlInterface* instance)
+{
+	std::string name = instance->get_name();
+
+	mInterfaceMap[name] = instance;
+
+	mAPI_v0.add_interface(instance);
+	mAPI_v1.add_interface(instance);
+
+	interface_added(name);
+
+	return 0;
+}
+
+DBusHandlerResult
+DBUSIPCServer::message_handler(
+    DBusConnection *connection,
+    DBusMessage *message
+    )
+{
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	if (dbus_message_is_method_call(message, WPAN_TUNNEL_DBUS_INTERFACE,
+	                                WPAN_TUNNEL_CMD_GET_INTERFACES)) {
+		DBusMessage *reply = dbus_message_new_method_return(message);
+		DBusMessageIter iter, array_iter;
+		dbus_message_iter_init_append(reply, &iter);
+
+		dbus_message_iter_open_container(
+		    &iter,
+		    DBUS_TYPE_ARRAY,
+			DBUS_TYPE_ARRAY_AS_STRING
+		    DBUS_TYPE_STRING_AS_STRING,
+		    &array_iter
+		    );
+		{
+			std::map<std::string, NCPControlInterface*>::const_iterator i;
+			std::map<std::string,
+					 NCPControlInterface*>::const_iterator end =
+				mInterfaceMap.end();
+			for (i = mInterfaceMap.begin(); i != end; ++i) {
+				DBusMessageIter item_iter;
+				const char* name = i->first.c_str();
+				const char* bus_name = dbus_bus_get_unique_name(connection);
+				dbus_message_iter_open_container(
+					&array_iter,
+					DBUS_TYPE_ARRAY,
+					DBUS_TYPE_STRING_AS_STRING,
+					&item_iter
+					);
+				dbus_message_iter_append_basic(&item_iter, DBUS_TYPE_STRING,
+											   &name);
+				dbus_message_iter_append_basic(&item_iter, DBUS_TYPE_STRING,
+											   &bus_name);
+				dbus_message_iter_close_container(&array_iter, &item_iter);
+			}
+		}
+		{
+			std::map<std::string, std::string>::const_iterator i;
+			std::map<std::string, std::string>::const_iterator end =
+				mExternalInterfaceMap.end();
+			for (i = mExternalInterfaceMap.begin(); i != end; ++i) {
+				DBusMessageIter item_iter;
+				const char* name = i->first.c_str();
+				const char* bus_name = i->second.c_str();
+				dbus_message_iter_open_container(
+					&array_iter,
+					DBUS_TYPE_ARRAY,
+					DBUS_TYPE_STRING_AS_STRING,
+					&item_iter
+					);
+				dbus_message_iter_append_basic(&item_iter, DBUS_TYPE_STRING,
+											   &name);
+				dbus_message_iter_append_basic(&item_iter, DBUS_TYPE_STRING,
+											   &bus_name);
+				dbus_message_iter_close_container(&array_iter, &item_iter);
+			}
+		}
+
+		dbus_message_iter_close_container(&iter, &array_iter);
+
+		dbus_connection_send(connection, reply, NULL);
+		dbus_message_unref(reply);
+
+		ret = DBUS_HANDLER_RESULT_HANDLED;
+	} else if (dbus_message_is_signal(message, WPAN_TUNNEL_DBUS_INTERFACE, WPAN_TUNNEL_SIGNAL_INTERFACE_ADDED)) {
+		ret = DBUS_HANDLER_RESULT_HANDLED;
+		if (!strequal(dbus_message_get_sender(message), dbus_bus_get_unique_name(connection))) {
+			const char* interface_name = NULL;
+			dbus_message_get_args(
+				message, NULL,
+				DBUS_TYPE_STRING, &interface_name,
+				DBUS_TYPE_INVALID
+			);
+			if (NULL != interface_name && (mInterfaceMap.count(interface_name) == 0)) {
+				mExternalInterfaceMap[interface_name] = dbus_message_get_sender(message);
+			}
+		}
+	} else if (dbus_message_is_signal(message, WPAN_TUNNEL_DBUS_INTERFACE, WPAN_TUNNEL_SIGNAL_INTERFACE_REMOVED)) {
+		ret = DBUS_HANDLER_RESULT_HANDLED;
+		if (!strequal(dbus_message_get_sender(message), dbus_bus_get_unique_name(connection))) {
+			const char* interface_name = NULL;
+			dbus_message_get_args(
+				message, NULL,
+				DBUS_TYPE_STRING, &interface_name,
+				DBUS_TYPE_INVALID
+			);
+			if ((NULL != interface_name)
+				&& (mInterfaceMap.count(interface_name) == 0)
+				&& (mExternalInterfaceMap.count(interface_name) == 1)
+				&& (mExternalInterfaceMap[interface_name] == dbus_message_get_sender(message))
+			) {
+				mExternalInterfaceMap.erase(interface_name);
+			}
+		}
+	} else if (dbus_message_is_method_call(message, WPAN_TUNNEL_DBUS_INTERFACE,
+	                                WPAN_TUNNEL_CMD_GET_VERSION)) {
+		DBusMessage *reply = dbus_message_new_method_return(message);
+		uint32_t version = WPAN_TUNNEL_DBUS_VERSION;
+		dbus_message_append_args(
+			reply,
+			DBUS_TYPE_UINT32, &version,
+			DBUS_TYPE_INVALID
+			);
+
+		dbus_connection_send(connection, reply, NULL);
+		dbus_message_unref(reply);
+
+		ret = DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+	return ret;
+}
+
+DBusHandlerResult
+DBUSIPCServer::dbus_message_handler(
+    DBusConnection *connection,
+    DBusMessage *   message,
+    void *                  user_data
+    )
+{
+	return ((DBUSIPCServer*)user_data)->message_handler(connection, message);
+}
diff --git a/src/ipc-dbus/DBUSIPCServer.h b/src/ipc-dbus/DBUSIPCServer.h
new file mode 100644
index 0000000..0a4ade4
--- /dev/null
+++ b/src/ipc-dbus/DBUSIPCServer.h
@@ -0,0 +1,76 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      Declaration of the DBus IPCServer subclass.
+ *
+ */
+
+#ifndef wpantund_DBUSIPCServer_h
+#define wpantund_DBUSIPCServer_h
+
+#include "IPCServer.h"
+#include <map>
+#include <dbus/dbus.h>
+#include <boost/signals2/signal.hpp>
+#include <boost/bind.hpp>
+
+#include "DBusIPCAPI_v0.h"
+#include "DBusIPCAPI_v1.h"
+
+namespace nl {
+namespace wpantund {
+
+class DBUSIPCServer : public IPCServer {
+public:
+
+	DBUSIPCServer();
+	virtual ~DBUSIPCServer();
+
+	virtual int add_interface(NCPControlInterface* instance);
+	virtual cms_t get_ms_to_next_event(void );
+	virtual void process(void);
+	virtual int update_fd_set(fd_set *read_fd_set, fd_set *write_fd_set, fd_set *error_fd_set, int *max_fd, cms_t *timeout);
+
+private:
+	DBusHandlerResult message_handler(
+	    DBusConnection *connection,
+	    DBusMessage *   message
+	    );
+
+	static DBusHandlerResult dbus_message_handler(
+	    DBusConnection *connection,
+	    DBusMessage *   message,
+	    void *                  user_data
+	    );
+
+	void interface_added(const std::string& interface_name);
+	void interface_removed(const std::string& interface_name);
+
+private:
+	DBusConnection *mConnection;
+	std::map<std::string, NCPControlInterface*> mInterfaceMap;
+	std::map<std::string, std::string> mExternalInterfaceMap;
+	DBusIPCAPI_v0 mAPI_v0;
+	DBusIPCAPI_v1 mAPI_v1;
+};
+
+};
+};
+
+
+#endif
diff --git a/src/ipc-dbus/DBusIPCAPI_v0.cpp b/src/ipc-dbus/DBusIPCAPI_v0.cpp
new file mode 100644
index 0000000..19dcf43
--- /dev/null
+++ b/src/ipc-dbus/DBusIPCAPI_v0.cpp
@@ -0,0 +1,1612 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <string.h>
+#include <errno.h>
+#include <algorithm>
+
+#include <boost/bind.hpp>
+
+#include <dbus/dbus.h>
+
+#include "DBusIPCAPI_v0.h"
+#include "wpan-dbus-v0.h"
+
+#include "NCPControlInterface.h"
+#include "NCPTypes.h"
+#include "NCPMfgInterface.h"
+#include "assert-macros.h"
+
+#include "DBUSHelpers.h"
+#include "any-to.h"
+#include "wpan-error.h"
+
+using namespace DBUSHelpers;
+using namespace nl;
+using namespace nl::wpantund;
+
+DBusIPCAPI_v0::DBusIPCAPI_v0(DBusConnection *connection)
+	:mConnection(connection)
+{
+	dbus_connection_ref(mConnection);
+	init_callback_tables();
+}
+
+DBusIPCAPI_v0::~DBusIPCAPI_v0()
+{
+	dbus_connection_unref(mConnection);
+}
+
+void
+DBusIPCAPI_v0::init_callback_tables()
+{
+#define INTERFACE_CALLBACK_CONNECT(cmd_name, member_func) \
+	mInterfaceCallbackTable[(cmd_name)] = boost::bind( \
+		&DBusIPCAPI_v0::member_func, this, _1, _2)
+
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_JOIN, interface_join_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_FORM, interface_form_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_BEGIN_NET_WAKE, interface_begin_net_wake_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_PERMIT_JOIN, interface_permit_join_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_LEAVE, interface_leave_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_DATA_POLL, interface_data_poll_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_CONFIG_GATEWAY, interface_config_gateway_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_ADD_ROUTE, interface_add_route_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_REMOVE_ROUTE, interface_remove_route_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_BEGIN_LOW_POWER, interface_begin_low_power_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_PING, interface_ping_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_HOST_DID_WAKE, interface_host_did_wake_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_STOP_SCAN, interface_stop_scan_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_GET_PROP, interface_get_prop_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_SET_PROP, interface_set_prop_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_RESET, interface_reset_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_STATUS, interface_status_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_ACTIVE_SCAN, interface_active_scan_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_RESUME, interface_resume_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_MFG_BEGIN_TEST, interface_mfg_begin_test_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_MFG_END_TEST, interface_mfg_end_test_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_MFG_TX_PACKET, interface_mfg_tx_packet_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_MFG_FINISH, interface_mfg_finish_handler);
+
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_MFG_CLOCKMON, interface_mfg_clockmon_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_MFG_GPIO_SET, interface_mfg_gpio_set_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_MFG_GPIO_GET, interface_mfg_gpio_get_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_MFG_CHANNELCAL, interface_mfg_channelcal_handler);
+	INTERFACE_CALLBACK_CONNECT(WPAN_IFACE_CMD_MFG_CHANNELCAL_GET, interface_mfg_channelcal_get_handler);
+}
+
+void
+DBusIPCAPI_v0::CallbackWithStatus_Helper(
+    int ret, DBusMessage *original_message
+    )
+{
+	DBusMessage *reply = dbus_message_new_method_return(original_message);
+
+	syslog(LOG_DEBUG, "Sending DBus response for \"%s\" to \"%s\"", dbus_message_get_member(original_message), dbus_message_get_sender(original_message));
+
+	if(reply) {
+		dbus_message_append_args(
+			reply,
+			DBUS_TYPE_INT32, &ret,
+			DBUS_TYPE_INVALID
+			);
+
+		dbus_connection_send(mConnection, reply, NULL);
+		dbus_message_unref(reply);
+	}
+	dbus_message_unref(original_message);
+}
+
+static void
+ipc_append_network_properties(
+    DBusMessageIter *iter, const WPAN::NetworkInstance& network
+    )
+{
+	const char* network_name = network.name.c_str();
+
+	if (network_name[0])
+		append_dict_entry(iter,
+		                  "NetworkName",
+		                  DBUS_TYPE_STRING,
+		                  &network_name);
+
+	if (network.get_xpanid_as_uint64() != 0) {
+		uint64_t xpan_id = network.get_xpanid_as_uint64();
+		append_dict_entry(iter, "XPanId", DBUS_TYPE_UINT64, &xpan_id);
+	}
+
+	if (network.panid && network.panid != 0xFFFF) {
+		uint16_t pan_id = network.panid;
+		append_dict_entry(iter, "PanId", DBUS_TYPE_UINT16, &pan_id);
+	}
+
+	if (network.channel) {
+		uint16_t channel = network.channel;
+		append_dict_entry(iter, "Channel", DBUS_TYPE_INT16, &channel);
+
+		if (network.rssi != -128) {
+			int8_t rssi = network.rssi;
+			append_dict_entry(iter, "RSSI", DBUS_TYPE_BYTE, &rssi);
+		}
+
+		dbus_bool_t allowing_join = network.joinable;
+		append_dict_entry(iter,
+		                  "AllowingJoin",
+		                  DBUS_TYPE_BOOLEAN,
+		                  &allowing_join);
+	}
+
+	if (network.type != 0) {
+		int32_t type = network.type;
+		append_dict_entry(iter, "Type", DBUS_TYPE_INT32, &type);
+	}
+
+	if (network.get_hwaddr_as_uint64() != 0)
+		append_dict_entry(iter, "BeaconHWAddr",
+		                  nl::Data(network.hwaddr, 8));
+
+}
+
+static void
+ipc_append_network_dict(
+    DBusMessageIter *iter, const WPAN::NetworkInstance& network
+    )
+{
+	DBusMessageIter dict;
+
+	dbus_message_iter_open_container(
+	    iter,
+	    DBUS_TYPE_ARRAY,
+	    DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+	    DBUS_TYPE_STRING_AS_STRING
+	    DBUS_TYPE_VARIANT_AS_STRING
+	    DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+	    &dict
+	    );
+	ipc_append_network_properties(&dict, network);
+
+
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+static void
+ipc_append_networks(
+    DBusMessageIter *iter, const std::list<WPAN::NetworkInstance>& networks
+    )
+{
+	DBusMessageIter array;
+
+	dbus_message_iter_open_container(
+	    iter,
+	    DBUS_TYPE_ARRAY,
+	    DBUS_TYPE_ARRAY_AS_STRING
+	    DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+	    DBUS_TYPE_STRING_AS_STRING
+	    DBUS_TYPE_VARIANT_AS_STRING
+	    DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+	    &array
+	    );
+
+	std::list<WPAN::NetworkInstance>::const_iterator i;
+	std::list<WPAN::NetworkInstance>::const_iterator end = networks.end();
+	for (i = networks.begin(); i != end; ++i) {
+		ipc_append_network_dict(&array, *i);
+	}
+
+	dbus_message_iter_close_container(iter, &array);
+}
+
+void
+DBusIPCAPI_v0::received_beacon(NCPControlInterface* interface, const WPAN::NetworkInstance& network)
+{
+	mReceivedBeacons.push_back(network);
+}
+
+void
+DBusIPCAPI_v0::scan_response_helper(
+    int ret,
+    DBusMessage *                                                   original_message
+)
+{
+	DBusMessage *reply = dbus_message_new_method_return(original_message);
+
+	if(reply) {
+		DBusMessageIter msg_iter;
+		dbus_message_append_args(
+			reply,
+			DBUS_TYPE_INT32, &ret,
+			DBUS_TYPE_INVALID
+			);
+
+		dbus_message_iter_init_append(reply, &msg_iter);
+
+		ipc_append_networks(&msg_iter, mReceivedBeacons);
+
+		dbus_connection_send(mConnection, reply, NULL);
+		dbus_message_unref(reply);
+	}
+	dbus_message_unref(original_message);
+}
+
+void
+DBusIPCAPI_v0::status_response_helper(
+    int ret, NCPControlInterface* interface, DBusMessage *message
+    )
+{
+	DBusMessage *reply = dbus_message_new_method_return(message);
+
+	if (reply) {
+		DBusMessageIter iter;
+		dbus_message_iter_init_append(reply, &iter);
+
+		DBusMessageIter dict;
+		boost::any value;
+		NCPState ncp_state = UNINITIALIZED;
+		std::string ncp_state_string;
+		const char* ncp_state_cstr = kWPANTUNDStateUninitialized;
+
+		dbus_message_iter_open_container(
+			&iter,
+			DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING
+			DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+			&dict
+		);
+
+		value = interface->get_property(kWPANTUNDProperty_NCPState);
+
+		if (!value.empty()) {
+			ncp_state_string = any_to_string(value);
+			ncp_state = string_to_ncp_state(ncp_state_string);
+			ncp_state_cstr = ncp_state_string.c_str();
+		}
+
+		append_dict_entry(&dict,
+						  kWPANTUNDProperty_NCPState,
+						  DBUS_TYPE_STRING,
+						  &ncp_state_cstr);
+
+		if (ncp_state_is_commissioned(ncp_state))
+		{
+			value = interface->get_property(kWPANTUNDProperty_NetworkName);
+			if (!value.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_NetworkName, value);
+			}
+
+			value = interface->get_property(kWPANTUNDProperty_NetworkXPANID);
+			if (!value.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_NetworkXPANID, value);
+			}
+
+			value = interface->get_property(kWPANTUNDProperty_NetworkPANID);
+			if (!value.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_NetworkPANID, value);
+			}
+
+			value = interface->get_property(kWPANTUNDProperty_NCPChannel);
+			if (!value.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_NCPChannel, value);
+			}
+
+			value = interface->get_property(kWPANTUNDProperty_IPv6LinkLocalAddress);
+			if (!value.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_IPv6LinkLocalAddress, value);
+			}
+
+			value = interface->get_property(kWPANTUNDProperty_IPv6MeshLocalAddress);
+			if (!value.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_IPv6MeshLocalAddress, value);
+			}
+
+			value = interface->get_property(kWPANTUNDProperty_NestLabs_LegacyMeshLocalAddress);
+			if (!value.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_NestLabs_LegacyMeshLocalAddress, value);
+			}
+
+			value = interface->get_property(kWPANTUNDProperty_IPv6MeshLocalPrefix);
+			if (!value.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_IPv6MeshLocalPrefix, value);
+			}
+
+			value = interface->get_property(kWPANTUNDProperty_NestLabs_LegacyMeshLocalPrefix);
+			if (!value.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_NestLabs_LegacyMeshLocalPrefix, value);
+			}
+
+			value = interface->get_property(kWPANTUNDProperty_NestLabs_NetworkAllowingJoin);
+			if (!value.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_NestLabs_NetworkAllowingJoin, value);
+			}
+
+			value = interface->get_property(kWPANTUNDProperty_NetworkNodeType);
+			if (!value.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_NetworkNodeType, value);
+			}
+		}
+
+		value = interface->get_property(kWPANTUNDProperty_DaemonEnabled);
+		if (!value.empty()) {
+			append_dict_entry(&dict, kWPANTUNDProperty_DaemonEnabled, value);
+		}
+
+		value = interface->get_property(kWPANTUNDProperty_NCPVersion);
+		if (!value.empty()) {
+			append_dict_entry(&dict, kWPANTUNDProperty_NCPVersion, value);
+		}
+
+		value = interface->get_property(kWPANTUNDProperty_DaemonVersion);
+		if (!value.empty()) {
+			append_dict_entry(&dict, kWPANTUNDProperty_DaemonVersion, value);
+		}
+
+		value = interface->get_property(kWPANTUNDProperty_NCPHardwareAddress);
+		if (!value.empty()) {
+			append_dict_entry(&dict, kWPANTUNDProperty_NCPHardwareAddress, value);
+		}
+
+		dbus_message_iter_close_container(&iter, &dict);
+
+		dbus_connection_send(mConnection, reply, NULL);
+		dbus_message_unref(reply);
+	}
+	dbus_message_unref(message);
+}
+
+void
+DBusIPCAPI_v0::CallbackWithStatusArg1_Helper(
+    int status, const boost::any& value, DBusMessage *message
+)
+{
+	DBusMessage *reply = dbus_message_new_method_return(message);
+	DBusMessageIter iter;
+
+	syslog(LOG_DEBUG, "Sending getprop response");
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	if (!status && value.empty()) {
+		status = kWPANTUNDStatus_PropertyEmpty;
+	}
+
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &status);
+
+	if (value.empty()) {
+		append_any_to_dbus_iter(&iter, std::string("<empty>"));
+	} else {
+		append_any_to_dbus_iter(&iter, value);
+	}
+
+	dbus_connection_send(mConnection, reply, NULL);
+	dbus_message_unref(message);
+	dbus_message_unref(reply);
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_join_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	const char* network_name = NULL;
+	int16_t node_type_int16 = ROUTER;
+	ValueMap options;
+	uint64_t xpanid;
+	uint16_t panid = 0xFFFF;
+	uint8_t channel = 0;
+
+	dbus_message_get_args(
+		message, NULL,
+		DBUS_TYPE_STRING, &network_name,
+		DBUS_TYPE_INT16, &node_type_int16,
+		DBUS_TYPE_UINT64, &xpanid,
+		DBUS_TYPE_UINT16, &panid,
+		DBUS_TYPE_BYTE, &channel,
+		DBUS_TYPE_INVALID
+	);
+
+	require(network_name != NULL, bail);
+
+	options[kWPANTUNDProperty_NetworkName] = std::string(network_name);
+	options[kWPANTUNDProperty_NetworkXPANID] = xpanid;
+	options[kWPANTUNDProperty_NetworkPANID] = panid;
+	options[kWPANTUNDProperty_NCPChannel] = channel;
+
+	if (node_type_int16) {
+		options[kWPANTUNDProperty_NetworkNodeType] = (int)node_type_int16;
+	}
+
+	dbus_message_ref(message);
+
+	interface->join(
+		options,
+		boost::bind(
+			&DBusIPCAPI_v0::CallbackWithStatus_Helper,
+			this,
+			_1,
+			message
+		)
+	);
+
+bail:
+
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_form_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	const char* network_name;
+	ValueMap options;
+	int16_t node_type_int16 = 0;
+	NCPControlInterface::ChannelMask channel_mask = 0;
+	uint8_t *ula_prefix = NULL;
+	int ula_prefix_len = 0;
+
+	dbus_message_get_args(
+		message, NULL,
+		DBUS_TYPE_STRING, &network_name,
+		DBUS_TYPE_INT16, &node_type_int16,
+		DBUS_TYPE_UINT32, &channel_mask,
+		DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &ula_prefix, &ula_prefix_len,
+		DBUS_TYPE_INVALID
+	);
+
+	if (node_type_int16) {
+		options[kWPANTUNDProperty_NetworkNodeType] = (int)node_type_int16;
+	}
+
+	if (channel_mask) {
+		options[kWPANTUNDProperty_NCPChannelMask] = channel_mask;
+	}
+
+	if (ula_prefix_len) {
+		options[kWPANTUNDProperty_NestLabs_LegacyMeshLocalPrefix] = Data(ula_prefix, ula_prefix_len);
+	}
+
+	{	// The mesh local prefix can be set by setting it before forming.
+		boost::any value;
+		value = interface->get_property(kWPANTUNDProperty_IPv6MeshLocalPrefix);
+		if (!value.empty()) {
+			options[kWPANTUNDProperty_IPv6MeshLocalPrefix] = value;
+		}
+	}
+
+	options[kWPANTUNDProperty_NetworkName] = std::string(network_name);
+
+	dbus_message_ref(message);
+
+	interface->form(
+		options,
+		boost::bind(
+			&DBusIPCAPI_v0::CallbackWithStatus_Helper,
+			this,
+			_1,
+			message
+		)
+	);
+
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_begin_net_wake_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	uint8_t data = 0;
+	uint32_t flags = 0;
+
+	dbus_message_ref(message);
+	dbus_message_get_args(
+		message, NULL,
+		DBUS_TYPE_BYTE, &data,
+		DBUS_TYPE_UINT32, &flags,
+		DBUS_TYPE_INVALID
+		);
+
+	interface->begin_net_wake(
+		data,
+		flags,
+		boost::bind(&DBusIPCAPI_v0::CallbackWithStatus_Helper, this, _1,
+					message)
+	);
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_permit_join_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	int32_t seconds = -1;
+	dbus_bool_t network_wide = FALSE;
+	uint8_t commissioning_traffic_type = 0xFF;
+	in_port_t commissioning_traffic_port = 0;
+
+	dbus_message_ref(message);
+	dbus_message_get_args(
+		message, NULL,
+		DBUS_TYPE_INT32, &seconds,
+		DBUS_TYPE_BOOLEAN, &network_wide,
+		DBUS_TYPE_UINT16, &commissioning_traffic_port,
+		DBUS_TYPE_BYTE, &commissioning_traffic_type,
+		DBUS_TYPE_INVALID
+		);
+	commissioning_traffic_port = htons(commissioning_traffic_port);
+
+	if(seconds==-1)
+		seconds = 5*60;
+
+	interface->permit_join(
+		seconds,
+		commissioning_traffic_type,
+		commissioning_traffic_port,
+		network_wide,
+		boost::bind(&DBusIPCAPI_v0::CallbackWithStatus_Helper, this, _1,
+					message)
+		);
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_leave_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	interface->leave(boost::bind(&DBusIPCAPI_v0::CallbackWithStatus_Helper,
+								 this, _1, message));
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_data_poll_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	interface->data_poll(boost::bind(&DBusIPCAPI_v0::CallbackWithStatus_Helper,
+								 this, _1, message));
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_config_gateway_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	dbus_bool_t defaultRoute = FALSE;
+	uint32_t preferredLifetime = 0;
+	uint32_t validLifetime = 0;
+	uint8_t *prefix = NULL;
+	int prefixLen = 0;
+
+	dbus_message_ref(message);
+	dbus_message_get_args(
+		message, NULL,
+		DBUS_TYPE_BOOLEAN, &defaultRoute,
+		DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &prefix, &prefixLen,
+		DBUS_TYPE_UINT32, &preferredLifetime,
+		DBUS_TYPE_UINT32, &validLifetime,
+		DBUS_TYPE_INVALID
+	);
+
+	if (0 == prefixLen) {
+		prefix = NULL;
+	}
+
+	interface->config_gateway(
+		defaultRoute,
+		prefix,
+		preferredLifetime,
+		validLifetime,
+		boost::bind(&DBusIPCAPI_v0::CallbackWithStatus_Helper,this, _1, message)
+	);
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_add_route_handler(
+   NCPControlInterface* interface,
+   DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	uint8_t *route_prefix = NULL;
+	int prefix_len = 0;
+	uint16_t domain_id = 0;
+	int16_t priority_raw;
+	NCPControlInterface::ExternalRoutePriority priority;
+
+	dbus_message_ref(message);
+	dbus_message_get_args(
+		message, NULL,
+		DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &route_prefix, &prefix_len,
+		DBUS_TYPE_UINT16, &domain_id,
+		DBUS_TYPE_INT16, &priority_raw,
+		DBUS_TYPE_INVALID
+	);
+
+	if (0 == prefix_len) {
+	   route_prefix = NULL;
+	}
+
+	priority = NCPControlInterface::ROUTE_MEDIUM_PREFERENCE;
+	if  (priority_raw > 0) {
+		priority = NCPControlInterface::ROUTE_HIGH_PREFERENCE;
+	} else if (priority_raw < 0) {
+		priority = NCPControlInterface::ROUTE_LOW_PREFRENCE;
+	}
+
+	interface->add_external_route(
+		route_prefix,
+		prefix_len,
+		domain_id,
+		priority,
+		boost::bind(&DBusIPCAPI_v0::CallbackWithStatus_Helper, this, _1, message)
+	);
+
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_remove_route_handler(
+   NCPControlInterface* interface,
+   DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	uint8_t *route_prefix = NULL;
+	int prefix_len = 0;
+	uint16_t domain_id = 0;
+
+	dbus_message_ref(message);
+	dbus_message_get_args(
+		message, NULL,
+		DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &route_prefix, &prefix_len,
+		DBUS_TYPE_UINT16, &domain_id,
+		DBUS_TYPE_INVALID
+	);
+
+	if (0 == prefix_len) {
+		route_prefix = NULL;
+	}
+
+	interface->remove_external_route(
+		route_prefix,
+		prefix_len,
+		domain_id,
+		boost::bind(&DBusIPCAPI_v0::CallbackWithStatus_Helper, this, _1, message)
+	);
+
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_begin_low_power_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	interface->begin_low_power(boost::bind(&DBusIPCAPI_v0::CallbackWithStatus_Helper,
+								 this, _1, message));
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_ping_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	interface->refresh_state(boost::bind(&DBusIPCAPI_v0::CallbackWithStatus_Helper,
+								 this, _1, message));
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_host_did_wake_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	interface->host_did_wake(boost::bind(&DBusIPCAPI_v0::CallbackWithStatus_Helper,
+								 this, _1, message));
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_stop_scan_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	interface->netscan_stop(boost::bind(&DBusIPCAPI_v0::CallbackWithStatus_Helper,
+									 this, _1, message));
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_get_prop_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	const char* property_key_cstr = "";
+	std::string property_key;
+	dbus_message_get_args(
+		message, NULL,
+		DBUS_TYPE_STRING, &property_key_cstr,
+		DBUS_TYPE_INVALID
+		);
+
+	property_key = property_key_cstr;
+
+	if (interface->translate_deprecated_property(property_key)) {
+		syslog(LOG_WARNING, "GetProp: Property \"%s\" is deprecated. Please use \"%s\" instead.", property_key_cstr, property_key.c_str());
+	}
+
+	dbus_message_ref(message);
+	interface->get_property(property_key,
+							boost::bind(&DBusIPCAPI_v0::CallbackWithStatusArg1_Helper, this,
+										_1, _2, message));
+
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_set_prop_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	DBusMessageIter iter;
+	const char* property_key_cstr = "";
+	std::string property_key;
+	boost::any property_value;
+
+	dbus_message_iter_init(message, &iter);
+
+	require (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING, bail);
+
+	dbus_message_iter_get_basic(&iter, &property_key_cstr);
+	dbus_message_iter_next(&iter);
+
+	property_value = any_from_dbus_iter(&iter);
+	property_key = property_key_cstr;
+
+	if (interface->translate_deprecated_property(property_key, property_value)) {
+		syslog(LOG_WARNING, "SetProp: Property \"%s\" is deprecated. Please use \"%s\" instead.", property_key_cstr, property_key.c_str());
+	}
+
+	dbus_message_ref(message);
+	interface->set_property(
+		property_key,
+		property_value,
+		boost::bind(&DBusIPCAPI_v0::CallbackWithStatus_Helper, this, _1,
+					message)
+		);
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+bail:
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_reset_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	interface->reset(boost::bind(&DBusIPCAPI_v0::CallbackWithStatus_Helper,
+								 this, _1, message));
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_status_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	NCPState ncp_state = UNINITIALIZED;
+	boost::any value(interface->get_property(kWPANTUNDProperty_NCPState));
+
+	if (!value.empty()) {
+		ncp_state = string_to_ncp_state(any_to_string(value));
+	}
+
+	if (ncp_state_is_sleeping(ncp_state)
+	 || ncp_state_is_detached_from_ncp(ncp_state)
+	 || (ncp_state == UNINITIALIZED)
+	) {
+		status_response_helper(0, interface, message);
+	} else {
+		interface->refresh_state(boost::bind(&DBusIPCAPI_v0::
+										 status_response_helper, this, _1, interface, message));
+	}
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_active_scan_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	int32_t period = 0;
+	ValueMap options;
+	NCPControlInterface::ChannelMask channel_mask = 0;
+
+	dbus_message_ref(message);
+
+	dbus_message_get_args(
+		message, NULL,
+		DBUS_TYPE_INT32, &period,
+		DBUS_TYPE_UINT32, &channel_mask,
+		DBUS_TYPE_INVALID
+	);
+
+	if (channel_mask) {
+		options[kWPANTUNDProperty_NCPChannelMask] = channel_mask;
+	}
+
+	if (period) {
+		// Ignoring period for now
+	}
+
+	mReceivedBeacons.clear();
+
+	interface->netscan_start(
+		options,
+		boost::bind(
+			&DBusIPCAPI_v0::scan_response_helper,
+			this,
+			_1,
+			message
+		)
+	);
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_resume_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	interface->attach(boost::bind(&DBusIPCAPI_v0::CallbackWithStatus_Helper,
+								  this, _1, message));
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_mfg_finish_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	NCPMfgInterface* mfg_interface(dynamic_cast<NCPMfgInterface*>(interface));
+
+	if (mfg_interface) {
+		dbus_message_ref(message);
+
+		mfg_interface->mfg_finish(
+			boost::bind(
+				&DBusIPCAPI_v0::CallbackWithStatusArg1_Helper,
+				this,
+				_1,
+				_2,
+				message
+			)
+		);
+
+		ret = DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_mfg_begin_test_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	NCPMfgInterface* mfg_interface(dynamic_cast<NCPMfgInterface*>(interface));
+
+	if (mfg_interface) {
+		dbus_message_ref(message);
+
+		int16_t test_type = 0;
+
+		dbus_message_get_args(
+			message, NULL,
+			DBUS_TYPE_INT16, &test_type
+		);
+
+		mfg_interface->mfg_begin_test(
+			test_type,
+			boost::bind(
+				&DBusIPCAPI_v0::CallbackWithStatus_Helper,
+				this,
+				_1,
+				message
+			)
+		);
+
+		ret = DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_mfg_end_test_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	int16_t test_type = 0;
+	NCPMfgInterface* mfg_interface(dynamic_cast<NCPMfgInterface*>(interface));
+
+	if (mfg_interface) {
+		dbus_message_ref(message);
+
+		dbus_message_get_args(
+			message, NULL,
+			DBUS_TYPE_INT16, &test_type
+		);
+
+		mfg_interface->mfg_end_test(test_type, boost::bind(&DBusIPCAPI_v0::CallbackWithStatus_Helper,this, _1, message));
+
+		ret = DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_mfg_tx_packet_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	const uint8_t *packet_data = NULL;
+	int packet_length = 0;
+	int16_t repeat = 1;
+	NCPMfgInterface* mfg_interface(dynamic_cast<NCPMfgInterface*>(interface));
+
+	if (mfg_interface) {
+		dbus_message_ref(message);
+
+		dbus_message_get_args(
+			message, NULL,
+			DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &packet_data, &packet_length,
+			DBUS_TYPE_INT16, &repeat,
+			DBUS_TYPE_INVALID
+		);
+
+		mfg_interface->mfg_tx_packet(
+			Data(packet_data, packet_length),
+			repeat,
+			boost::bind(&DBusIPCAPI_v0::CallbackWithStatus_Helper,this, _1, message)
+		);
+
+		ret = DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::message_handler(
+    NCPControlInterface* interface,
+    DBusConnection *        connection,
+    DBusMessage *           message
+    )
+{
+	if ((dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL)
+		&& dbus_message_has_interface(message, WPAN_TUNNEL_DBUS_INTERFACE)
+		&& mInterfaceCallbackTable.count(dbus_message_get_member(message))
+	) {
+		return mInterfaceCallbackTable[dbus_message_get_member(message)](
+			interface,
+			message
+		);
+	}
+
+	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_mfg_clockmon_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	NCPMfgInterface* mfg_interface(dynamic_cast<NCPMfgInterface*>(interface));
+
+	if (mfg_interface) {
+		dbus_message_ref(message);
+
+		dbus_bool_t enabled;
+		uint32_t timerId;
+
+		dbus_message_get_args(
+			message, NULL,
+			DBUS_TYPE_BOOLEAN, &enabled,
+			DBUS_TYPE_UINT32, &timerId,
+			DBUS_TYPE_INVALID
+		);
+
+		mfg_interface->mfg_clockmon(
+			static_cast<bool>(enabled),
+			timerId,
+			boost::bind(
+				&DBusIPCAPI_v0::CallbackWithStatus_Helper,
+				this,
+				_1,
+				message
+			)
+		);
+
+		ret = DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_mfg_gpio_set_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	NCPMfgInterface* mfg_interface(dynamic_cast<NCPMfgInterface*>(interface));
+
+	if (mfg_interface) {
+		dbus_message_ref(message);
+
+		uint8_t port_pin;
+		uint8_t config;
+		uint8_t value;
+
+		dbus_message_get_args(
+			message, NULL,
+			DBUS_TYPE_BYTE, &port_pin,
+			DBUS_TYPE_BYTE, &config,
+			DBUS_TYPE_BYTE, &value,
+			DBUS_TYPE_INVALID
+		);
+
+		mfg_interface->mfg_gpio_set(
+			port_pin,
+			config,
+			value,
+			boost::bind(
+				&DBusIPCAPI_v0::CallbackWithStatus_Helper,
+				this,
+				_1,
+				message
+			)
+		);
+
+		ret = DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_mfg_gpio_get_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	NCPMfgInterface* mfg_interface(dynamic_cast<NCPMfgInterface*>(interface));
+
+	if (mfg_interface) {
+		dbus_message_ref(message);
+
+		uint8_t port_pin;
+
+		dbus_message_get_args(
+			message, NULL,
+			DBUS_TYPE_BYTE, &port_pin,
+			DBUS_TYPE_INVALID
+		);
+
+		mfg_interface->mfg_gpio_get(
+			port_pin,
+			boost::bind(
+				&DBusIPCAPI_v0::CallbackWithStatusArg1_Helper,
+				this,
+				_1,
+				_2,
+				message
+			)
+		);
+
+		ret = DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+	return ret;
+}
+
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_mfg_channelcal_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	NCPMfgInterface* mfg_interface(dynamic_cast<NCPMfgInterface*>(interface));
+
+	if (mfg_interface) {
+		dbus_message_ref(message);
+
+		uint8_t channel;
+		uint32_t duration;
+
+		dbus_message_get_args(
+			message, NULL,
+			DBUS_TYPE_BYTE, &channel,
+			DBUS_TYPE_UINT32, &duration,
+			DBUS_TYPE_INVALID
+		);
+
+		mfg_interface->mfg_channelcal(
+			channel,
+			duration,
+			boost::bind(
+				&DBusIPCAPI_v0::CallbackWithStatus_Helper,
+				this,
+				_1,
+				message
+			)
+		);
+
+		ret = DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v0::interface_mfg_channelcal_get_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	NCPMfgInterface* mfg_interface(dynamic_cast<NCPMfgInterface*>(interface));
+
+	if (mfg_interface) {
+		dbus_message_ref(message);
+
+		uint8_t channel;
+
+		dbus_message_get_args(
+			message, NULL,
+			DBUS_TYPE_BYTE, &channel,
+			DBUS_TYPE_INVALID
+		);
+
+		mfg_interface->mfg_channelcal_get(
+			channel,
+			boost::bind(
+				&DBusIPCAPI_v0::CallbackWithStatusArg1_Helper,
+				this,
+				_1,
+				_2,
+				message
+			)
+		);
+
+		ret = DBUS_HANDLER_RESULT_HANDLED;
+	}
+
+	return ret;
+}
+
+void
+DBusIPCAPI_v0::ncp_state_changed(NCPControlInterface* interface)
+{
+	DBusMessage* signal;
+	boost::any value;
+	NCPState ncp_state = UNINITIALIZED;
+	std::string ncp_state_str;
+	const char* ncp_state_cstr = kWPANTUNDStateUninitialized;
+
+	value = interface->get_property(kWPANTUNDProperty_NCPState);
+
+	if (!value.empty()) {
+		ncp_state_str = any_to_string(value);
+		ncp_state = string_to_ncp_state(ncp_state_str);
+		ncp_state_cstr = ncp_state_str.c_str();
+	}
+
+	DBusMessageIter iter;
+
+	syslog(LOG_DEBUG,
+	       "DBus Sending Association State Changed to %s",
+	       ncp_state_cstr);
+	signal = dbus_message_new_signal(
+	    (std::string(WPAN_TUNNEL_DBUS_PATH) + "/" + interface->get_name()).c_str(),
+	    WPAN_TUNNEL_DBUS_INTERFACE,
+	    WPAN_IFACE_SIGNAL_STATE_CHANGED
+	    );
+	dbus_message_append_args(
+	    signal,
+	    DBUS_TYPE_STRING, &ncp_state_cstr,
+	    DBUS_TYPE_INVALID
+	    );
+
+	dbus_message_iter_init_append(signal, &iter);
+
+	DBusMessageIter dict;
+
+	dbus_message_iter_open_container(
+	    &iter,
+	    DBUS_TYPE_ARRAY,
+	    DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+	    DBUS_TYPE_STRING_AS_STRING
+	    DBUS_TYPE_VARIANT_AS_STRING
+	    DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+	    &dict
+	    );
+
+	append_dict_entry(&dict, kWPANTUNDProperty_DaemonEnabled, interface->get_property(kWPANTUNDProperty_DaemonEnabled));
+
+	if (ncp_state_is_commissioned(ncp_state)) {
+		ipc_append_network_properties(&dict,
+	                              interface->get_current_network_instance());
+		boost::any prefix = interface->get_property(kWPANTUNDProperty_IPv6MeshLocalPrefix);
+		if (!prefix.empty()) {
+			append_dict_entry(&dict, kWPANTUNDProperty_IPv6MeshLocalPrefix, prefix);
+		}
+
+		prefix = interface->get_property(kWPANTUNDProperty_NestLabs_LegacyMeshLocalAddress);
+		if (!prefix.empty()) {
+			append_dict_entry(&dict, kWPANTUNDProperty_NestLabs_LegacyMeshLocalAddress, prefix);
+		}
+
+		append_dict_entry(&dict, kWPANTUNDProperty_NetworkNodeType, interface->get_property(kWPANTUNDProperty_NetworkNodeType));
+
+		if (ncp_state >= ASSOCIATED) {
+			boost::any networkKey = interface->get_property(kWPANTUNDProperty_NetworkKey);
+			if (!networkKey.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_NetworkKey, networkKey);
+			}
+		}
+	}
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	dbus_connection_send(mConnection, signal, NULL);
+	dbus_message_unref(signal);
+}
+
+
+static void
+ObjectPathUnregisterFunction_cb(DBusConnection *connection, void *user_data)
+{
+	delete (std::pair<NCPControlInterface*, DBusIPCAPI_v0*> *)user_data;
+}
+
+int
+DBusIPCAPI_v0::add_interface(NCPControlInterface* interface)
+{
+	static const DBusObjectPathVTable ipc_interface_vtable = {
+		&ObjectPathUnregisterFunction_cb,
+		&DBusIPCAPI_v0::dbus_message_handler,
+	};
+
+	std::string name = interface->get_name();
+	std::string path = std::string(WPAN_TUNNEL_DBUS_PATH) + "/" + name;
+
+	std::pair<NCPControlInterface*, DBusIPCAPI_v0*> *cb_data =
+	    new std::pair<NCPControlInterface*, DBusIPCAPI_v0*>(interface, this);
+
+	NCPMfgInterface* mfg_interface(dynamic_cast<NCPMfgInterface*>(interface));
+
+	require(dbus_connection_register_object_path(
+	            mConnection,
+	            path.c_str(),
+	            &ipc_interface_vtable,
+	            (void*)cb_data
+	            ), bail);
+
+	if (mfg_interface) {
+		mfg_interface->mOnMfgRXPacket.connect(
+			boost::bind(
+				&DBusIPCAPI_v0::mfg_rx_packet,
+				this,
+				interface,
+				_1,
+				_2,
+				_3
+			)
+		);
+	}
+
+	interface->mOnPropertyChanged.connect(
+	    boost::bind(
+			&DBusIPCAPI_v0::property_changed,
+			this,
+			interface,
+			_1,
+			_2
+		)
+	);
+
+	interface->mOnNetScanBeacon.connect(
+	    boost::bind(
+			&DBusIPCAPI_v0::received_beacon,
+			this,
+			interface,
+			_1
+		)
+	);
+
+bail:
+	return 0;
+}
+
+void
+DBusIPCAPI_v0::property_changed(NCPControlInterface* interface,const std::string& key, const boost::any& value)
+{
+	DBusMessage* signal;
+	DBusMessageIter iter;
+	std::string key_as_path;
+	std::string path;
+
+	// Transform the key into a DBus-compatible path
+	for (std::string::const_iterator i = key.begin();
+		i != key.end();
+		++i
+	) {
+		const char c = *i;
+		if (isalnum(c) || (c == '_')) {
+			key_as_path += c;
+		} else if (c == ':') {
+			key_as_path += '/';
+		} else if (c == '.') {
+			key_as_path += '_';
+		}
+	}
+
+	if (key == kWPANTUNDProperty_NestLabs_NetworkWakeRemaining) {
+		uint8_t data = static_cast<uint8_t>(any_to_int(interface->get_property(kWPANTUNDProperty_NestLabs_NetworkWakeData)));
+		net_wake_event(interface, data, any_to_int(value));
+	} else if (key == kWPANTUNDProperty_DaemonReadyForHostSleep) {
+		if (any_to_bool(value)) {
+			allow_sleep(interface);
+		} else {
+			prevent_sleep(interface);
+		}
+	} else if (key == kWPANTUNDProperty_NCPState) {
+		ncp_state_changed(interface);
+	} else if (key == kWPANTUNDProperty_NetworkNodeType) {
+		property_changed(interface, "NCPNodeType", value);
+	}
+
+	signal = dbus_message_new_signal(
+		(
+			std::string(WPAN_TUNNEL_DBUS_PATH) + "/" + interface->get_name()
+			+ "/" + WPAN_TUNNEL_DBUS_PATH_PROPERTIES + "/" + key_as_path
+		).c_str(),
+		WPAN_TUNNEL_DBUS_INTERFACE,
+		WPAN_IFACE_SIGNAL_PROPERTY_CHANGED
+	);
+
+	dbus_message_iter_init_append(signal, &iter);
+
+	append_any_to_dbus_iter(&iter, key);
+	append_any_to_dbus_iter(&iter, value);
+
+	dbus_connection_send(mConnection, signal, NULL);
+	dbus_message_unref(signal);
+}
+
+void
+DBusIPCAPI_v0::net_wake_event(NCPControlInterface* interface,uint8_t data, cms_t ms_remaining)
+{
+	DBusMessage* signal;
+
+	signal = dbus_message_new_signal(
+	    (std::string(WPAN_TUNNEL_DBUS_PATH) + "/" + interface->get_name()).c_str(),
+	    WPAN_TUNNEL_DBUS_INTERFACE,
+	    WPAN_IFACE_SIGNAL_NET_WAKE
+	    );
+	dbus_message_append_args(
+	    signal,
+	    DBUS_TYPE_BYTE, &data,
+	    DBUS_TYPE_INT32, &ms_remaining,
+	    DBUS_TYPE_INVALID
+	);
+
+	dbus_connection_send(mConnection, signal, NULL);
+	dbus_message_unref(signal);
+}
+
+void
+DBusIPCAPI_v0::mfg_rx_packet(NCPControlInterface* interface, Data packet, uint8_t lqi, int8_t rssi)
+{
+	DBusMessageIter iter;
+	DBusMessage* signal;
+
+	signal = dbus_message_new_signal(
+	    (std::string(WPAN_TUNNEL_DBUS_PATH) + "/" + interface->get_name()).c_str(),
+	    WPAN_TUNNEL_DBUS_INTERFACE,
+	    WPAN_IFACE_SIGNAL_MFG_RX
+	    );
+
+	dbus_message_iter_init_append(signal, &iter);
+
+	append_any_to_dbus_iter(&iter, packet);
+	append_any_to_dbus_iter(&iter, lqi);
+	append_any_to_dbus_iter(&iter, rssi);
+
+	dbus_connection_send(mConnection, signal, NULL);
+	dbus_message_unref(signal);
+}
+
+void
+DBusIPCAPI_v0::prevent_sleep(NCPControlInterface* interface)
+{
+	DBusMessage* signal;
+
+	signal = dbus_message_new_signal(
+	    (std::string(WPAN_TUNNEL_DBUS_PATH) + "/" + interface->get_name()).c_str(),
+	    WPAN_TUNNEL_DBUS_INTERFACE,
+	    WPAN_IFACE_SIGNAL_PREVENT_SLEEP
+	);
+	dbus_connection_send(mConnection, signal, NULL);
+	dbus_message_unref(signal);
+}
+
+void
+DBusIPCAPI_v0::allow_sleep(NCPControlInterface* interface)
+{
+	DBusMessage* signal;
+
+	signal = dbus_message_new_signal(
+	    (std::string(WPAN_TUNNEL_DBUS_PATH) + "/" + interface->get_name()).c_str(),
+	    WPAN_TUNNEL_DBUS_INTERFACE,
+	    WPAN_IFACE_SIGNAL_ALLOW_SLEEP
+	);
+	dbus_connection_send(mConnection, signal, NULL);
+	dbus_message_unref(signal);
+}
+
+
+DBusHandlerResult
+DBusIPCAPI_v0::dbus_message_handler(
+    DBusConnection *connection,
+    DBusMessage *   message,
+    void *                  user_data
+    )
+{
+	if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) {
+		syslog(LOG_INFO, "Inbound DBus message for INTERFACE \"%s\" from \"%s\"", dbus_message_get_member(message), dbus_message_get_sender(message));
+	}
+	std::pair<NCPControlInterface*,
+	          DBusIPCAPI_v0*> *cb_data =
+	    (std::pair<NCPControlInterface*, DBusIPCAPI_v0*> *)user_data;
+	return cb_data->second->message_handler(cb_data->first,
+	                                                  connection,
+	                                                  message);
+}
diff --git a/src/ipc-dbus/DBusIPCAPI_v0.h b/src/ipc-dbus/DBusIPCAPI_v0.h
new file mode 100644
index 0000000..390d73c
--- /dev/null
+++ b/src/ipc-dbus/DBusIPCAPI_v0.h
@@ -0,0 +1,218 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef wpantund_DBusIPCAPI_v0_h
+#define wpantund_DBusIPCAPI_v0_h
+
+#include <map>
+#include <list>
+
+#include <dbus/dbus.h>
+
+#include <boost/bind.hpp>
+#include <boost/any.hpp>
+#include <boost/function.hpp>
+
+#include "NetworkInstance.h"
+#include "Data.h"
+#include "time-utils.h"
+
+namespace nl {
+namespace wpantund {
+
+class NCPControlInterface;
+
+class DBusIPCAPI_v0 {
+public:
+	DBusIPCAPI_v0(DBusConnection *connection);
+	~DBusIPCAPI_v0();
+
+	int add_interface(NCPControlInterface* interface);
+
+private:
+
+	DBusHandlerResult message_handler(
+		NCPControlInterface* interface,
+		DBusConnection *connection,
+		DBusMessage *message
+    );
+
+	static DBusHandlerResult dbus_message_handler(
+		DBusConnection *connection,
+		DBusMessage *message,
+		void *user_data
+	);
+
+	void init_callback_tables(void);
+
+	// ------------------------------------------------------------------------
+
+	void CallbackWithStatus_Helper(int ret, DBusMessage *original_message);
+	void CallbackWithStatusArg1_Helper(int ret, const boost::any& value, DBusMessage *original_message);
+
+	void status_response_helper(int ret, NCPControlInterface* interface, DBusMessage *original_message);
+	void scan_response_helper(int ret, DBusMessage *original_message);
+
+	// ------------------------------------------------------------------------
+
+	void ncp_state_changed(NCPControlInterface* interface);
+	void net_wake_event(NCPControlInterface* interface,uint8_t data, cms_t ms_remaining);
+	void prevent_sleep(NCPControlInterface* interface);
+	void allow_sleep(NCPControlInterface* interface);
+	void property_changed(NCPControlInterface* interface,const std::string& key, const boost::any& value);
+	void received_beacon(NCPControlInterface* interface, const WPAN::NetworkInstance& network);
+
+	void mfg_rx_packet(NCPControlInterface* interface, nl::Data packet, uint8_t lqi, int8_t rssi);
+
+	// ------------------------------------------------------------------------
+
+	DBusHandlerResult interface_join_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_form_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_begin_net_wake_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_permit_join_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_add_route_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_remove_route_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_leave_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_data_poll_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_config_gateway_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_begin_low_power_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_ping_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_host_did_wake_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_stop_scan_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_get_prop_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_set_prop_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_reset_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_status_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_active_scan_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_resume_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_mfg_begin_test_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_mfg_end_test_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_mfg_tx_packet_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_mfg_finish_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_mfg_clockmon_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+
+	DBusHandlerResult interface_mfg_gpio_set_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+
+	DBusHandlerResult interface_mfg_gpio_get_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+
+	DBusHandlerResult interface_mfg_channelcal_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+
+	DBusHandlerResult interface_mfg_channelcal_get_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+
+private:
+	typedef DBusHandlerResult (interface_handler_cb)(
+		NCPControlInterface*,
+		DBusMessage *
+	);
+
+	DBusConnection *mConnection;
+	std::list<WPAN::NetworkInstance> mReceivedBeacons;
+	std::map<std::string, boost::function<interface_handler_cb> > mInterfaceCallbackTable;
+
+}; // class DBusIPCAPI_v0
+
+}; // namespace nl
+}; // namespace wpantund
+
+
+#endif
diff --git a/src/ipc-dbus/DBusIPCAPI_v1.cpp b/src/ipc-dbus/DBusIPCAPI_v1.cpp
new file mode 100644
index 0000000..8eb76b7
--- /dev/null
+++ b/src/ipc-dbus/DBusIPCAPI_v1.cpp
@@ -0,0 +1,984 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <algorithm>
+
+#include <boost/bind.hpp>
+
+#include <dbus/dbus.h>
+
+#include "wpan-dbus-v1.h"
+#include "DBusIPCAPI_v1.h"
+#include "wpan-error.h"
+
+#include "NCPControlInterface.h"
+#include "NCPTypes.h"
+#include "assert-macros.h"
+
+#include "DBUSHelpers.h"
+#include "any-to.h"
+
+using namespace DBUSHelpers;
+using namespace nl;
+using namespace nl::wpantund;
+
+DBusIPCAPI_v1::DBusIPCAPI_v1(DBusConnection *connection)
+	:mConnection(connection)
+{
+	dbus_connection_ref(mConnection);
+	init_callback_tables();
+}
+
+DBusIPCAPI_v1::~DBusIPCAPI_v1()
+{
+	dbus_connection_unref(mConnection);
+}
+
+void
+DBusIPCAPI_v1::init_callback_tables()
+{
+#define INTERFACE_CALLBACK_CONNECT(cmd_name, member_func) \
+	mInterfaceCallbackTable[(cmd_name)] = boost::bind( \
+		&DBusIPCAPI_v1::member_func, this, _1, _2)
+
+	INTERFACE_CALLBACK_CONNECT(WPANTUND_IF_CMD_RESET, interface_reset_handler);
+	INTERFACE_CALLBACK_CONNECT(WPANTUND_IF_CMD_STATUS, interface_status_handler);
+
+	INTERFACE_CALLBACK_CONNECT(WPANTUND_IF_CMD_JOIN, interface_join_handler);
+	INTERFACE_CALLBACK_CONNECT(WPANTUND_IF_CMD_FORM, interface_form_handler);
+	INTERFACE_CALLBACK_CONNECT(WPANTUND_IF_CMD_LEAVE, interface_leave_handler);
+	INTERFACE_CALLBACK_CONNECT(WPANTUND_IF_CMD_ATTACH, interface_attach_handler);
+
+	INTERFACE_CALLBACK_CONNECT(WPANTUND_IF_CMD_ROUTE_ADD, interface_route_add_handler);
+	INTERFACE_CALLBACK_CONNECT(WPANTUND_IF_CMD_ROUTE_REMOVE, interface_route_remove_handler);
+
+	INTERFACE_CALLBACK_CONNECT(WPANTUND_IF_CMD_DATA_POLL, interface_data_poll_handler);
+	INTERFACE_CALLBACK_CONNECT(WPANTUND_IF_CMD_CONFIG_GATEWAY, interface_config_gateway_handler);
+
+	INTERFACE_CALLBACK_CONNECT(WPANTUND_IF_CMD_BEGIN_LOW_POWER, interface_begin_low_power_handler);
+	INTERFACE_CALLBACK_CONNECT(WPANTUND_IF_CMD_HOST_DID_WAKE, interface_host_did_wake_handler);
+	INTERFACE_CALLBACK_CONNECT(WPANTUND_IF_CMD_NET_SCAN_STOP, interface_net_scan_stop_handler);
+	INTERFACE_CALLBACK_CONNECT(WPANTUND_IF_CMD_NET_SCAN_START, interface_net_scan_start_handler);
+
+	INTERFACE_CALLBACK_CONNECT(WPANTUND_IF_CMD_PROP_GET, interface_get_prop_handler);
+	INTERFACE_CALLBACK_CONNECT(WPANTUND_IF_CMD_PROP_SET, interface_set_prop_handler);
+}
+
+static void
+ObjectPathUnregisterFunction_cb(DBusConnection *connection, void *user_data)
+{
+	delete (std::pair<NCPControlInterface*, DBusIPCAPI_v1*> *)user_data;
+}
+
+std::string
+DBusIPCAPI_v1::path_for_iface(NCPControlInterface* interface)
+{
+	return std::string(WPANTUND_DBUS_PATH) + "/" + interface->get_name();
+}
+
+int
+DBusIPCAPI_v1::add_interface(NCPControlInterface* interface)
+{
+	static const DBusObjectPathVTable ipc_interface_vtable = {
+		&ObjectPathUnregisterFunction_cb,
+		&DBusIPCAPI_v1::dbus_message_handler,
+	};
+
+	std::string name = interface->get_name();
+	std::string path = std::string(WPANTUND_DBUS_PATH) + "/" + name;
+
+	std::pair<NCPControlInterface*, DBusIPCAPI_v1*> *cb_data =
+	    new std::pair<NCPControlInterface*, DBusIPCAPI_v1*>(interface, this);
+
+	require(dbus_connection_register_object_path(
+	            mConnection,
+	            path.c_str(),
+	            &ipc_interface_vtable,
+	            (void*)cb_data
+	            ), bail);
+
+	interface->mOnPropertyChanged.connect(
+	    boost::bind(
+			&DBusIPCAPI_v1::property_changed,
+			this,
+			interface,
+			_1,
+			_2
+		)
+	);
+
+	interface->mOnNetScanBeacon.connect(
+	    boost::bind(
+			&DBusIPCAPI_v1::received_beacon,
+			this,
+			interface,
+			_1
+		)
+	);
+
+
+bail:
+	return 0;
+}
+
+void
+DBusIPCAPI_v1::CallbackWithStatus_Helper(int ret, DBusMessage *original_message)
+{
+	DBusMessage *reply = dbus_message_new_method_return(original_message);
+
+	syslog(LOG_DEBUG, "Sending DBus response for \"%s\" to \"%s\"", dbus_message_get_member(original_message), dbus_message_get_sender(original_message));
+
+	if(reply) {
+		dbus_message_append_args(
+			reply,
+			DBUS_TYPE_INT32, &ret,
+			DBUS_TYPE_INVALID
+		);
+
+		dbus_connection_send(mConnection, reply, NULL);
+		dbus_message_unref(reply);
+	}
+	dbus_message_unref(original_message);
+}
+
+static void
+ipc_append_network_properties(
+    DBusMessageIter *iter, const WPAN::NetworkInstance& network
+    )
+{
+	const char* network_name = network.name.c_str();
+
+	if (network_name[0]) {
+		append_dict_entry(
+			iter,
+			kWPANTUNDProperty_NetworkName,
+			DBUS_TYPE_STRING,
+			&network_name
+		);
+	}
+
+	if (network.get_xpanid_as_uint64() != 0) {
+		uint64_t xpan_id = network.get_xpanid_as_uint64();
+		append_dict_entry(
+			iter,
+			kWPANTUNDProperty_NetworkXPANID,
+			DBUS_TYPE_UINT64,
+			&xpan_id
+		);
+	}
+
+	{
+		uint16_t pan_id = network.panid;
+		append_dict_entry(
+			iter,
+			kWPANTUNDProperty_NetworkPANID,
+			DBUS_TYPE_UINT16,
+			&pan_id
+		);
+	}
+
+	if (network.type != 0) {
+		int32_t type = network.type;
+		append_dict_entry(
+			iter,
+			kWPANTUNDProperty_NetworkNodeType,
+			DBUS_TYPE_INT32,
+			&type
+		);
+	}
+
+	if (network.channel) {
+		uint16_t channel = network.channel;
+		append_dict_entry(
+			iter,
+			kWPANTUNDProperty_NCPChannel,
+			DBUS_TYPE_INT16,
+			&channel
+		);
+
+		if (network.rssi != -128) {
+			int8_t rssi = network.rssi;
+			append_dict_entry(iter, "RSSI", DBUS_TYPE_BYTE, &rssi);
+		}
+
+		dbus_bool_t allowing_join = network.joinable;
+		append_dict_entry(
+			iter,
+			kWPANTUNDProperty_NestLabs_NetworkAllowingJoin,
+			DBUS_TYPE_BOOLEAN,
+			&allowing_join
+		);
+	}
+
+	if (network.get_hwaddr_as_uint64() != 0) {
+		append_dict_entry(
+			iter,
+			kWPANTUNDProperty_NCPHardwareAddress,
+			nl::Data(network.hwaddr, 8)
+		);
+	}
+}
+
+static void
+ipc_append_network_dict(
+    DBusMessageIter *iter, const WPAN::NetworkInstance& network
+    )
+{
+	DBusMessageIter dict;
+
+	dbus_message_iter_open_container(
+	    iter,
+	    DBUS_TYPE_ARRAY,
+	    DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+	    DBUS_TYPE_STRING_AS_STRING
+	    DBUS_TYPE_VARIANT_AS_STRING
+	    DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+	    &dict
+	    );
+	ipc_append_network_properties(&dict, network);
+
+	dbus_message_iter_close_container(iter, &dict);
+}
+
+void
+DBusIPCAPI_v1::received_beacon(NCPControlInterface* interface, const WPAN::NetworkInstance& network)
+{
+	DBusMessageIter iter;
+	DBusMessage* signal;
+
+	signal = dbus_message_new_signal(
+		path_for_iface(interface).c_str(),
+		WPANTUND_DBUS_APIv1_INTERFACE,
+		WPANTUND_IF_SIGNAL_NET_SCAN_BEACON
+    );
+
+	dbus_message_iter_init_append(signal, &iter);
+
+	ipc_append_network_dict(&iter, network);
+
+	dbus_connection_send(mConnection, signal, NULL);
+
+	dbus_message_unref(signal);
+}
+void
+DBusIPCAPI_v1::property_changed(NCPControlInterface* interface,const std::string& key, const boost::any& value)
+{
+	DBusMessageIter iter;
+	DBusMessage* signal;
+	std::string key_as_path;
+	std::string path;
+
+	// Transform the key into a DBus-compatible path
+	for (std::string::const_iterator i = key.begin();
+		i != key.end();
+		++i
+	) {
+		const char c = *i;
+		if (isalnum(c) || (c == '_')) {
+			key_as_path += c;
+		} else if (c == ':') {
+			key_as_path += '/';
+		} else if (c == '.') {
+			key_as_path += '_';
+		}
+	}
+
+	path = path_for_iface(interface) + "/Property/" + key_as_path;
+
+	syslog(LOG_DEBUG, "DBusAPIv1:PropChanged: %s - value: %s", path.c_str(), any_to_string(value).c_str());
+
+	signal = dbus_message_new_signal(
+		path.c_str(),
+		WPANTUND_DBUS_APIv1_INTERFACE,
+		WPANTUND_IF_SIGNAL_PROP_CHANGED
+    );
+
+	if (signal) {
+		dbus_message_iter_init_append(signal, &iter);
+
+		append_any_to_dbus_iter(&iter, key);
+		append_any_to_dbus_iter(&iter, value);
+
+		dbus_connection_send(mConnection, signal, NULL);
+		dbus_message_unref(signal);
+	}
+}
+
+void
+DBusIPCAPI_v1::status_response_helper(
+    int ret, NCPControlInterface* interface, DBusMessage *message
+    )
+{
+	DBusMessage *reply = dbus_message_new_method_return(message);
+
+	if (reply) {
+		DBusMessageIter iter;
+		dbus_message_iter_init_append(reply, &iter);
+
+		DBusMessageIter dict;
+		boost::any value;
+		NCPState ncp_state = UNINITIALIZED;
+		std::string ncp_state_string;
+		const char* ncp_state_cstr = kWPANTUNDStateUninitialized;
+
+		dbus_message_iter_open_container(
+			&iter,
+			DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING
+			DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+			&dict
+		);
+
+		value = interface->get_property(kWPANTUNDProperty_NCPState);
+
+		if (!value.empty()) {
+			ncp_state_string = any_to_string(value);
+			ncp_state = string_to_ncp_state(ncp_state_string);
+			ncp_state_cstr = ncp_state_string.c_str();
+		}
+
+		append_dict_entry(&dict,
+						  kWPANTUNDProperty_NCPState,
+						  DBUS_TYPE_STRING,
+						  &ncp_state_cstr);
+
+		value = interface->get_property(kWPANTUNDProperty_DaemonEnabled);
+		if (!value.empty()) {
+			append_dict_entry(&dict, kWPANTUNDProperty_DaemonEnabled, value);
+		}
+
+		value = interface->get_property(kWPANTUNDProperty_NCPVersion);
+		if (!value.empty()) {
+			append_dict_entry(&dict, kWPANTUNDProperty_NCPVersion, value);
+		}
+
+		value = interface->get_property(kWPANTUNDProperty_DaemonVersion);
+		if (!value.empty()) {
+			append_dict_entry(&dict, kWPANTUNDProperty_DaemonVersion, value);
+		}
+
+		value = interface->get_property(kWPANTUNDProperty_ConfigNCPDriverName);
+		if (!value.empty()) {
+			append_dict_entry(&dict, kWPANTUNDProperty_ConfigNCPDriverName, value);
+		}
+
+		value = interface->get_property(kWPANTUNDProperty_NCPHardwareAddress);
+		if (!value.empty()) {
+			append_dict_entry(&dict, kWPANTUNDProperty_NCPHardwareAddress, value);
+		}
+
+		if (ncp_state_is_commissioned(ncp_state))
+		{
+			value = interface->get_property(kWPANTUNDProperty_NCPChannel);
+			if (!value.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_NCPChannel, value);
+			}
+
+			value = interface->get_property(kWPANTUNDProperty_NetworkNodeType);
+			if (!value.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_NetworkNodeType, value);
+			}
+
+			value = interface->get_property(kWPANTUNDProperty_NetworkName);
+			if (!value.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_NetworkName, value);
+			}
+
+			value = interface->get_property(kWPANTUNDProperty_NetworkXPANID);
+			if (!value.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_NetworkXPANID, value);
+			}
+
+			value = interface->get_property(kWPANTUNDProperty_NetworkPANID);
+			if (!value.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_NetworkPANID, value);
+			}
+
+			value = interface->get_property(kWPANTUNDProperty_IPv6LinkLocalAddress);
+			if (!value.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_IPv6LinkLocalAddress, value);
+			}
+
+			value = interface->get_property(kWPANTUNDProperty_IPv6MeshLocalAddress);
+			if (!value.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_IPv6MeshLocalAddress, value);
+			}
+
+			value = interface->get_property(kWPANTUNDProperty_IPv6MeshLocalPrefix);
+			if (!value.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_IPv6MeshLocalPrefix, value);
+			}
+
+			value = interface->get_property(kWPANTUNDProperty_NestLabs_LegacyMeshLocalAddress);
+			if (!value.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_NestLabs_LegacyMeshLocalAddress, value);
+			}
+
+			value = interface->get_property(kWPANTUNDProperty_NestLabs_LegacyMeshLocalPrefix);
+			if (!value.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_NestLabs_LegacyMeshLocalPrefix, value);
+			}
+
+			value = interface->get_property(kWPANTUNDProperty_NestLabs_NetworkAllowingJoin);
+			if (!value.empty()) {
+				append_dict_entry(&dict, kWPANTUNDProperty_NestLabs_NetworkAllowingJoin, value);
+			}
+		}
+
+		dbus_message_iter_close_container(&iter, &dict);
+
+		dbus_connection_send(mConnection, reply, NULL);
+		dbus_message_unref(reply);
+	}
+	dbus_message_unref(message);
+}
+
+void
+DBusIPCAPI_v1::CallbackWithStatusArg1_Helper(
+    int status, const boost::any& value, DBusMessage *message
+)
+{
+	DBusMessage *reply = dbus_message_new_method_return(message);
+	DBusMessageIter iter;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	if (!status && value.empty()) {
+		status = kWPANTUNDStatus_PropertyEmpty;
+	}
+
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &status);
+
+	if (value.empty()) {
+		append_any_to_dbus_iter(&iter, std::string("<empty>"));
+	} else {
+		append_any_to_dbus_iter(&iter, value);
+	}
+
+	dbus_connection_send(mConnection, reply, NULL);
+	dbus_message_unref(message);
+	dbus_message_unref(reply);
+}
+
+
+DBusHandlerResult
+DBusIPCAPI_v1::interface_reset_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	interface->reset(boost::bind(&DBusIPCAPI_v1::CallbackWithStatus_Helper,
+								 this, _1, message));
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v1::interface_status_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	NCPState ncp_state = UNINITIALIZED;
+	boost::any value(interface->get_property(kWPANTUNDProperty_NCPState));
+
+	if (!value.empty()) {
+		ncp_state = string_to_ncp_state(any_to_string(value));
+	}
+
+	if (ncp_state_is_sleeping(ncp_state)
+	 || ncp_state_is_detached_from_ncp(ncp_state)
+	 || (ncp_state == UNINITIALIZED)
+	) {
+		status_response_helper(0, interface, message);
+	} else {
+		interface->refresh_state(boost::bind(&DBusIPCAPI_v1::
+										 status_response_helper, this, _1, interface, message));
+	}
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v1::interface_join_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	ValueMap options;
+	DBusMessageIter iter;
+
+	dbus_message_iter_init(message, &iter);
+
+	options = value_map_from_dbus_iter(&iter);
+
+	dbus_message_ref(message);
+
+	interface->join(
+		options,
+		boost::bind(
+			&DBusIPCAPI_v1::CallbackWithStatus_Helper,
+			this,
+			_1,
+			message
+		)
+	);
+
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v1::interface_form_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	ValueMap options;
+	DBusMessageIter iter;
+
+	dbus_message_iter_init(message, &iter);
+
+	options = value_map_from_dbus_iter(&iter);
+
+	dbus_message_ref(message);
+
+	interface->form(
+		options,
+		boost::bind(
+			&DBusIPCAPI_v1::CallbackWithStatus_Helper,
+			this,
+			_1,
+			message
+		)
+	);
+
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v1::interface_leave_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	interface->leave(boost::bind(&DBusIPCAPI_v1::CallbackWithStatus_Helper,
+								 this, _1, message));
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v1::interface_attach_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	interface->attach(boost::bind(&DBusIPCAPI_v1::CallbackWithStatus_Helper,
+								  this, _1, message));
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v1::interface_begin_low_power_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	interface->begin_low_power(boost::bind(&DBusIPCAPI_v1::CallbackWithStatus_Helper,
+								 this, _1, message));
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v1::interface_host_did_wake_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	interface->host_did_wake(boost::bind(&DBusIPCAPI_v1::CallbackWithStatus_Helper,
+								 this, _1, message));
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v1::interface_net_scan_stop_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	interface->netscan_stop(boost::bind(&DBusIPCAPI_v1::CallbackWithStatus_Helper,
+									 this, _1, message));
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+
+DBusHandlerResult
+DBusIPCAPI_v1::interface_get_prop_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	const char* property_key_cstr = "";
+	std::string property_key;
+	dbus_message_get_args(
+		message, NULL,
+		DBUS_TYPE_STRING, &property_key_cstr,
+		DBUS_TYPE_INVALID
+		);
+
+	property_key = property_key_cstr;
+
+	if (interface->translate_deprecated_property(property_key)) {
+		syslog(LOG_WARNING, "PropGet: Property \"%s\" is deprecated. Please use \"%s\" instead.", property_key_cstr, property_key.c_str());
+	}
+
+	dbus_message_ref(message);
+
+	interface->get_property(
+		property_key,
+		boost::bind(
+			&DBusIPCAPI_v1::CallbackWithStatusArg1_Helper,
+			this,
+			_1,
+			_2,
+			message
+		)
+	);
+
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v1::interface_set_prop_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	DBusMessageIter iter;
+	const char* property_key_cstr = "";
+	std::string property_key;
+	boost::any property_value;
+
+	dbus_message_iter_init(message, &iter);
+
+	require (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING, bail);
+
+	dbus_message_iter_get_basic(&iter, &property_key_cstr);
+	dbus_message_iter_next(&iter);
+
+	property_value = any_from_dbus_iter(&iter);
+	property_key = property_key_cstr;
+
+	if (interface->translate_deprecated_property(property_key, property_value)) {
+		syslog(LOG_WARNING, "PropSet: Property \"%s\" is deprecated. Please use \"%s\" instead.", property_key_cstr, property_key.c_str());
+	}
+
+	dbus_message_ref(message);
+
+	interface->set_property(
+		property_key,
+		property_value,
+		boost::bind(
+			&DBusIPCAPI_v1::CallbackWithStatus_Helper,
+			this,
+			_1,
+			message
+		)
+	);
+
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+bail:
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v1::interface_net_scan_start_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	ValueMap options;
+	NCPControlInterface::ChannelMask channel_mask = 0;
+
+	dbus_message_ref(message);
+
+	dbus_message_get_args(
+		message, NULL,
+		DBUS_TYPE_UINT32, &channel_mask,
+		DBUS_TYPE_INVALID
+	);
+
+	if (channel_mask) {
+		options[kWPANTUNDProperty_NCPChannelMask] = channel_mask;
+	}
+
+	interface->netscan_start(
+		options,
+		boost::bind(
+			&DBusIPCAPI_v1::CallbackWithStatus_Helper,
+			this,
+			_1,
+			message
+		)
+	);
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v1::interface_data_poll_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	interface->data_poll(boost::bind(&DBusIPCAPI_v1::CallbackWithStatus_Helper,
+								 this, _1, message));
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v1::interface_config_gateway_handler(
+	NCPControlInterface* interface,
+	DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	dbus_bool_t defaultRoute = FALSE;
+	uint32_t preferredLifetime = 0;
+	uint32_t validLifetime = 0;
+	uint8_t *prefix = NULL;
+	int prefixLen = 0;
+
+	dbus_message_ref(message);
+	dbus_message_get_args(
+		message, NULL,
+		DBUS_TYPE_BOOLEAN, &defaultRoute,
+		DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &prefix, &prefixLen,
+		DBUS_TYPE_UINT32, &preferredLifetime,
+		DBUS_TYPE_UINT32, &validLifetime,
+		DBUS_TYPE_INVALID
+	);
+
+	if (0 == prefixLen) {
+		prefix = NULL;
+	}
+
+	interface->config_gateway(
+		defaultRoute,
+		prefix,
+		preferredLifetime,
+		validLifetime,
+		boost::bind(&DBusIPCAPI_v1::CallbackWithStatus_Helper,this, _1, message)
+	);
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v1::interface_route_add_handler(
+   NCPControlInterface* interface,
+   DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	uint8_t *route_prefix = NULL;
+	int prefix_len = 0;
+	uint16_t domain_id = 0;
+	int16_t priority_raw;
+	NCPControlInterface::ExternalRoutePriority priority;
+
+	dbus_message_ref(message);
+	dbus_message_get_args(
+		message, NULL,
+		DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &route_prefix, &prefix_len,
+		DBUS_TYPE_UINT16, &domain_id,
+		DBUS_TYPE_INT16, &priority_raw,
+		DBUS_TYPE_INVALID
+	);
+
+	if (0 == prefix_len) {
+	   route_prefix = NULL;
+	}
+
+	priority = NCPControlInterface::ROUTE_MEDIUM_PREFERENCE;
+	if  (priority_raw > 0) {
+		priority = NCPControlInterface::ROUTE_HIGH_PREFERENCE;
+	} else if (priority_raw < 0) {
+		priority = NCPControlInterface::ROUTE_LOW_PREFRENCE;
+	}
+
+	interface->add_external_route(
+		route_prefix,
+		prefix_len,
+		domain_id,
+		priority,
+		boost::bind(&DBusIPCAPI_v1::CallbackWithStatus_Helper, this, _1, message)
+	);
+
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v1::interface_route_remove_handler(
+   NCPControlInterface* interface,
+   DBusMessage *        message
+) {
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	dbus_message_ref(message);
+
+	uint8_t *route_prefix = NULL;
+	int prefix_len = 0;
+	uint16_t domain_id = 0;
+
+	dbus_message_ref(message);
+	dbus_message_get_args(
+		message, NULL,
+		DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &route_prefix, &prefix_len,
+		DBUS_TYPE_UINT16, &domain_id,
+		DBUS_TYPE_INVALID
+	);
+
+	if (0 == prefix_len) {
+		route_prefix = NULL;
+	}
+
+	interface->remove_external_route(
+		route_prefix,
+		prefix_len,
+		domain_id,
+		boost::bind(&DBusIPCAPI_v1::CallbackWithStatus_Helper, this, _1, message)
+	);
+
+	ret = DBUS_HANDLER_RESULT_HANDLED;
+
+	return ret;
+}
+
+
+DBusHandlerResult
+DBusIPCAPI_v1::message_handler(
+    NCPControlInterface* interface,
+    DBusConnection *        connection,
+    DBusMessage *           message
+    )
+{
+	DBusHandlerResult ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+	if ((dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL)
+		&& dbus_message_has_interface(message, WPANTUND_DBUS_APIv1_INTERFACE)
+		&& mInterfaceCallbackTable.count(dbus_message_get_member(message))
+	) {
+		try {
+			ret = mInterfaceCallbackTable.at(dbus_message_get_member(message))(
+				interface,
+				message
+			);
+		} catch (std::invalid_argument x) {
+			DBusIPCAPI_v1::CallbackWithStatus_Helper(kWPANTUNDStatus_InvalidArgument,message);
+			ret = DBUS_HANDLER_RESULT_HANDLED;
+		}
+	}
+
+	return ret;
+}
+
+DBusHandlerResult
+DBusIPCAPI_v1::dbus_message_handler(
+    DBusConnection *connection,
+    DBusMessage *   message,
+    void *          user_data
+) {
+	if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) {
+		syslog(LOG_INFO, "Inbound DBus message for INTERFACE \"%s\" from \"%s\"", dbus_message_get_member(message), dbus_message_get_sender(message));
+	}
+	std::pair<NCPControlInterface*,
+	          DBusIPCAPI_v1*> *cb_data =
+	    (std::pair<NCPControlInterface*, DBusIPCAPI_v1*> *)user_data;
+	return cb_data->second->message_handler(cb_data->first,
+	                                                  connection,
+	                                                  message);
+}
diff --git a/src/ipc-dbus/DBusIPCAPI_v1.h b/src/ipc-dbus/DBusIPCAPI_v1.h
new file mode 100644
index 0000000..8668cb7
--- /dev/null
+++ b/src/ipc-dbus/DBusIPCAPI_v1.h
@@ -0,0 +1,175 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef wpantund_DBusIPCAPI_v1_h
+#define wpantund_DBusIPCAPI_v1_h
+
+#include <map>
+#include <list>
+
+#include <dbus/dbus.h>
+
+#include <boost/bind.hpp>
+#include <boost/any.hpp>
+#include <boost/function.hpp>
+
+#include "NetworkInstance.h"
+#include "Data.h"
+#include "time-utils.h"
+
+namespace nl {
+namespace wpantund {
+
+class NCPControlInterface;
+
+class DBusIPCAPI_v1 {
+public:
+	DBusIPCAPI_v1(DBusConnection *connection);
+	~DBusIPCAPI_v1();
+
+	int add_interface(NCPControlInterface* interface);
+
+private:
+
+	DBusHandlerResult message_handler(
+		NCPControlInterface* interface,
+		DBusConnection *connection,
+		DBusMessage *message
+    );
+
+	static DBusHandlerResult dbus_message_handler(
+		DBusConnection *connection,
+		DBusMessage *message,
+		void *user_data
+	);
+
+	void init_callback_tables(void);
+
+	std::string path_for_iface(NCPControlInterface* interface);
+
+	// ------------------------------------------------------------------------
+
+	void CallbackWithStatus_Helper(int ret, DBusMessage *original_message);
+	void CallbackWithStatusArg1_Helper(int ret, const boost::any& value, DBusMessage *original_message);
+
+	void status_response_helper(int ret, NCPControlInterface* interface, DBusMessage *original_message);
+	void scan_response_helper(int ret, DBusMessage *original_message);
+
+	// ------------------------------------------------------------------------
+
+	void property_changed(NCPControlInterface* interface,const std::string& key, const boost::any& value);
+	void received_beacon(NCPControlInterface* interface, const WPAN::NetworkInstance& network);
+
+	// ------------------------------------------------------------------------
+
+	DBusHandlerResult interface_route_add_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+
+	DBusHandlerResult interface_route_remove_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+
+	DBusHandlerResult interface_reset_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+
+	DBusHandlerResult interface_status_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+
+	DBusHandlerResult interface_join_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+
+	DBusHandlerResult interface_form_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+
+	DBusHandlerResult interface_leave_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+
+	DBusHandlerResult interface_attach_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+
+	DBusHandlerResult interface_begin_low_power_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+
+	DBusHandlerResult interface_host_did_wake_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+
+	DBusHandlerResult interface_get_prop_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+	DBusHandlerResult interface_set_prop_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+
+	DBusHandlerResult interface_net_scan_start_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+
+	DBusHandlerResult interface_net_scan_stop_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+
+	DBusHandlerResult interface_data_poll_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+
+	DBusHandlerResult interface_config_gateway_handler(
+		NCPControlInterface* interface,
+		DBusMessage *        message
+	);
+
+private:
+	typedef DBusHandlerResult (interface_handler_cb)(
+		NCPControlInterface*,
+		DBusMessage *
+	);
+
+	DBusConnection *mConnection;
+	std::map<std::string, boost::function<interface_handler_cb> > mInterfaceCallbackTable;
+
+}; // class DBusIPCAPI_v1
+
+}; // namespace nl
+}; // namespace wpantund
+
+
+#endif
diff --git a/src/ipc-dbus/Makefile.am b/src/ipc-dbus/Makefile.am
new file mode 100644
index 0000000..80f38af
--- /dev/null
+++ b/src/ipc-dbus/Makefile.am
@@ -0,0 +1,54 @@
+#
+# Copyright (c) 2016 Nest Labs, Inc.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src \
+	-I$(top_srcdir)/src/util \
+	-I$(top_srcdir)/src/ipc-dbus \
+	-I$(top_srcdir)/src/wpantund \
+	-I$(top_srcdir)/third_party/assert-macros \
+	$(NULL)
+
+EXTRA_DIST = wpantund.conf
+
+DISTCLEANFILES = .deps Makefile
+
+AUTOMAKE_OPTIONS = subdir-objects
+
+dbusconfdir = $(DBUS_CONFDIR)
+
+dbusconf_DATA = wpantund.conf
+
+noinst_LTLIBRARIES = libwpantund-dbus.la
+
+libwpantund_dbus_la_SOURCES = \
+	DBUSIPCServer.cpp \
+	DBUSIPCServer.h \
+	DBusIPCAPI_v1.cpp \
+	DBusIPCAPI_v1.h \
+	wpan-dbus-v1.h \
+	DBusIPCAPI_v0.cpp \
+	DBusIPCAPI_v0.h \
+	wpan-dbus-v0.h \
+	../util/DBUSHelpers.cpp \
+	$(NULL)
+
+libwpantund_dbus_la_LIBADD =  $(DBUS_LIBS)
+libwpantund_dbus_la_CPPFLAGS = $(AM_CPPFLAGS) $(DBUS_CFLAGS)
+libwpantund_dbus_la_CXXFLAGS = $(BOOST_CXXFLAGS)
+
+pkginclude_HEADERS = wpan-dbus-v0.h wpan-dbus-v1.h
diff --git a/src/ipc-dbus/wpan-dbus-v0.h b/src/ipc-dbus/wpan-dbus-v0.h
new file mode 100644
index 0000000..9677316
--- /dev/null
+++ b/src/ipc-dbus/wpan-dbus-v0.h
@@ -0,0 +1,99 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef wpantund_wpan_dbus_v0_h
+#define wpantund_wpan_dbus_v0_h
+
+#include "wpan-properties.h"
+
+#if !defined(wpantund_DBusIPCAPI_v0_h) && !defined(BUILD_FEATURE_WPANTUND)
+// Disabling these warnings for now.
+// Will re-enable once more stuff has moved over.
+//#warning wpan-dbus-v0.h is deprecated. Please migrate to the API in wpan-dbus-v1.h.
+#endif
+
+#define WPAN_TUNNEL_DBUS_NAME   "com.nestlabs.WPANTunnelDriver"
+#define WPAN_TUNNEL_DBUS_INTERFACE  "com.nestlabs.WPANTunnelDriver"
+#define WPAN_TUNNEL_DBUS_PATH   "/com/nestlabs/WPANTunnelDriver"
+#define WPAN_TUNNEL_DBUS_VERSION   2
+#define WPAN_TUNNEL_DBUS_PATH_PROPERTIES "Properties"
+
+#define WPAN_TUNNEL_CMD_GET_INTERFACES  "GetInterfaces"
+#define WPAN_TUNNEL_CMD_GET_VERSION     "GetVersion"
+
+#define WPAN_TUNNEL_SIGNAL_INTERFACE_ADDED			"InterfaceAdded"
+#define WPAN_TUNNEL_SIGNAL_INTERFACE_REMOVED		"InterfaceRemoved"
+
+#define WPAN_IFACE_SIGNAL_STATE_CHANGED	"AssociationStateChanged"
+#define WPAN_IFACE_SIGNAL_PROPERTY_CHANGED	"PropertyChanged"
+#define WPAN_IFACE_SIGNAL_NET_WAKE	    "NetWake"
+#define WPAN_IFACE_SIGNAL_PREVENT_SLEEP	"PreventSleep"
+#define WPAN_IFACE_SIGNAL_ALLOW_SLEEP	"AllowSleep"
+
+#define WPAN_IFACE_CMD_JOIN             "Join"
+#define WPAN_IFACE_CMD_FORM             "Form"
+#define WPAN_IFACE_CMD_LEAVE            "Leave"
+#define WPAN_IFACE_CMD_ACTIVE_SCAN      "Scan"
+#define WPAN_IFACE_CMD_RESUME           "Resume"
+#define WPAN_IFACE_CMD_PERMIT_JOIN      "PermitJoin"
+#define WPAN_IFACE_CMD_STOP_SCAN        "StopScan"
+#define WPAN_IFACE_CMD_RESET            "Reset"
+#define WPAN_IFACE_CMD_STATUS           "Status"
+#define WPAN_IFACE_CMD_PING             "Ping"
+#define WPAN_IFACE_CMD_SCAN             "Scan"
+#define WPAN_IFACE_CMD_BEGIN_NET_WAKE   "BeginNetWake"
+#define WPAN_IFACE_CMD_CONFIG_GATEWAY   "ConfigGateway"
+#define WPAN_IFACE_CMD_ADD_ROUTE        "AddRoute"
+#define WPAN_IFACE_CMD_REMOVE_ROUTE     "RemoveRoute"
+#define WPAN_IFACE_CMD_DATA_POLL        "DataPoll"
+#define WPAN_IFACE_CMD_HOST_DID_WAKE    "HostDidWake"
+
+#define WPAN_IFACE_CMD_BEGIN_LOW_POWER  "BeginLowPower"
+
+/* The property "MfgTestMode" must be enabled in order to use these commands */
+#define WPAN_IFACE_CMD_MFG_FINISH      "MfgFinish"
+#define WPAN_IFACE_CMD_MFG_BEGIN_TEST  "MfgBeginTest"	// Argument is test type
+#define WPAN_IFACE_CMD_MFG_END_TEST    "MfgEndTest"		// Argument is test type
+#define WPAN_IFACE_CMD_MFG_TX_PACKET   "MfgTXPacket"	// Argument is packet
+#define WPAN_IFACE_SIGNAL_MFG_RX       "MfgRXPacket"    // packet, lqi, rssi
+#define WPAN_IFACE_CMD_MFG_CLOCKMON       "MfgClockMon"
+#define WPAN_IFACE_CMD_MFG_GPIO_SET       "MfgGPIOSet"
+#define WPAN_IFACE_CMD_MFG_GPIO_GET       "MfgGPIOGet"
+#define WPAN_IFACE_CMD_MFG_CHANNELCAL     "MfgChannelCal"
+#define WPAN_IFACE_CMD_MFG_CHANNELCAL_GET "MfgChannelCalGet"
+/* Also, there are the following properties that can be set when in MFG mode:
+ *	"Channel", "TXPower", "SYNOffset"
+ */
+
+
+#define WPAN_IFACE_CMD_GET_PROP         "GetProp"
+#define WPAN_IFACE_CMD_SET_PROP         "SetProp"
+
+
+#define WPAN_IFACE_PROTOCOL_TCPUDP      0xFF
+#define WPAN_IFACE_PROTOCOL_TCP         6
+#define WPAN_IFACE_PROTOCOL_UDP         17
+
+#define WPAN_IFACE_ROLE_UNKNOWN             0
+#define WPAN_IFACE_ROLE_ROUTER              2
+#define WPAN_IFACE_ROLE_END_DEVICE          3
+#define WPAN_IFACE_ROLE_SLEEPY_END_DEVICE   4
+#define WPAN_IFACE_ROLE_LURKER              6
+
+#endif
diff --git a/src/ipc-dbus/wpan-dbus-v1.h b/src/ipc-dbus/wpan-dbus-v1.h
new file mode 100644
index 0000000..00c2e3c
--- /dev/null
+++ b/src/ipc-dbus/wpan-dbus-v1.h
@@ -0,0 +1,80 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef wpantund_wpan_dbus_v1_h
+#define wpantund_wpan_dbus_v1_h
+
+#include "wpan-properties.h"
+
+#define WPANTUND_DBUS_NAME                      "org.wpantund"
+#define WPANTUND_DBUS_PATH                      "/org/wpantund"
+
+// ============================================================================
+// Base Interface
+
+#define WPANTUND_DBUS_BASE_INTERFACE            "org.wpantund"
+
+#define WPANTUND_BASE_CMD_GET_INTERFACES        "GetInterfaces"
+
+#define WPANTUND_BASE_SIGNAL_INTERFACE_ADDED    "InterfaceAdded"
+#define WPANTUND_BASE_SIGNAL_INTERFACE_REMOVED  "InterfaceRemoved"
+
+#define WPANTUND_IF_GET_VERSION                 "GetVersion"
+
+// ============================================================================
+// Standard API Interface
+
+// Note that this API is not yet fully baked and may change before
+// being considered frozen.
+
+#define WPANTUND_DBUS_APIv1_INTERFACE         WPANTUND_DBUS_BASE_INTERFACE".v1"
+
+#define WPANTUND_IF_CMD_JOIN                  "Join"
+#define WPANTUND_IF_CMD_FORM                  "Form"
+#define WPANTUND_IF_CMD_LEAVE                 "Leave"
+#define WPANTUND_IF_CMD_ATTACH                "Attach"
+
+#define WPANTUND_IF_CMD_RESET                 "Reset"
+#define WPANTUND_IF_CMD_STATUS                "Status"
+
+#define WPANTUND_IF_CMD_ROUTE_ADD             "RouteAdd"
+#define WPANTUND_IF_CMD_ROUTE_REMOVE          "RouteRemove"
+#define WPANTUND_IF_CMD_CONFIG_GATEWAY        "ConfigGateway"
+#define WPANTUND_IF_CMD_DATA_POLL             "DataPoll"
+
+#define WPANTUND_IF_CMD_BEGIN_LOW_POWER       "BeginLowPower"
+#define WPANTUND_IF_CMD_HOST_DID_WAKE         "HostDidWake"
+
+#define WPANTUND_IF_CMD_NET_SCAN_START        "NetScanStart"
+#define WPANTUND_IF_CMD_NET_SCAN_STOP         "NetScanStop"
+#define WPANTUND_IF_SIGNAL_NET_SCAN_BEACON    "NetScanBeacon"
+
+#define WPANTUND_IF_CMD_PROP_GET              "PropGet"
+#define WPANTUND_IF_CMD_PROP_SET              "PropSet"
+#define WPANTUND_IF_SIGNAL_PROP_CHANGED       "PropChanged"
+
+// ============================================================================
+// NestLabs Internal API Interface
+
+#define WPANTUND_DBUS_NLAPIv1_INTERFACE       "com.nestlabs.wpantund.v1"
+
+#define WPANTUND_IF_CMD_PERMIT_JOIN           "PermitJoin"
+#define WPANTUND_IF_CMD_NETWORK_WAKE_BEGIN    "NetworkWakeBegin"
+
+#endif
diff --git a/src/ipc-dbus/wpantund.conf b/src/ipc-dbus/wpantund.conf
new file mode 100644
index 0000000..bf26bf1
--- /dev/null
+++ b/src/ipc-dbus/wpantund.conf
@@ -0,0 +1,44 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+    <policy user="root">
+        <allow own="org.wpantund"/>
+        <allow own="org.wpantund.wpanctl"/>
+
+        <allow send_interface="org.wpantund.Join"/>
+        <allow send_interface="org.wpantund.Form"/>
+        <allow send_interface="org.wpantund.Leave"/>
+        <allow send_interface="org.wpantund.Attach"/>
+        <allow send_interface="org.wpantund.Status"/>
+        <allow send_interface="org.wpantund.Reset"/>
+        <allow send_interface="org.wpantund.NetScanStart"/>
+        <allow send_interface="org.wpantund.NetScanStop"/>
+        <allow send_interface="org.wpantund.List"/>
+
+
+        <!-- Deprecated -->
+        <allow own="com.nestlabs.WPANTunnelDriver"/>
+        <allow own="com.nestlabs.WPANTunnelDriver.wpanctl"/>
+        <allow send_destination="com.nestlabs.WPANTunnelDriver"/>
+        <allow send_interface="com.nestlabs.WPANTunnelDriver.Join"/>
+        <allow send_interface="com.nestlabs.WPANTunnelDriver.Form"/>
+        <allow send_interface="com.nestlabs.WPANTunnelDriver.Leave"/>
+        <allow send_interface="com.nestlabs.WPANTunnelDriver.Resume"/>
+        <allow send_interface="com.nestlabs.WPANTunnelDriver.Scan"/>
+        <allow send_interface="com.nestlabs.WPANTunnelDriver.PermitJoin"/>
+        <allow send_interface="com.nestlabs.WPANTunnelDriver.Status"/>
+        <allow send_interface="com.nestlabs.WPANTunnelDriver.List"/>
+    </policy>
+    <policy at_console="true">
+        <allow send_destination="org.wpantund"/>
+
+        <!-- Deprecated -->
+        <allow send_destination="com.nestlabs.WPANTunnelDriver"/>
+    </policy>
+    <policy context="default">
+        <deny send_destination="org.wpantund"/>
+
+        <!-- Deprecated -->
+        <deny send_destination="com.nestlabs.WPANTunnelDriver"/>
+    </policy>
+</busconfig>
diff --git a/src/ncp-dummy/DummyNCPControlInterface.cpp b/src/ncp-dummy/DummyNCPControlInterface.cpp
new file mode 100644
index 0000000..c0bb063
--- /dev/null
+++ b/src/ncp-dummy/DummyNCPControlInterface.cpp
@@ -0,0 +1,220 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "assert-macros.h"
+
+#include "DummyNCPControlInterface.h"
+#include "DummyNCPInstance.h"
+
+#include "wpantund.h"
+#include "config-file.h"
+#include "nlpt.h"
+#include "string-utils.h"
+#include "any-to.h"
+#include "time-utils.h"
+
+#include <cstring>
+#include <algorithm>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <syslog.h>
+#include <sys/time.h>
+
+#include <boost/bind.hpp>
+
+using namespace nl;
+using namespace nl::wpantund;
+
+// ----------------------------------------------------------------------------
+// MARK: -
+
+DummyNCPControlInterface::DummyNCPControlInterface(DummyNCPInstance* instance_pointer)
+	:mNCPInstance(instance_pointer)
+{
+}
+
+// ----------------------------------------------------------------------------
+// MARK: -
+
+void
+DummyNCPControlInterface::join(
+	const ValueMap& options,
+    CallbackWithStatus cb
+) {
+	// TODO: Writeme!
+	cb(kWPANTUNDStatus_FeatureNotImplemented);
+}
+
+void
+DummyNCPControlInterface::form(
+	const ValueMap& options,
+    CallbackWithStatus cb
+) {
+
+	// TODO: Writeme!
+	cb(kWPANTUNDStatus_FeatureNotImplemented);
+}
+
+void
+DummyNCPControlInterface::leave(CallbackWithStatus cb)
+{
+	// TODO: Writeme!
+	cb(kWPANTUNDStatus_FeatureNotImplemented);
+}
+
+void
+DummyNCPControlInterface::attach(CallbackWithStatus cb)
+{
+	// TODO: Writeme!
+	cb(kWPANTUNDStatus_FeatureNotImplemented);
+}
+
+void
+DummyNCPControlInterface::reset(CallbackWithStatus cb)
+{
+	// TODO: Writeme!
+	cb(kWPANTUNDStatus_FeatureNotImplemented);
+}
+
+void
+DummyNCPControlInterface::begin_net_wake(uint8_t data, uint32_t flags, CallbackWithStatus cb)
+{
+	// TODO: Writeme!
+	cb(kWPANTUNDStatus_FeatureNotImplemented);
+}
+
+void
+DummyNCPControlInterface::host_did_wake(CallbackWithStatus cb)
+{
+	// TODO: Writeme!
+	cb(kWPANTUNDStatus_FeatureNotImplemented);
+}
+
+void
+DummyNCPControlInterface::begin_low_power(CallbackWithStatus cb)
+{
+	// TODO: Writeme!
+	cb(kWPANTUNDStatus_FeatureNotImplemented);
+}
+
+void
+DummyNCPControlInterface::refresh_state(CallbackWithStatus cb)
+{
+	// TODO: Writeme!
+	cb(kWPANTUNDStatus_FeatureNotImplemented);
+}
+
+void
+DummyNCPControlInterface::data_poll(CallbackWithStatus cb)
+{
+	cb(kWPANTUNDStatus_FeatureNotImplemented); // TODO: Send data poll command
+}
+
+void
+DummyNCPControlInterface::config_gateway(bool defaultRoute, const uint8_t prefix[8], uint32_t preferredLifetime, uint32_t validLifetime, CallbackWithStatus cb)
+{
+	cb(kWPANTUNDStatus_FeatureNotImplemented); // TODO: Send config gateway command
+}
+
+void
+DummyNCPControlInterface::add_external_route(const uint8_t *route, int route_prefix_len, int domain_id,
+	ExternalRoutePriority priority, CallbackWithStatus cb)
+{
+	cb(kWPANTUNDStatus_FeatureNotImplemented); // TODO: Send add external route command
+}
+
+void
+DummyNCPControlInterface::remove_external_route(const uint8_t *route, int route_prefix_len, int domain_id, CallbackWithStatus cb)
+{
+	cb(kWPANTUNDStatus_FeatureNotImplemented); // TODO: Send remove external route command
+}
+
+void
+DummyNCPControlInterface::permit_join(
+    int seconds,
+    uint8_t traffic_type,
+    in_port_t traffic_port,
+    bool network_wide,
+    CallbackWithStatus cb
+    )
+{
+	// TODO: Writeme!
+	cb(kWPANTUNDStatus_FeatureNotImplemented);
+}
+
+void
+DummyNCPControlInterface::netscan_start(
+    const ValueMap& options,
+    CallbackWithStatus cb
+) {
+	cb(kWPANTUNDStatus_FeatureNotImplemented); // TODO: Start network scan
+}
+
+void
+DummyNCPControlInterface::netscan_stop(CallbackWithStatus cb)
+{
+	cb(kWPANTUNDStatus_FeatureNotImplemented); // TODO: Start network scan
+}
+
+std::string
+DummyNCPControlInterface::get_name() {
+	return mNCPInstance->get_name();
+}
+
+const WPAN::NetworkInstance&
+DummyNCPControlInterface::get_current_network_instance()const
+{
+	return mNCPInstance->get_current_network_instance();
+}
+
+
+NCPInstance&
+DummyNCPControlInterface::get_ncp_instance()
+{
+	return (*mNCPInstance);
+}
+
+
+// ----------------------------------------------------------------------------
+// MARK: -
+
+void
+DummyNCPControlInterface::get_property(
+    const std::string& in_key, CallbackWithStatusArg1 cb
+    )
+{
+	syslog(LOG_INFO, "get_property: key: \"%s\"", in_key.c_str());
+	mNCPInstance->get_property(in_key, cb);
+}
+
+void
+DummyNCPControlInterface::set_property(
+    const std::string&                      key,
+    const boost::any&                       value,
+    CallbackWithStatus      cb
+    )
+{
+	syslog(LOG_INFO, "set_property: key: \"%s\"", key.c_str());
+	mNCPInstance->set_property(key, value, cb);
+
+}
diff --git a/src/ncp-dummy/DummyNCPControlInterface.h b/src/ncp-dummy/DummyNCPControlInterface.h
new file mode 100644
index 0000000..64b740a
--- /dev/null
+++ b/src/ncp-dummy/DummyNCPControlInterface.h
@@ -0,0 +1,103 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __WPAN_DUMMY_NCP_H__
+#define __WPAN_DUMMY_NCP_H__ 1
+
+#include "NCPInstance.h"
+#include "NCPControlInterface.h"
+#include "nlpt.h"
+#include "Callbacks.h"
+#include "EventHandler.h"
+
+#include <queue>
+#include <set>
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+namespace nl {
+namespace wpantund {
+
+class DummyNCPInstance;
+
+class DummyNCPControlInterface : public NCPControlInterface {
+public:
+	friend class DummyNCPInstance;
+
+	DummyNCPControlInterface(DummyNCPInstance* instance_pointer);
+	virtual ~DummyNCPControlInterface() { }
+
+	virtual const WPAN::NetworkInstance& get_current_network_instance(void)const;
+
+	virtual void join(
+		const ValueMap& options,
+	    CallbackWithStatus cb = NilReturn()
+	);
+
+	virtual void form(
+		const ValueMap& options,
+	    CallbackWithStatus cb = NilReturn()
+	);
+
+	virtual void leave(CallbackWithStatus cb = NilReturn());
+	virtual void attach(CallbackWithStatus cb = NilReturn());
+	virtual void begin_low_power(CallbackWithStatus cb = NilReturn());
+
+	virtual void netscan_start(const ValueMap& options, CallbackWithStatus cb = NilReturn());
+	virtual void netscan_stop(CallbackWithStatus cb = NilReturn());
+
+	virtual void begin_net_wake(uint8_t data, uint32_t flags, CallbackWithStatus cb = NilReturn());
+	virtual void reset(CallbackWithStatus cb = NilReturn());
+	virtual void permit_join(
+	    int seconds = 15 * 60,
+	    uint8_t commissioning_traffic_type = 0xFF,
+	    in_port_t commissioning_traffic_port = 0,
+	    bool network_wide = false,
+	    CallbackWithStatus      cb = NilReturn());
+
+	virtual void refresh_state(CallbackWithStatus cb = NilReturn());
+
+	virtual void get_property(
+	    const std::string& key, CallbackWithStatusArg1 cb);
+	virtual void set_property(
+	    const std::string&                      key,
+	    const boost::any&                       value,
+	    CallbackWithStatus      cb);
+	virtual void config_gateway(bool defaultRoute, const uint8_t prefix[8], uint32_t preferredLifetime, uint32_t validLifetime, CallbackWithStatus cb = NilReturn());
+	virtual void add_external_route(const uint8_t route[], int route_prefix_len, int domain_id, ExternalRoutePriority priority, CallbackWithStatus);
+	virtual void remove_external_route(const uint8_t route[], int route_prefix_len, int domain_id, CallbackWithStatus cb);
+
+	virtual void data_poll(CallbackWithStatus cb = NilReturn());
+	virtual void host_did_wake(CallbackWithStatus cb = NilReturn());
+
+	virtual std::string get_name();
+
+	virtual NCPInstance& get_ncp_instance(void);
+
+private:
+
+	DummyNCPInstance* mNCPInstance;
+
+};
+
+}; // namespace wpantund
+}; // namespace nl
+
+
+#endif
diff --git a/src/ncp-dummy/DummyNCPInstance.cpp b/src/ncp-dummy/DummyNCPInstance.cpp
new file mode 100644
index 0000000..659057d
--- /dev/null
+++ b/src/ncp-dummy/DummyNCPInstance.cpp
@@ -0,0 +1,92 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "DummyNCPInstance.h"
+#include "time-utils.h"
+#include "assert-macros.h"
+#include <syslog.h>
+#include <errno.h>
+#include "socket-utils.h"
+#include <stdexcept>
+#include <sys/file.h>
+#include "SuperSocket.h"
+
+using namespace nl;
+using namespace wpantund;
+
+WPANTUND_DEFINE_NCPINSTANCE_PLUGIN(dummy, DummyNCPInstance);
+
+DummyNCPInstance::DummyNCPInstance(const Settings& settings) :
+	NCPInstanceBase(settings), mControlInterface(this)
+{
+}
+
+DummyNCPInstance::~DummyNCPInstance()
+{
+}
+
+int
+DummyNCPInstance::vprocess_event(int event, va_list args)
+{
+	EH_BEGIN();
+
+	EH_SLEEP_FOR(1);
+
+	change_ncp_state(OFFLINE);
+	signal_property_changed(kWPANTUNDProperty_NCPState, ncp_state_to_string(get_ncp_state()));
+
+	// Wait forever, this is the dummy plugin.
+	EH_WAIT_UNTIL(false);
+
+	EH_END();
+}
+
+char
+DummyNCPInstance::ncp_to_driver_pump()
+{
+	struct nlpt*const pt = &mNCPToDriverPumpPT;
+
+	NLPT_BEGIN(pt);
+	NLPT_END(pt);
+}
+
+char
+DummyNCPInstance::driver_to_ncp_pump()
+{
+	struct nlpt*const pt = &mNCPToDriverPumpPT;
+
+	NLPT_BEGIN(pt);
+	NLPT_END(pt);
+}
+
+bool
+DummyNCPInstance::setup_property_supported_by_class(const std::string& prop_name)
+{
+	return NCPInstanceBase::setup_property_supported_by_class(prop_name);
+}
+
+DummyNCPControlInterface&
+DummyNCPInstance::get_control_interface()
+{
+	return mControlInterface;
+}
diff --git a/src/ncp-dummy/DummyNCPInstance.h b/src/ncp-dummy/DummyNCPInstance.h
new file mode 100644
index 0000000..2d9d8d4
--- /dev/null
+++ b/src/ncp-dummy/DummyNCPInstance.h
@@ -0,0 +1,70 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __wpantund__DummyNCPInstance__
+#define __wpantund__DummyNCPInstance__
+
+#include "NCPInstanceBase.h"
+#include "DummyNCPControlInterface.h"
+#include "nlpt.h"
+#include "SocketWrapper.h"
+#include "SocketAsyncOp.h"
+
+#include <queue>
+#include <set>
+#include <map>
+#include <errno.h>
+
+WPANTUND_DECLARE_NCPINSTANCE_PLUGIN(dummy, DummyNCPInstance);
+
+namespace nl {
+namespace wpantund {
+
+class DummyNCPControlInterface;
+
+class DummyNCPInstance : public NCPInstanceBase {
+	friend class DummyNCPControlInterface;
+
+public:
+	DummyNCPInstance(const Settings& settings = Settings());
+
+	virtual ~DummyNCPInstance();
+
+	virtual DummyNCPControlInterface& get_control_interface();
+
+	virtual int vprocess_event(int event, va_list args);
+
+protected:
+	virtual char ncp_to_driver_pump();
+	virtual char driver_to_ncp_pump();
+
+
+public:
+	static bool setup_property_supported_by_class(const std::string& prop_name);
+
+private:
+	DummyNCPControlInterface mControlInterface;
+}; // class DummyNCPInstance
+
+extern class DummyNCPInstance* gNCPInstance;
+
+}; // namespace wpantund
+}; // namespace nl
+
+#endif /* defined(__wpantund__DummyNCPInstance__) */
diff --git a/src/ncp-dummy/Makefile.am b/src/ncp-dummy/Makefile.am
new file mode 100644
index 0000000..8fbb626
--- /dev/null
+++ b/src/ncp-dummy/Makefile.am
@@ -0,0 +1,59 @@
+#
+# Copyright (c) 2016 Nest Labs, Inc.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src \
+	-I$(top_srcdir)/src/util \
+	-I$(top_srcdir)/src/ncp-dummy \
+	-I$(top_srcdir)/src/wpantund \
+	-I$(top_srcdir)/third_party/pt \
+	-I$(top_srcdir)/third_party/assert-macros \
+	$(NULL)
+
+DISTCLEANFILES = \
+	.deps \
+	Makefile \
+	$(NULL)
+
+if BUILD_PLUGIN_NCP_DUMMY
+
+# Work-around the omnipotent automake nanny-state.
+mypkglibexecdir = $(pkglibexecdir)
+
+NCP_SOURCES = \
+	DummyNCPControlInterface.cpp \
+	DummyNCPControlInterface.h \
+	DummyNCPInstance.cpp \
+	DummyNCPInstance.h \
+	$(NULL)
+
+if STATIC_LINK_NCP_PLUGIN
+noinst_LTLIBRARIES = libncp-dummy.la
+libncp_dummy_la_SOURCES = $(NCP_SOURCES)
+libncp_dummy_la_CXXFLAGS = @BOOST_CXXFLAGS@
+else
+mypkglibexec_LTLIBRARIES = ncp-dummy.la
+ncp_dummy_la_LDFLAGS = \
+	-module \
+	-avoid-version \
+	-shared \
+	$(NULL)
+ncp_dummy_la_SOURCES = $(NCP_SOURCES)
+ncp_dummy_la_CXXFLAGS = @BOOST_CXXFLAGS@
+endif
+
+endif #BUILD_PLUGIN_NCP_dummy
diff --git a/src/ncp-spinel/Makefile.am b/src/ncp-spinel/Makefile.am
new file mode 100644
index 0000000..ce3ba00
--- /dev/null
+++ b/src/ncp-spinel/Makefile.am
@@ -0,0 +1,89 @@
+#
+# Copyright (c) 2016 Nest Labs, Inc.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src \
+	-I$(top_srcdir)/src/util \
+	-I$(top_srcdir)/src/ncp-spinel \
+	-I$(top_srcdir)/src/wpantund \
+	-I$(top_srcdir)/third_party/pt \
+	-I$(top_srcdir)/third_party/assert-macros \
+	-I$(top_srcdir)/third_party/openthread/src/ncp \
+	$(NULL)
+
+DISTCLEANFILES = \
+	.deps \
+	Makefile \
+	$(NULL)
+
+EXTRA_DIST =                                         \
+	../../third_party/openthread/CONTRIBUTING.md     \
+	../../third_party/openthread/LICENSE             \
+	../../third_party/openthread/README              \
+	../../third_party/openthread/README.google       \
+	../../third_party/openthread/README.md           \
+	../../third_party/openthread/src/ncp/PROTOCOL.md \
+	../../third_party/openthread/src/ncp/spinel.c    \
+	../../third_party/openthread/src/ncp/spinel.h    \
+	$(NULL)
+
+if BUILD_PLUGIN_NCP_SPINEL
+
+# Work around the omnipotent automake nanny-state.
+mypkglibexecdir = $(pkglibexecdir)
+
+NCP_SOURCES = \
+	SpinelNCPControlInterface.cpp \
+	SpinelNCPControlInterface.h \
+	SpinelNCPInstance.cpp \
+	SpinelNCPInstance.h \
+	SpinelNCPInstance-DataPump.cpp \
+	SpinelNCPInstance-Protothreads.cpp \
+	SpinelNCPTask.cpp \
+	SpinelNCPTask.h \
+	SpinelNCPTaskDeepSleep.cpp \
+	SpinelNCPTaskDeepSleep.h \
+	SpinelNCPTaskForm.cpp \
+	SpinelNCPTaskForm.h \
+	SpinelNCPTaskJoin.cpp \
+	SpinelNCPTaskJoin.h \
+	SpinelNCPTaskLeave.cpp \
+	SpinelNCPTaskLeave.h \
+	SpinelNCPTaskScan.cpp \
+	SpinelNCPTaskScan.h \
+	SpinelNCPTaskSendCommand.cpp \
+	SpinelNCPTaskSendCommand.h \
+	SpinelNCPTaskWake.cpp \
+	SpinelNCPTaskWake.h \
+	$(top_srcdir)/third_party/openthread/src/ncp/spinel.c \
+	spinel-extra.c \
+	spinel-extra.h \
+	$(NULL)
+
+
+if STATIC_LINK_NCP_PLUGIN
+noinst_LTLIBRARIES = libncp-spinel.la
+libncp_spinel_la_SOURCES = $(NCP_SOURCES)
+libncp_spinel_la_CXXFLAGS = @BOOST_CXXFLAGS@
+else
+mypkglibexec_LTLIBRARIES = ncp-spinel.la
+ncp_spinel_la_LDFLAGS = -module -avoid-version -shared
+ncp_spinel_la_SOURCES = $(NCP_SOURCES)
+ncp_spinel_la_CXXFLAGS = @BOOST_CXXFLAGS@
+endif
+
+endif #BUILD_PLUGIN_NCP_SPINEL
diff --git a/src/ncp-spinel/SpinelNCPControlInterface.cpp b/src/ncp-spinel/SpinelNCPControlInterface.cpp
new file mode 100644
index 0000000..f51bbb9
--- /dev/null
+++ b/src/ncp-spinel/SpinelNCPControlInterface.cpp
@@ -0,0 +1,438 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "assert-macros.h"
+
+#include "SpinelNCPControlInterface.h"
+#include "SpinelNCPInstance.h"
+
+#include "wpantund.h"
+#include "config-file.h"
+#include "nlpt.h"
+#include "string-utils.h"
+#include "any-to.h"
+#include "time-utils.h"
+
+#include <cstring>
+#include <algorithm>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <syslog.h>
+#include <sys/time.h>
+
+#include <boost/bind.hpp>
+
+#include "spinel-extra.h"
+
+#include "SpinelNCPTask.h"
+#include "SpinelNCPTaskWake.h"
+#include "SpinelNCPTaskJoin.h"
+#include "SpinelNCPTaskForm.h"
+#include "SpinelNCPTaskLeave.h"
+#include "SpinelNCPTaskScan.h"
+#include "SpinelNCPTaskSendCommand.h"
+
+using namespace nl;
+using namespace nl::wpantund;
+
+// ----------------------------------------------------------------------------
+// MARK: -
+
+SpinelNCPControlInterface::SpinelNCPControlInterface(SpinelNCPInstance* instance_pointer)
+	:mNCPInstance(instance_pointer)
+{
+}
+
+// ----------------------------------------------------------------------------
+// MARK: -
+
+void
+SpinelNCPControlInterface::join(
+	const ValueMap& options,
+    CallbackWithStatus cb
+) {
+	mNCPInstance->start_new_task(boost::shared_ptr<SpinelNCPTask>(
+		new SpinelNCPTaskJoin(
+			mNCPInstance,
+			boost::bind(cb,_1),
+			options
+		)
+	));
+}
+
+void
+SpinelNCPControlInterface::form(
+	const ValueMap& options,
+    CallbackWithStatus cb
+) {
+	mNCPInstance->start_new_task(boost::shared_ptr<SpinelNCPTask>(
+		new SpinelNCPTaskForm(
+			mNCPInstance,
+			boost::bind(cb,_1),
+			options
+		)
+	));
+}
+
+void
+SpinelNCPControlInterface::leave(CallbackWithStatus cb)
+{
+	mNCPInstance->start_new_task(boost::shared_ptr<SpinelNCPTask>(
+		new SpinelNCPTaskLeave(
+			mNCPInstance,
+			boost::bind(cb,_1)
+		)
+	));
+}
+
+void
+SpinelNCPControlInterface::attach(CallbackWithStatus cb)
+{
+	mNCPInstance->start_new_task(boost::shared_ptr<SpinelNCPTask>(
+		new SpinelNCPTaskSendCommand(
+			mNCPInstance,
+			boost::bind(cb,_1),
+			SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT8_S), SPINEL_PROP_NET_STATE, SPINEL_NET_STATE_ATTACHED)
+		)
+	));
+}
+
+void
+SpinelNCPControlInterface::reset(CallbackWithStatus cb)
+{
+	mNCPInstance->start_new_task(boost::shared_ptr<SpinelNCPTask>(
+		new SpinelNCPTaskSendCommand(
+			mNCPInstance,
+			boost::bind(cb,kWPANTUNDStatus_Ok),
+			SpinelPackData(SPINEL_FRAME_PACK_CMD_RESET)
+		)
+	));
+}
+
+void
+SpinelNCPControlInterface::begin_net_wake(uint8_t data, uint32_t flags, CallbackWithStatus cb)
+{
+	// TODO: Writeme!
+	cb(kWPANTUNDStatus_FeatureNotImplemented);
+}
+
+void
+SpinelNCPControlInterface::host_did_wake(CallbackWithStatus cb)
+{
+	// TODO: Writeme!
+	cb(kWPANTUNDStatus_FeatureNotImplemented);
+}
+
+void
+SpinelNCPControlInterface::begin_low_power(CallbackWithStatus cb)
+{
+	// TODO: Writeme!
+	cb(kWPANTUNDStatus_FeatureNotImplemented);
+}
+
+void
+SpinelNCPControlInterface::refresh_state(CallbackWithStatus cb)
+{
+	mNCPInstance->start_new_task(boost::shared_ptr<SpinelNCPTask>(
+		new SpinelNCPTaskSendCommand(
+			mNCPInstance,
+			boost::bind(cb,_1),
+			SpinelPackData(SPINEL_FRAME_PACK_CMD_NOOP)
+		)
+	));
+}
+
+void
+SpinelNCPControlInterface::data_poll(CallbackWithStatus cb)
+{
+	mNCPInstance->start_new_task(boost::shared_ptr<SpinelNCPTask>(
+		new SpinelNCPTaskSendCommand(
+			mNCPInstance,
+			boost::bind(cb,_1),
+			SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_STREAM_NET)
+		)
+	));
+}
+
+void
+SpinelNCPControlInterface::config_gateway(bool defaultRoute, const uint8_t prefix[8], uint32_t preferredLifetime, uint32_t validLifetime, CallbackWithStatus cb)
+{
+    const static int kPreferredFlag = 1 << 5;
+    const static int kValidFlag = 1 << 4;
+    const static int kConfigureFlag = 1 << 2; (void)kConfigureFlag;
+    const static int kDefaultRouteFlag = 1 << 1;
+    const static int kDhcpFlag = 1 << 3; (void)kDhcpFlag;
+
+	spinel_ipv6addr_t addr = {};
+	uint8_t flags = 0;
+
+	if (!prefix) {
+		cb(kWPANTUNDStatus_InvalidArgument);
+		return;
+	}
+
+	if (!mNCPInstance->mEnabled) {
+		cb(kWPANTUNDStatus_InvalidWhenDisabled);
+		return;
+	}
+
+	if (defaultRoute) {
+		flags |= kDefaultRouteFlag;
+	}
+
+	flags |= kPreferredFlag | kValidFlag;
+
+	memcpy(addr.s6_addr, prefix, 8);
+
+	memcpy(addr.s6_addr + 8, mNCPInstance->mNCPHardwareAddress, 8);
+	addr.s6_addr[8] ^= 0x02; // flip the private-use bit on the hardware address.
+
+	if (validLifetime == 0) {
+		mNCPInstance->start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				mNCPInstance,
+				boost::bind(cb,_1),
+				SpinelPackData(
+					SPINEL_FRAME_PACK_CMD_PROP_VALUE_REMOVE(
+						SPINEL_DATATYPE_IPv6ADDR_S
+						SPINEL_DATATYPE_UINT8_S
+						SPINEL_DATATYPE_BOOL_S
+						SPINEL_DATATYPE_UINT8_S
+					),
+					SPINEL_PROP_THREAD_ON_MESH_NETS,
+					&addr,
+					64,
+					false,
+					flags
+				)
+			)
+		));
+	} else {
+		mNCPInstance->start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				mNCPInstance,
+				boost::bind(cb,_1),
+				SpinelPackData(
+					SPINEL_FRAME_PACK_CMD_PROP_VALUE_INSERT(
+						SPINEL_DATATYPE_IPv6ADDR_S
+						SPINEL_DATATYPE_UINT8_S
+						SPINEL_DATATYPE_BOOL_S
+						SPINEL_DATATYPE_UINT8_S
+					),
+					SPINEL_PROP_THREAD_ON_MESH_NETS,
+					&addr,
+					64,
+					false,
+					flags
+				)
+			)
+		));
+	}
+}
+
+void
+SpinelNCPControlInterface::add_external_route(const uint8_t *route, int route_prefix_len, int domain_id,
+	ExternalRoutePriority priority, CallbackWithStatus cb)
+{
+    const static int kPreferenceOffset = 6;
+	spinel_ipv6addr_t addr = {};
+	uint8_t flags = 0;
+
+	switch (priority) {
+	case ROUTE_HIGH_PREFERENCE:
+		flags = (1 << kPreferenceOffset);
+		break;
+
+	case ROUTE_MEDIUM_PREFERENCE:
+		flags = 0;
+		break;
+
+	case ROUTE_LOW_PREFRENCE:
+		flags = (3 << kPreferenceOffset);
+		break;
+	}
+
+	memcpy(addr.s6_addr, route, route_prefix_len);
+
+	mNCPInstance->start_new_task(boost::shared_ptr<SpinelNCPTask>(
+		new SpinelNCPTaskSendCommand(
+			mNCPInstance,
+			boost::bind(cb,_1),
+			SpinelPackData(
+				SPINEL_FRAME_PACK_CMD_PROP_VALUE_INSERT(
+					SPINEL_DATATYPE_IPv6ADDR_S
+					SPINEL_DATATYPE_UINT8_S
+					SPINEL_DATATYPE_BOOL_S
+					SPINEL_DATATYPE_UINT8_S
+				),
+				SPINEL_PROP_THREAD_LOCAL_ROUTES,
+				&addr,
+				route_prefix_len*8, // because route_prefix_len is in bytes
+				false,
+				flags
+			)
+		)
+	));
+}
+
+void
+SpinelNCPControlInterface::remove_external_route(const uint8_t *route, int route_prefix_len, int domain_id, CallbackWithStatus cb)
+{
+	spinel_ipv6addr_t addr = {};
+
+	mNCPInstance->start_new_task(boost::shared_ptr<SpinelNCPTask>(
+		new SpinelNCPTaskSendCommand(
+			mNCPInstance,
+			boost::bind(cb,_1),
+			SpinelPackData(
+				SPINEL_FRAME_PACK_CMD_PROP_VALUE_REMOVE(
+					SPINEL_DATATYPE_IPv6ADDR_S
+					SPINEL_DATATYPE_UINT8_S
+					SPINEL_DATATYPE_BOOL_S
+					SPINEL_DATATYPE_UINT8_S
+				),
+				SPINEL_PROP_THREAD_LOCAL_ROUTES,
+				&addr,
+				route_prefix_len*8, // because route_prefix_len is in bytes
+				false,
+				0
+			)
+		)
+	));
+}
+
+void
+SpinelNCPControlInterface::permit_join(
+    int seconds,
+    uint8_t traffic_type,
+    in_port_t traffic_port,
+    bool network_wide,
+    CallbackWithStatus cb
+    )
+{
+	int ret = kWPANTUNDStatus_Ok;
+
+	if (!mNCPInstance->mEnabled) {
+		ret = kWPANTUNDStatus_InvalidWhenDisabled;
+		goto bail;
+	}
+
+	ret = mNCPInstance->set_commissioniner(seconds, traffic_type, traffic_port);
+
+	require_noerr(ret, bail);
+
+	if (seconds > 0) {
+		mNCPInstance->start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				mNCPInstance,
+				boost::bind(cb,_1),
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT16_S), SPINEL_PROP_THREAD_ASSISTING_PORTS, ntohs(traffic_port))
+			)
+		));
+	} else {
+		mNCPInstance->start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				mNCPInstance,
+				boost::bind(cb,_1),
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_VOID_S), SPINEL_PROP_THREAD_ASSISTING_PORTS)
+			)
+		));
+	}
+
+bail:
+	if (ret) {
+		cb(ret);
+	} else {
+		syslog(LOG_NOTICE, "PermitJoin: seconds=%d type=%d port=%d", seconds, traffic_type, ntohs(traffic_port));
+	}
+}
+
+void
+SpinelNCPControlInterface::netscan_start(
+    const ValueMap& options,
+    CallbackWithStatus cb
+) {
+	ChannelMask channel_mask(mNCPInstance->mDefaultChannelMask);
+
+	if (options.count(kWPANTUNDProperty_NCPChannelMask)) {
+		channel_mask = any_to_int(options.at(kWPANTUNDProperty_NCPChannelMask));
+	}
+
+	if (-1 == mNCPInstance->start_new_task(boost::shared_ptr<SpinelNCPTask>(
+		new SpinelNCPTaskScan(
+			mNCPInstance,
+			boost::bind(cb,_1),
+			channel_mask
+		)
+	))) {
+		cb(kWPANTUNDStatus_InvalidForCurrentState);
+	}
+}
+
+void
+SpinelNCPControlInterface::netscan_stop(CallbackWithStatus cb)
+{
+	cb(kWPANTUNDStatus_FeatureNotImplemented); // TODO: Start network scan
+}
+
+std::string
+SpinelNCPControlInterface::get_name() {
+	return mNCPInstance->get_name();
+}
+
+const WPAN::NetworkInstance&
+SpinelNCPControlInterface::get_current_network_instance()const
+{
+	return mNCPInstance->get_current_network_instance();
+}
+
+
+NCPInstance&
+SpinelNCPControlInterface::get_ncp_instance()
+{
+	return (*mNCPInstance);
+}
+
+
+// ----------------------------------------------------------------------------
+// MARK: -
+
+void
+SpinelNCPControlInterface::get_property(
+    const std::string& in_key, CallbackWithStatusArg1 cb
+    )
+{
+	syslog(LOG_INFO, "get_property: key: \"%s\"", in_key.c_str());
+	mNCPInstance->get_property(in_key, cb);
+}
+
+void
+SpinelNCPControlInterface::set_property(
+    const std::string&                      key,
+    const boost::any&                       value,
+    CallbackWithStatus      cb
+    )
+{
+	syslog(LOG_INFO, "set_property: key: \"%s\"", key.c_str());
+	mNCPInstance->set_property(key, value, cb);
+}
diff --git a/src/ncp-spinel/SpinelNCPControlInterface.h b/src/ncp-spinel/SpinelNCPControlInterface.h
new file mode 100644
index 0000000..dbdf6fd
--- /dev/null
+++ b/src/ncp-spinel/SpinelNCPControlInterface.h
@@ -0,0 +1,103 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __WPAN_DUMMY_NCP_H__
+#define __WPAN_DUMMY_NCP_H__ 1
+
+#include "NCPInstance.h"
+#include "NCPControlInterface.h"
+#include "nlpt.h"
+#include "Callbacks.h"
+#include "EventHandler.h"
+
+#include <queue>
+#include <set>
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+namespace nl {
+namespace wpantund {
+
+class SpinelNCPInstance;
+
+class SpinelNCPControlInterface : public NCPControlInterface {
+public:
+	friend class SpinelNCPInstance;
+
+	SpinelNCPControlInterface(SpinelNCPInstance* instance_pointer);
+	virtual ~SpinelNCPControlInterface() { }
+
+	virtual const WPAN::NetworkInstance& get_current_network_instance(void)const;
+
+	virtual void join(
+		const ValueMap& options,
+	    CallbackWithStatus cb = NilReturn()
+	);
+
+	virtual void form(
+		const ValueMap& options,
+	    CallbackWithStatus cb = NilReturn()
+	);
+
+	virtual void leave(CallbackWithStatus cb = NilReturn());
+	virtual void attach(CallbackWithStatus cb = NilReturn());
+	virtual void begin_low_power(CallbackWithStatus cb = NilReturn());
+
+	virtual void netscan_start(const ValueMap& options, CallbackWithStatus cb = NilReturn());
+	virtual void netscan_stop(CallbackWithStatus cb = NilReturn());
+
+	virtual void begin_net_wake(uint8_t data, uint32_t flags, CallbackWithStatus cb = NilReturn());
+	virtual void reset(CallbackWithStatus cb = NilReturn());
+	virtual void permit_join(
+	    int seconds = 15 * 60,
+	    uint8_t commissioning_traffic_type = 0xFF,
+	    in_port_t commissioning_traffic_port = 0,
+	    bool network_wide = false,
+	    CallbackWithStatus      cb = NilReturn());
+
+	virtual void refresh_state(CallbackWithStatus cb = NilReturn());
+
+	virtual void get_property(
+	    const std::string& key, CallbackWithStatusArg1 cb);
+	virtual void set_property(
+	    const std::string&                      key,
+	    const boost::any&                       value,
+	    CallbackWithStatus      cb);
+	virtual void config_gateway(bool defaultRoute, const uint8_t prefix[8], uint32_t preferredLifetime, uint32_t validLifetime, CallbackWithStatus cb = NilReturn());
+	virtual void add_external_route(const uint8_t route[], int route_prefix_len, int domain_id, ExternalRoutePriority priority, CallbackWithStatus);
+	virtual void remove_external_route(const uint8_t route[], int route_prefix_len, int domain_id, CallbackWithStatus cb);
+
+	virtual void data_poll(CallbackWithStatus cb = NilReturn());
+	virtual void host_did_wake(CallbackWithStatus cb = NilReturn());
+
+	virtual std::string get_name();
+
+	virtual NCPInstance& get_ncp_instance(void);
+
+private:
+
+	SpinelNCPInstance* mNCPInstance;
+
+};
+
+}; // namespace wpantund
+}; // namespace nl
+
+
+#endif
diff --git a/src/ncp-spinel/SpinelNCPInstance-DataPump.cpp b/src/ncp-spinel/SpinelNCPInstance-DataPump.cpp
new file mode 100644
index 0000000..81c22a0
--- /dev/null
+++ b/src/ncp-spinel/SpinelNCPInstance-DataPump.cpp
@@ -0,0 +1,499 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "SpinelNCPInstance.h"
+#include "time-utils.h"
+#include "assert-macros.h"
+#include <syslog.h>
+#include <errno.h>
+#include "socket-utils.h"
+#include <stdexcept>
+#include <sys/file.h>
+#include "SuperSocket.h"
+
+using namespace nl;
+using namespace wpantund;
+
+#define HDLC_BYTE_FLAG             0x7E
+#define HDLC_BYTE_ESC              0x7D
+#define HDLC_BYTE_XON              0x11
+#define HDLC_BYTE_XOFF             0x13
+#define HDLC_BYTE_SPECIAL          0xF8
+#define HDLC_ESCAPE_XFORM          0x20
+
+static bool
+hdlc_byte_needs_escape(uint8_t byte)
+{
+	switch(byte) {
+	case HDLC_BYTE_SPECIAL:
+	case HDLC_BYTE_ESC:
+	case HDLC_BYTE_FLAG:
+	case HDLC_BYTE_XOFF:
+	case HDLC_BYTE_XON:
+		return true;
+
+	default:
+		return false;
+	}
+}
+
+static uint16_t
+hdlc_crc16(uint16_t aFcs, uint8_t aByte)
+{
+#if 1
+	// CRC-16/CCITT, CRC-16/CCITT-TRUE, CRC-CCITT
+	// width=16 poly=0x1021 init=0x0000 refin=true refout=true xorout=0x0000 check=0x2189 name="KERMIT"
+	// http://reveng.sourceforge.net/crc-catalogue/16.htm#crc.cat.kermit
+    static const uint16_t sFcsTable[256] =
+    {
+        0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+        0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+        0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+        0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+        0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+        0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+        0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+        0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+        0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+        0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+        0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+        0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+        0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+        0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+        0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+        0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+        0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+        0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+        0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+        0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+        0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+        0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+        0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+        0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+        0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+        0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+        0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+        0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+        0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+        0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+        0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+        0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+    };
+    return (aFcs >> 8) ^ sFcsTable[(aFcs ^ aByte) & 0xff];
+#else
+	// CRC-16/CCITT-FALSE, same CRC as 802.15.4
+	// width=16 poly=0x1021 init=0xffff refin=false refout=false xorout=0x0000 check=0x29b1 name="CRC-16/CCITT-FALSE"
+	// http://reveng.sourceforge.net/crc-catalogue/16.htm#crc.cat.crc-16-ccitt-false
+	aFcs = (uint16_t)((aFcs >> 8) | (aFcs << 8));
+	aFcs ^= aByte;
+	aFcs ^= ((aFcs & 0xff) >> 4);
+	aFcs ^= (aFcs << 12);
+	aFcs ^= ((aFcs & 0xff) << 5);
+	return aFcs;
+#endif
+}
+
+char
+SpinelNCPInstance::ncp_to_driver_pump()
+{
+	struct nlpt*const pt = &mNCPToDriverPumpPT;
+//	unsigned int prop_key = 0;
+	unsigned int command_value = 0;
+	uint8_t byte;
+
+	// Automatically detect socket resets and behave accordingly.
+	if (mSerialAdapter->did_reset()) {
+		syslog(LOG_NOTICE, "[-NCP-]: Socket Reset");
+		NLPT_INIT(&mNCPToDriverPumpPT);
+		NLPT_INIT(&mDriverToNCPPumpPT);
+
+		process_event(EVENT_NCP_CONN_RESET);
+	}
+
+	NLPT_BEGIN(pt);
+
+	// This macro abstracts the logic to read a single character into
+	// `data`, in a protothreads-friendly way.
+#define READ_CHARACTER(pt, data, on_fail) \
+	while(1) {                                  \
+		NLPT_WAIT_UNTIL_READABLE_OR_COND(pt, mSerialAdapter->get_read_fd(), mSerialAdapter->can_read()); \
+		ssize_t retlen = mSerialAdapter->read(data, 1); \
+		if (retlen < 0) { \
+			syslog(LOG_ERR, "[-NCP-]: Socket error on read: %s %d", \
+			       strerror((int)-retlen), (int)(-retlen)); \
+			signal_fatal_error(ERRORCODE_ERRNO); \
+			goto on_fail; \
+		} else if (retlen == 0) { \
+			continue; \
+		} \
+		break ; \
+	};
+
+	while (!ncp_state_is_detached_from_ncp(get_ncp_state())) {
+		mInboundHeader = 0;
+		mInboundFrameSize = 0;
+
+		// Yield until the socket is readable. We do a
+		// yield here instead of a wait because we want to
+		// only handle one packet per run through the main loop.
+		// Using `YIELD` instead of `WAIT` guarantees that we
+		// will yield control of the protothread at least once,
+		// even if the socket is already readable.
+		NLPT_YIELD_UNTIL_READABLE_OR_COND(pt, mSerialAdapter->get_read_fd(), mSerialAdapter->can_read());
+
+#if WPANTUND_SPINEL_USE_FLEN
+		do {
+			READ_CHARACTER(pt, (void*)&mInboundFrame[0], on_error);
+
+			if (HDLC_BYTE_FLAG != mInboundFrame[0]) {
+				// The dreaded extraneous character error.
+
+				// Log the error.
+				{
+					char printable = mInboundFrame[0];
+					if(iscntrl(printable) || printable<0)
+						printable = '.';
+
+					syslog(LOG_WARNING,
+						   "[NCP->] Extraneous Character: 0x%02X [%c] (%d)\n",
+						   (uint8_t)mInboundFrame[0],
+						   printable,
+						   (uint8_t)mInboundFrame[0]);
+				}
+
+				// Flush out all remaining data since this is a strong
+				// indication that something has gone horribly wrong.
+				while (mSerialAdapter->can_read()) {
+					READ_CHARACTER(pt, (void*)&mInboundFrame[0], on_error);
+				}
+
+				ncp_is_misbehaving();
+				goto on_error;
+			}
+		} while (UART_STREAM_FLAG != mInboundFrame[0]);
+
+		// Read the frame length
+		READ_CHARACTER(pt, (void*)((char*)&mInboundFrame+0), on_error);
+		READ_CHARACTER(pt, (void*)((char*)&mInboundFrame+1), on_error);
+
+		mInboundFrameSize = (mInboundFrame[0] << 8) + mInboundFrame[1];
+
+		require(mInboundFrameSize > 1, on_error);
+		require(mInboundFrameSize <= SPINEL_FRAME_MAX_SIZE, on_error);
+
+		// Read the rest of the packet.
+		NLPT_ASYNC_READ_STREAM(
+			pt,
+			mSerialAdapter.get(),
+			mInboundFrame,
+			mInboundFrameSize
+		);
+#else
+
+		mInboundFrameSize = 0;
+		mInboundFrameHDLCCRC = 0xffff;
+
+		do {
+			READ_CHARACTER(pt, &byte, on_error);
+
+			if (byte == HDLC_BYTE_FLAG) {
+				break;
+			}
+			if (byte == HDLC_BYTE_ESC) {
+				READ_CHARACTER(pt, &byte, on_error);
+				if (byte == HDLC_BYTE_FLAG) {
+					break;
+				} else {
+					byte ^= HDLC_ESCAPE_XFORM;
+				}
+			}
+
+			if (mInboundFrameSize >= 2) {
+				mInboundFrameHDLCCRC = hdlc_crc16(mInboundFrameHDLCCRC, mInboundFrame[mInboundFrameSize-2]);
+			}
+
+			mInboundFrame[mInboundFrameSize++] = byte;
+
+		} while(true);
+
+		if (mInboundFrameSize <= 2) {
+			continue;
+		}
+
+		mInboundFrameSize -= 2;
+		mInboundFrameHDLCCRC ^= 0xFFFF;
+		{
+			uint16_t frame_crc = (mInboundFrame[mInboundFrameSize]|(mInboundFrame[mInboundFrameSize+1]<<8));
+			if (mInboundFrameHDLCCRC != frame_crc) {
+				syslog(LOG_ERR, "[NCP->]: Frame CRC Mismatch: Calc:0x%04X != Frame:0x%04X", mInboundFrameHDLCCRC, frame_crc);
+			}
+			require(mInboundFrameHDLCCRC == frame_crc, on_error);
+		}
+
+#endif
+
+		if (pt->last_errno) {
+			syslog(LOG_ERR, "[-NCP-]: Socket error on read: %s", strerror(pt->last_errno));
+			errno = pt->last_errno;
+			signal_fatal_error(ERRORCODE_ERRNO);
+			goto on_error;
+		}
+
+		if (spinel_datatype_unpack(mInboundFrame, mInboundFrameSize, "Ci", &mInboundHeader, &command_value) > 0) {
+			if ((mInboundHeader&SPINEL_HEADER_FLAG) != SPINEL_HEADER_FLAG) {
+				// Unrecognized frame.
+				break;
+			}
+
+			if (SPINEL_HEADER_GET_IID(mInboundHeader) != 0) {
+				// We only support IID zero for now.
+				break;
+			}
+
+			handle_ncp_spinel_callback(command_value, mInboundFrame, mInboundFrameSize);
+		}
+	} // while (!ncp_state_is_detached_from_ncp(get_ncp_state()))
+
+on_error:;
+	// If we get here, we will restart the protothread at the next iteration.
+
+	NLPT_END(pt);
+}
+
+char
+SpinelNCPInstance::driver_to_ncp_pump()
+{
+	struct nlpt*const pt = &mDriverToNCPPumpPT;
+
+	NLPT_BEGIN(pt);
+
+	while (!ncp_state_is_detached_from_ncp(get_ncp_state())) {
+		// If there is an outbound callback at this
+		// point, then we assume it is stale and
+		// immediately clear it out.
+		if (!mOutboundCallback.empty()) {
+			mOutboundCallback(kWPANTUNDStatus_Canceled);
+			mOutboundCallback.clear();
+		}
+
+		// Wait for a packet to be available from interface OR management queue.
+		if (mOutboundBufferLen > 0) {
+			// If there is something in the outbound queue,
+			// we shouldn't try any of the checks below, since it
+			// will delay processing.
+
+		} else if (static_cast<bool>(mLegacyInterface) && is_legacy_interface_enabled()) {
+			NLPT_YIELD_UNTIL_READABLE2_OR_COND(
+				pt,
+				mPrimaryInterface->get_read_fd(),
+				mLegacyInterface->get_read_fd(),
+				(mOutboundBufferLen > 0)
+				|| mLegacyInterface->can_read()
+				|| mPrimaryInterface->can_read()
+			);
+
+		} else {
+			NLPT_YIELD_UNTIL_READABLE_OR_COND(
+				pt,
+				mPrimaryInterface->get_read_fd(),
+				mPrimaryInterface->can_read() || (mOutboundBufferLen > 0)
+			);
+		}
+
+		// Get packet or management command, and also
+		// perform any necessary filtering.
+		if (mOutboundBufferLen > 0) {
+			if (mOutboundBuffer[1] == SPINEL_CMD_PROP_VALUE_GET) {
+				spinel_prop_key_t key;
+				spinel_datatype_unpack(mOutboundBuffer, mOutboundBufferLen, "Cii", NULL, NULL, &key);
+				syslog(LOG_INFO, "[->NCP] CMD_PROP_VALUE_GET(%s) len=%d", spinel_prop_key_to_cstr(key), mOutboundBufferLen);
+			} else if (mOutboundBuffer[1] == SPINEL_CMD_PROP_VALUE_SET) {
+				spinel_prop_key_t key;
+				spinel_datatype_unpack(mOutboundBuffer, mOutboundBufferLen, "Cii", NULL, NULL, &key);
+				syslog(LOG_INFO, "[->NCP] CMD_PROP_VALUE_SET(%s)", spinel_prop_key_to_cstr(key));
+			} else if (mOutboundBuffer[1] == SPINEL_CMD_PROP_VALUE_INSERT) {
+				spinel_prop_key_t key;
+				spinel_datatype_unpack(mOutboundBuffer, mOutboundBufferLen, "Cii", NULL, NULL, &key);
+				syslog(LOG_INFO, "[->NCP] CMD_PROP_VALUE_INSERT(%s)", spinel_prop_key_to_cstr(key));
+			} else if (mOutboundBuffer[1] == SPINEL_CMD_PROP_VALUE_REMOVE) {
+				spinel_prop_key_t key;
+				spinel_datatype_unpack(mOutboundBuffer, mOutboundBufferLen, "Cii", NULL, NULL, &key);
+				syslog(LOG_INFO, "[->NCP] CMD_PROP_VALUE_SET(%s)", spinel_prop_key_to_cstr(key));
+			} else if (mOutboundBuffer[1] == SPINEL_CMD_NOOP) {
+				syslog(LOG_INFO, "[->NCP] CMD_NOOP");
+			} else if (mOutboundBuffer[1] == SPINEL_CMD_RESET) {
+				syslog(LOG_INFO, "[->NCP] CMD_RESET");
+			}
+		} else {
+			// There is an IPv6 packet waiting on one of the tunnel interfaces.
+
+			if (mPrimaryInterface->can_read()) {
+				mOutboundBufferLen = (spinel_ssize_t)mPrimaryInterface->read(
+					&mOutboundBuffer[5],
+					sizeof(mOutboundBuffer)-5
+				);
+				mOutboundBufferType = FRAME_TYPE_DATA;
+			} else if (static_cast<bool>(mLegacyInterface)) {
+				mOutboundBufferLen = (spinel_ssize_t)mLegacyInterface->read(
+					&mOutboundBuffer[5],
+					sizeof(mOutboundBuffer)-5
+				);
+				mOutboundBufferType = FRAME_TYPE_LEGACY_DATA;
+			}
+
+			if (0 > mOutboundBufferLen) {
+				syslog(LOG_ERR,
+				       "driver_to_ncp_pump: Socket error on read: %s",
+				       strerror(errno));
+				signal_fatal_error(ERRORCODE_ERRNO);
+				break;
+			}
+
+			if (mOutboundBufferLen <= 0) {
+				// No packet...?
+				mOutboundBufferLen = 0;
+				continue;
+			}
+
+			if (!should_forward_ncpbound_frame(&mOutboundBufferType, &mOutboundBuffer[5], mOutboundBufferLen)) {
+				mOutboundBufferLen = 0;
+				continue;
+			}
+
+			mOutboundBuffer[3] = (mOutboundBufferLen & 0xFF);
+			mOutboundBuffer[4] = ((mOutboundBufferLen >> 8) & 0xFF);
+
+			mOutboundBufferLen += 5;
+
+			mOutboundBuffer[0] = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0;
+			mOutboundBuffer[1] = SPINEL_CMD_PROP_VALUE_SET;
+
+			if (mOutboundBufferType == FRAME_TYPE_DATA) {
+				mOutboundBuffer[2] = SPINEL_PROP_STREAM_NET;
+			} else if (mOutboundBufferType == FRAME_TYPE_INSECURE_DATA) {
+				mOutboundBuffer[2] = SPINEL_PROP_STREAM_NET_INSECURE;
+			} else {
+				mOutboundBuffer[0] = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_1;
+				mOutboundBuffer[2] = SPINEL_PROP_STREAM_NET;
+			}
+		}
+
+#if VERBOSE_DEBUG || 1
+		// Very verbose debugging. Dumps out all outbound packets.
+		{
+			char readable_buffer[300];
+			encode_data_into_string(mOutboundBuffer,
+			                        mOutboundBufferLen,
+			                        readable_buffer,
+			                        sizeof(readable_buffer),
+			                        0);
+			syslog(LOG_INFO, "\t↳ %s", (const char*)readable_buffer);
+		}
+#endif // VERBOSE_DEBUG
+
+
+#if WPANTUND_SPINEL_USE_FLEN
+		mOutboundBufferHeader[0] = HDLC_BYTE_FLAG;
+		mOutboundBufferHeader[1] = (mOutboundBufferLen >> 8);
+		mOutboundBufferHeader[2] = (mOutboundBufferLen & 0xFF);
+
+		mOutboundBufferSent = 0;
+
+		// Go ahead send
+		NLPT_ASYNC_WRITE_STREAM(
+			pt,
+			mSerialAdapter.get(),
+			mOutboundBufferHeader,
+			mOutboundBufferLen + sizeof(mOutboundBufferHeader)
+		);
+		mOutboundBufferSent += pt->byte_count;
+#else
+
+		mOutboundBufferEscapedLen = 1;
+		mOutboundBufferEscaped[0] = HDLC_BYTE_FLAG;
+		{
+			spinel_ssize_t i;
+			uint8_t byte;
+			uint16_t crc(0xFFFF);
+			for (i = 0; i < mOutboundBufferLen; i++) {
+				byte = mOutboundBuffer[i];
+				crc = hdlc_crc16(crc, byte);
+				if (hdlc_byte_needs_escape(byte)) {
+					mOutboundBufferEscaped[mOutboundBufferEscapedLen++] = HDLC_BYTE_ESC;
+					mOutboundBufferEscaped[mOutboundBufferEscapedLen++] = byte ^ HDLC_ESCAPE_XFORM;
+				} else {
+					mOutboundBufferEscaped[mOutboundBufferEscapedLen++] = byte;
+				}
+			}
+			crc ^= 0xFFFF;
+			byte = (crc & 0xFF);
+			if (hdlc_byte_needs_escape(byte)) {
+				mOutboundBufferEscaped[mOutboundBufferEscapedLen++] = HDLC_BYTE_ESC;
+				mOutboundBufferEscaped[mOutboundBufferEscapedLen++] = byte ^ HDLC_ESCAPE_XFORM;
+			} else {
+				mOutboundBufferEscaped[mOutboundBufferEscapedLen++] = byte;
+			}
+			byte = ((crc>>8) & 0xFF);
+			if (hdlc_byte_needs_escape(byte)) {
+				mOutboundBufferEscaped[mOutboundBufferEscapedLen++] = HDLC_BYTE_ESC;
+				mOutboundBufferEscaped[mOutboundBufferEscapedLen++] = byte ^ HDLC_ESCAPE_XFORM;
+			} else {
+				mOutboundBufferEscaped[mOutboundBufferEscapedLen++] = byte;
+			}
+			mOutboundBufferEscaped[mOutboundBufferEscapedLen++] = HDLC_BYTE_FLAG;
+		}
+
+		mOutboundBufferSent = 0;
+
+		// Go ahead send
+		NLPT_ASYNC_WRITE_STREAM(
+			pt,
+			mSerialAdapter.get(),
+			mOutboundBufferEscaped,
+			mOutboundBufferEscapedLen
+		);
+		mOutboundBufferSent += pt->byte_count;
+#endif
+
+		mOutboundBufferLen = 0;
+
+		require(pt->last_errno == 0, on_error);
+
+		// Go ahead and fire off the "did send" callback.
+		if (!mOutboundCallback.empty()) {
+			mOutboundCallback(kWPANTUNDStatus_Ok);
+			mOutboundCallback.clear();
+		}
+
+	} // while(true)
+
+on_error:
+	// If we get here, we will restart the protothread at the next iteration.
+
+	if (!mOutboundCallback.empty()) {
+		mOutboundCallback(kWPANTUNDStatus_Failure);
+		mOutboundCallback.clear();
+	}
+
+	NLPT_END(pt);
+}
diff --git a/src/ncp-spinel/SpinelNCPInstance-Protothreads.cpp b/src/ncp-spinel/SpinelNCPInstance-Protothreads.cpp
new file mode 100644
index 0000000..49a813a
--- /dev/null
+++ b/src/ncp-spinel/SpinelNCPInstance-Protothreads.cpp
@@ -0,0 +1,512 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "SpinelNCPInstance.h"
+#include "time-utils.h"
+#include "assert-macros.h"
+#include <syslog.h>
+#include <errno.h>
+#include "socket-utils.h"
+#include <stdexcept>
+#include <sys/file.h>
+#include "SuperSocket.h"
+#include "SpinelNCPTask.h"
+#include "spinel-extra.h"
+
+#include "SpinelNCPTaskSendCommand.h"
+#include "SpinelNCPTaskScan.h"
+#include "SpinelNCPTaskForm.h"
+#include "SpinelNCPTaskLeave.h"
+#include "SpinelNCPTaskJoin.h"
+#include "SpinelNCPTaskWake.h"
+#include "SpinelNCPTaskDeepSleep.h"
+
+using namespace nl;
+using namespace wpantund;
+
+int
+SpinelNCPInstance::vprocess_disabled(int event, va_list args)
+{
+	EH_BEGIN_SUB(&mSubPT);
+
+
+	while(!mEnabled) {
+		// If the association state is uninitialized, fail early.
+		if (get_ncp_state() == UNINITIALIZED) {
+			syslog(LOG_NOTICE, "Cannot attempt to sleep until NCP is initialized.");
+			EH_EXIT();
+		}
+
+		// Wait for any tasks or commands to complete.
+		EH_REQUIRE_WITHIN(
+			NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+			mEnabled || is_busy(),
+			timeout_error
+		);
+
+		if (mEnabled) {
+			break;
+		}
+
+		if ((get_ncp_state() != DEEP_SLEEP) && (get_ncp_state() != FAULT)) {
+			start_new_task(boost::shared_ptr<SpinelNCPTask>(new SpinelNCPTaskDeepSleep(this, NilReturn())));
+
+			EH_WAIT_UNTIL_WITH_TIMEOUT(
+				NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+				(get_ncp_state() == DEEP_SLEEP) || mTaskQueue.empty()
+			);
+		}
+
+		// If we didn't enter deep sleep then we need to bail early.
+		if ((get_ncp_state() != DEEP_SLEEP) && (get_ncp_state() != FAULT)) {
+			if (!ncp_state_is_initializing(get_ncp_state())) {
+				get_control_interface().reset();
+			}
+			EH_EXIT();
+		}
+
+		EH_WAIT_UNTIL(!IS_EVENT_FROM_NCP(event));
+
+		EH_REQUIRE_WITHIN(
+			NCP_DEEP_SLEEP_TICKLE_TIMEOUT,
+			(get_ncp_state() != DEEP_SLEEP)
+			|| mEnabled
+			|| IS_EVENT_FROM_NCP(event),
+			do_deep_sleep_tickle
+		);
+
+		continue;
+
+		// Exceptions
+
+do_deep_sleep_tickle:
+		// ...Go ahead and reset the NCP and state machine.
+		// This will make sure the NCP is in a known state
+		// and allow it to recover if the NCP is
+		// unresponsive.
+		syslog(LOG_WARNING, "DEEP-SLEEP-TICKLE: Resetting NCP . . .");
+
+		// Send a RESET.
+		CONTROL_REQUIRE_PREP_TO_SEND_COMMAND_WITHIN(NCP_DEFAULT_COMMAND_SEND_TIMEOUT, timeout_error);
+		mOutboundBufferLen = spinel_datatype_pack(GetInstance(this)->mOutboundBuffer, sizeof(GetInstance(this)->mOutboundBuffer), "Ci", 0, SPINEL_CMD_RESET);
+		CONTROL_REQUIRE_OUTBOUND_BUFFER_FLUSHED_WITHIN(NCP_DEFAULT_COMMAND_SEND_TIMEOUT, timeout_error);
+
+		mResetIsExpected = true;
+
+		// Wait for the response.
+		EH_REQUIRE_WITHIN(
+			NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+			event == EVENT_NCP_RESET,
+			timeout_error
+		);
+
+		mResetIsExpected = false;
+		continue;
+
+timeout_error:
+		EH_EXIT();
+	}
+
+	set_ncp_power(true);
+
+	if (ncp_state_is_sleeping(get_ncp_state())) {
+		start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskWake(
+				this,
+				NilReturn()
+			)
+		));
+	}
+
+	EH_END();
+}
+
+int
+SpinelNCPInstance::vprocess_resume(int event, va_list args)
+{
+	EH_BEGIN_SUB(&mSubPT);
+	EH_END();
+}
+
+int
+SpinelNCPInstance::vprocess_associated(int event, va_list args)
+{
+	// Conditions under which this protothread should be exited gracefully.
+	const bool should_exit = !mEnabled
+		|| !ncp_state_is_joining_or_joined(get_ncp_state());
+
+	EH_BEGIN_SUB(&mSubPT);
+
+	EH_WAIT_UNTIL_WITH_TIMEOUT(
+		NCP_TICKLE_TIMEOUT,
+		should_exit || !(IS_EVENT_FROM_NCP(event))
+	);
+
+	// This is not a typo. Despite the above "WAIT_UNTIL" looking
+	// very similar, it is not the same! DO NOT REMOVE!
+	EH_WAIT_UNTIL_WITH_TIMEOUT(
+		NCP_TICKLE_TIMEOUT,
+		should_exit || (IS_EVENT_FROM_NCP(event))
+	);
+
+	if (eh_did_timeout) {
+		syslog(LOG_INFO, "Tickle...");
+		CONTROL_REQUIRE_PREP_TO_SEND_COMMAND_WITHIN(NCP_DEFAULT_COMMAND_SEND_TIMEOUT, on_error);
+		mOutboundBufferLen = spinel_datatype_pack(GetInstance(this)->mOutboundBuffer, sizeof(GetInstance(this)->mOutboundBuffer), "Ci", 0, SPINEL_CMD_NOOP);
+		CONTROL_REQUIRE_OUTBOUND_BUFFER_FLUSHED_WITHIN(NCP_DEFAULT_COMMAND_SEND_TIMEOUT, on_error);
+
+		CONTROL_REQUIRE_COMMAND_RESPONSE_WITHIN(NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT, on_error);
+		mFailureCount = 0;
+	}
+
+	EH_EXIT();
+
+on_error:
+
+	syslog(LOG_ERR, "NCP is misbehaving or unresponsive");
+	reinitialize_ncp();
+
+	EH_END();
+}
+
+int
+SpinelNCPInstance::vprocess_offline(int event, va_list args)
+{
+	// Conditions under which this protothread should be exited gracefully.
+	const bool should_exit = ncp_state_is_interface_up(get_ncp_state())
+		|| !mEnabled
+		|| (mOutboundBufferLen>0);
+
+	float sleep_timeout = mAutoDeepSleepTimeout;
+
+	if (!mNetworkKey.empty() || (mNetworkKeyIndex != 0)) {
+		sleep_timeout += 60;
+	}
+
+	EH_BEGIN_SUB(&mSubPT);
+
+	// Wait for auto deep sleep to be turned on, or if there is an exit condition.
+	EH_WAIT_UNTIL(should_exit || mAutoDeepSleep);
+
+	// Wait for auto deep sleep to be turned off or for us to be not asleep, or if there is an exit condition
+	EH_WAIT_UNTIL(
+	   should_exit
+	   || !mAutoDeepSleep
+	   || !ncp_state_is_sleeping(get_ncp_state())
+	);
+
+	// Wait within a timeout for us to enter sleep state, or for auto deep sleep to be turned off, or
+	// if we have a command to send to NCP or receive a callback/event from NCP, or if there is an exit condition
+	EH_WAIT_UNTIL_WITH_TIMEOUT(
+		sleep_timeout,
+		should_exit
+		|| !mAutoDeepSleep
+		|| !mTaskQueue.empty()
+		|| (IS_EVENT_FROM_NCP(event))
+		|| ncp_state_is_sleeping(get_ncp_state())
+	);
+
+	if (eh_did_timeout) {
+		start_new_task(boost::shared_ptr<SpinelNCPTask>(new SpinelNCPTaskDeepSleep(this, NilReturn())));
+	}
+
+	EH_END();
+}
+
+int
+SpinelNCPInstance::vprocess_init(int event, va_list args)
+{
+	int status;
+
+	if (event == EVENT_NCP_RESET) {
+		if (mDriverState == INITIALIZING) {
+			syslog(LOG_ERR, "Unexpected reset durring NCP initialization.");
+			mFailureCount++;
+			PT_INIT(&mSubPT);
+		} else if (mDriverState == INITIALIZING_WAITING_FOR_RESET) {
+			mDriverState = INITIALIZING;
+		}
+	}
+
+	EH_BEGIN_SUB(&mSubPT);
+
+	if (get_ncp_state() == UPGRADING) {
+		EH_WAIT_UNTIL(get_upgrade_status() != EINPROGRESS);
+
+		status = get_upgrade_status();
+
+		if (status == 0) {
+			syslog(LOG_INFO, "Firmware Update Complete.");
+		} else {
+			syslog(LOG_ERR, "Firmware Update Failed with Error %d", status);
+			mFailureCount++;
+
+			if (mFailureCount > mFailureThreshold) {
+				change_ncp_state(FAULT);
+			}
+		}
+	}
+
+	if (get_ncp_state() == FAULT) {
+		EH_EXIT();
+	}
+
+	syslog(LOG_INFO, "Initializing NCP");
+
+	change_ncp_state(UNINITIALIZED);
+
+	set_ncp_power(true);
+
+	clear_nonpermanent_global_addresses();
+
+	mNCPVersionString = "";
+
+	mDriverState = INITIALIZING_WAITING_FOR_RESET;
+
+	if (mResetIsExpected) {
+		EH_WAIT_UNTIL_WITH_TIMEOUT(NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT, mResetIsExpected == false);
+
+		if (eh_did_timeout) {
+			// mResetIsExpected has been set for too long and we
+			// haven't gotten a reset yet. This can prevent us from sleeping.
+			// by incrementing the failure count here we will cause
+			// another reset to occur in the code below.
+			mFailureCount++;
+			mResetIsExpected = false;
+			syslog(LOG_ERR, "Was waiting for a reset, but we never got one.");
+		}
+	} else {
+		// Backoff delay. Normaly zero. May increase if we are in a reset loop.
+		EH_SLEEP_FOR(mRunawayResetBackoffManager.delay_for_unexpected_reset());
+	}
+
+	do {
+		EH_SLEEP_FOR(0.1);
+
+		if (mFailureCount > mFailureThreshold) {
+			syslog(LOG_ALERT, "The NCP is misbehaving: Repeatedly unable to initialize NCP. Entering fault state.");
+			change_ncp_state(FAULT);
+			EH_EXIT();
+		}
+
+		if (mAutoUpdateFirmware && (mFailureCount > (mFailureThreshold - 1)) && can_upgrade_firmware()) {
+			syslog(LOG_ALERT, "The NCP is misbehaving: Attempting a firmware update");
+			upgrade_firmware();
+			EH_RESTART();
+		}
+
+		if ((event != EVENT_NCP_RESET) && (mFailureCount > 0)) {
+			syslog(LOG_ERR, "Resetting and trying again... (retry %d)", mFailureCount);
+
+			change_ncp_state(UNINITIALIZED);
+
+			mNetworkKey = Data();
+			mNetworkKeyIndex = 0;
+
+			reset_tasks(kWPANTUNDStatus_Canceled);
+
+			// Do a hard reset only on even attempts.
+			if ((mFailureCount & 1) == 0) {
+				hard_reset_ncp();
+			} else {
+				CONTROL_REQUIRE_PREP_TO_SEND_COMMAND_WITHIN(NCP_DEFAULT_COMMAND_SEND_TIMEOUT, on_error);
+				mOutboundBufferLen = spinel_datatype_pack(GetInstance(this)->mOutboundBuffer, sizeof(GetInstance(this)->mOutboundBuffer), "Ci", 0, SPINEL_CMD_RESET);
+				CONTROL_REQUIRE_OUTBOUND_BUFFER_FLUSHED_WITHIN(NCP_DEFAULT_COMMAND_SEND_TIMEOUT, on_error);
+			}
+
+			mDriverState = INITIALIZING_WAITING_FOR_RESET;
+
+			EH_REQUIRE_WITHIN(
+				NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+				event == EVENT_NCP_RESET,
+				on_error
+			);
+
+			mDriverState = INITIALIZING;
+		}
+
+		// Get the protocol version
+		CONTROL_REQUIRE_PREP_TO_SEND_COMMAND_WITHIN(NCP_DEFAULT_COMMAND_SEND_TIMEOUT, on_error);
+		GetInstance(this)->mOutboundBufferLen = spinel_cmd_prop_value_get(GetInstance(this)->mOutboundBuffer, sizeof(GetInstance(this)->mOutboundBuffer), SPINEL_PROP_PROTOCOL_VERSION);
+		CONTROL_REQUIRE_OUTBOUND_BUFFER_FLUSHED_WITHIN(NCP_DEFAULT_COMMAND_SEND_TIMEOUT, on_error);
+
+		CONTROL_REQUIRE_COMMAND_RESPONSE_WITHIN(NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT, on_error);
+
+		status = peek_ncp_callback_status(event, args);
+		require_noerr(status, on_error);
+
+		if (get_ncp_state() == UNINITIALIZED) {
+			// Get the thread state
+			CONTROL_REQUIRE_PREP_TO_SEND_COMMAND_WITHIN(NCP_DEFAULT_COMMAND_SEND_TIMEOUT, on_error);
+			GetInstance(this)->mOutboundBufferLen = spinel_cmd_prop_value_get(GetInstance(this)->mOutboundBuffer, sizeof(GetInstance(this)->mOutboundBuffer), SPINEL_PROP_NET_STATE);
+			CONTROL_REQUIRE_OUTBOUND_BUFFER_FLUSHED_WITHIN(NCP_DEFAULT_COMMAND_SEND_TIMEOUT, on_error);
+
+			CONTROL_REQUIRE_COMMAND_RESPONSE_WITHIN(NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT, on_error);
+
+			require(get_ncp_state() != UNINITIALIZED, on_error);
+		}
+
+		// If we are "joining" at this point, then we must start over.
+		// This will cause a reset to occur.
+		require(!ncp_state_is_joining(get_ncp_state()), on_error);
+
+		// This next line causes any resets received after this
+		// point to cause the control protothread to be restarted.
+		mDriverState = INITIALIZING;
+
+		if (mEnabled) {
+			// Refresh our internal copies of the following radio parameters:
+			static const spinel_prop_key_t keys_to_fetch[] = {
+				SPINEL_PROP_NCP_VERSION,
+				SPINEL_PROP_INTERFACE_TYPE,
+				SPINEL_PROP_VENDOR_ID,
+				SPINEL_PROP_CAPS,
+				SPINEL_PROP_PHY_CHAN,
+				SPINEL_PROP_PHY_CHAN_SUPPORTED,
+				SPINEL_PROP_MAC_15_4_PANID,
+				SPINEL_PROP_MAC_15_4_LADDR,
+				SPINEL_PROP_HWADDR,
+				SPINEL_PROP_NET_MASTER_KEY,
+				SPINEL_PROP_NET_KEY_SEQUENCE,
+				SPINEL_PROP_NET_NETWORK_NAME,
+				SPINEL_PROP_NET_XPANID,
+				SPINEL_PROP_IPV6_LL_ADDR,
+				SPINEL_PROP_IPV6_ML_PREFIX,
+			};
+
+			for (mSubPTIndex = 0; mSubPTIndex < sizeof(keys_to_fetch)/sizeof(keys_to_fetch[0]); mSubPTIndex++) {
+				CONTROL_REQUIRE_PREP_TO_SEND_COMMAND_WITHIN(NCP_DEFAULT_COMMAND_SEND_TIMEOUT, on_error);
+				GetInstance(this)->mOutboundBufferLen = spinel_cmd_prop_value_get(GetInstance(this)->mOutboundBuffer, sizeof(GetInstance(this)->mOutboundBuffer), keys_to_fetch[mSubPTIndex]);
+				CONTROL_REQUIRE_OUTBOUND_BUFFER_FLUSHED_WITHIN(NCP_DEFAULT_COMMAND_SEND_TIMEOUT, on_error);
+				CONTROL_REQUIRE_COMMAND_RESPONSE_WITHIN(NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT, on_error);
+
+				status = peek_ncp_callback_status(event, args);
+
+				if (status != 0) {
+					syslog(LOG_WARNING, "Error fetching property %d from NCP: %d", keys_to_fetch[mSubPTIndex], status);
+				}
+			}
+
+			// TODO: Perform initialization of other radio paramaters here:
+
+			// TX Power
+
+			// CCA Threshold
+
+			// etc...
+		}
+
+		break;
+
+on_error:
+		if (status) {
+			syslog(LOG_ERR, "Error from NCP: %d", status);
+		}
+		EH_SLEEP_FOR(0.5);
+		mFailureCount++;
+	} while (true);
+
+	mFailureCount = 0;
+	mResetIsExpected = false;
+	mDriverState = NORMAL_OPERATION;
+
+	syslog(LOG_NOTICE, "Finished initializing NCP");
+
+	EH_END();
+}
+
+int
+SpinelNCPInstance::vprocess_event(int event, va_list args)
+{
+	if (get_ncp_state() == FAULT) {
+		// We perform no processing in the fault state.
+		PT_INIT(&mControlPT);
+		return 0;
+	}
+
+	while (!mTaskQueue.empty()) {
+		va_list tmp;
+		boost::shared_ptr<SpinelNCPTask> current_task(mTaskQueue.front());
+		va_copy(tmp, args);
+		char ret = current_task->vprocess_event(event, tmp);
+		va_end(tmp);
+
+		if (ret == PT_ENDED || ret == PT_EXITED) {
+			mTaskQueue.pop_front();
+			continue;
+		}
+
+		break;
+	}
+
+	EH_BEGIN();
+
+	EH_SPAWN(&mSubPT, vprocess_init(event, args));
+
+	if (get_ncp_state() == FAULT) {
+		EH_EXIT();
+	}
+
+	EH_WAIT_UNTIL(mTaskQueue.empty());
+
+	// If we are in the COMMISSIONED NCP state and we
+	if (mAutoResume && mEnabled && (get_ncp_state() == COMMISSIONED)) {
+		EH_SPAWN(&mSubPT, vprocess_resume(event, args));
+	}
+
+	while (1) {
+		// Yield for one loop cycle only. This prevents
+		// us from entering any endless loops.
+		EH_SLEEP_FOR(0);
+
+		if (ncp_state_is_initializing(get_ncp_state())) {
+			EH_RESTART();
+
+		} else if (!mEnabled) {
+			syslog(LOG_NOTICE, "Interface Disabled.");
+			EH_SPAWN(&mSubPT, vprocess_disabled(event, args));
+
+		} else if (ncp_state_is_joining_or_joined(get_ncp_state())) {
+			EH_SPAWN(&mSubPT, vprocess_associated(event, args));
+
+		} else if (!ncp_state_is_interface_up(get_ncp_state())) {
+			EH_SPAWN(&mSubPT, vprocess_offline(event, args));
+
+		} else {
+			syslog(
+				LOG_WARNING,
+				"Unexpected NCP state %d (%s)",
+				get_ncp_state(),
+				ncp_state_to_string(get_ncp_state()).c_str()
+			);
+
+			// Yield for one main loop cycle without
+			// specifying a timeout to avoid high CPU usage
+			// cases like this.
+			EH_YIELD();
+		}
+
+	} // while(1)
+
+	EH_END();
+}
diff --git a/src/ncp-spinel/SpinelNCPInstance.cpp b/src/ncp-spinel/SpinelNCPInstance.cpp
new file mode 100644
index 0000000..9e8fc62
--- /dev/null
+++ b/src/ncp-spinel/SpinelNCPInstance.cpp
@@ -0,0 +1,982 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "SpinelNCPInstance.h"
+#include "time-utils.h"
+#include "assert-macros.h"
+#include <syslog.h>
+#include <errno.h>
+#include "socket-utils.h"
+#include <stdexcept>
+#include <sys/file.h>
+#include "SuperSocket.h"
+#include "SpinelNCPTask.h"
+#include "SpinelNCPTaskWake.h"
+#include "SpinelNCPTaskSendCommand.h"
+#include "any-to.h"
+#include "spinel-extra.h"
+
+using namespace nl;
+using namespace wpantund;
+
+WPANTUND_DEFINE_NCPINSTANCE_PLUGIN(spinel, SpinelNCPInstance);
+
+int
+SpinelNCPInstance::start_new_task(const boost::shared_ptr<SpinelNCPTask> &task)
+{
+	if (ncp_state_is_detached_from_ncp(get_ncp_state())) {
+		task->finish(kWPANTUNDStatus_InvalidWhenDisabled);
+	} else if (PT_SCHEDULE(task->process_event(EVENT_STARTING_TASK))) {
+
+		if (ncp_state_is_sleeping(get_ncp_state())
+			&& (dynamic_cast<const SpinelNCPTaskWake*>(task.get()) == NULL)
+		) {
+			start_new_task(boost::shared_ptr<SpinelNCPTask>(new SpinelNCPTaskWake(this, NilReturn())));
+		}
+		mTaskQueue.push_back(task);
+		return 0;
+	}
+	return -1;
+}
+
+int
+nl::wpantund::peek_ncp_callback_status(int event, va_list args)
+{
+	int ret = 0;
+
+	if (EVENT_NCP_PROP_VALUE_IS == event) {
+		va_list tmp;
+		va_copy(tmp, args);
+		unsigned int key = va_arg(tmp, unsigned int);
+		if (SPINEL_PROP_LAST_STATUS == key) {
+			const uint8_t* spinel_data_ptr = va_arg(tmp, const uint8_t*);
+			spinel_size_t spinel_data_len = va_arg(tmp, spinel_size_t);
+
+			if (spinel_datatype_unpack(spinel_data_ptr, spinel_data_len, "i", &ret) <= 0) {
+				ret = SPINEL_STATUS_PARSE_ERROR;
+			}
+		}
+		va_end(tmp);
+	} else if (EVENT_NCP_RESET == event) {
+		va_list tmp;
+		va_copy(tmp, args);
+		ret = va_arg(tmp, int);
+		va_end(tmp);
+	}
+
+	return ret;
+}
+
+SpinelNCPInstance::SpinelNCPInstance(const Settings& settings) :
+	NCPInstanceBase(settings), mControlInterface(this)
+{
+	mOutboundBufferLen = 0;
+	mInboundHeader = 0;
+	mDefaultChannelMask = 0x07FFF800;
+}
+
+SpinelNCPInstance::~SpinelNCPInstance()
+{
+}
+
+
+bool
+SpinelNCPInstance::setup_property_supported_by_class(const std::string& prop_name)
+{
+	return NCPInstanceBase::setup_property_supported_by_class(prop_name);
+}
+
+SpinelNCPControlInterface&
+SpinelNCPInstance::get_control_interface()
+{
+	return mControlInterface;
+}
+
+std::set<std::string>
+SpinelNCPInstance::get_supported_property_keys()const
+{
+	std::set<std::string> properties (NCPInstanceBase::get_supported_property_keys());
+
+	properties.insert(kWPANTUNDProperty_ConfigNCPDriverName);
+	properties.insert(kWPANTUNDProperty_NCPChannel);
+	properties.insert(kWPANTUNDProperty_NCPFrequency);
+	properties.insert(kWPANTUNDProperty_NCPRSSI);
+
+	properties.insert(kWPANTUNDProperty_ThreadLeaderAddress);
+	properties.insert(kWPANTUNDProperty_ThreadLeaderRouterID);
+	properties.insert(kWPANTUNDProperty_ThreadLeaderWeight);
+	properties.insert(kWPANTUNDProperty_ThreadLeaderLocalWeight);
+	properties.insert(kWPANTUNDProperty_ThreadNetworkData);
+	properties.insert(kWPANTUNDProperty_ThreadNetworkDataVersion);
+	properties.insert(kWPANTUNDProperty_ThreadStableNetworkDataVersion);
+
+	return properties;
+}
+
+cms_t
+SpinelNCPInstance::get_ms_to_next_event(void)
+{
+	cms_t cms = EventHandler::get_ms_to_next_event();
+
+	if (ncp_state_is_detached_from_ncp(get_ncp_state())) {
+		return CMS_DISTANT_FUTURE;
+	}
+
+	// If the control protothread hasn't even started, set cms to zero.
+	if (0 == mControlPT.lc) {
+		cms = 0;
+	}
+
+	if (!mTaskQueue.empty()) {
+		int tmp_cms = mTaskQueue.front()->get_ms_to_next_event();
+		if (tmp_cms < cms) {
+			cms = tmp_cms;
+		}
+	}
+
+	if (cms < 0) {
+		cms = 0;
+	}
+
+	return cms;
+}
+
+void
+SpinelNCPInstance::get_property(
+	const std::string& key,
+	CallbackWithStatusArg1 cb
+) {
+	syslog(LOG_INFO, "get_property: key: \"%s\"", key.c_str());
+
+	if (strcaseequal(key.c_str(), kWPANTUNDProperty_ConfigNCPDriverName)) {
+		cb(0, boost::any(std::string("spinel")));
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NCPCCAThreshold)) {
+		if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				this,
+				cb,
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_CCA_THRESHOLD),
+				NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+				SPINEL_DATATYPE_INT8_S
+			)
+		))) {
+			cb(kWPANTUNDStatus_InvalidForCurrentState, boost::any());
+		}
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NCPFrequency)) {
+		if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				this,
+				cb,
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_FREQ),
+				NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+				SPINEL_DATATYPE_INT32_S
+			)
+		))) {
+			cb(kWPANTUNDStatus_InvalidForCurrentState, boost::any());
+		}
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NetworkKey)) {
+		if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				this,
+				cb,
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_NET_MASTER_KEY),
+				NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+				SPINEL_DATATYPE_DATA_S
+			)
+		))) {
+			cb(kWPANTUNDStatus_InvalidForCurrentState, boost::any());
+		}
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NetworkKeyIndex)) {
+		if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				this,
+				cb,
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_NET_KEY_SEQUENCE),
+				NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+				SPINEL_DATATYPE_UINT32_S
+			)
+		))) {
+			cb(kWPANTUNDStatus_InvalidForCurrentState, boost::any());
+		}
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NCPRSSI)) {
+		if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				this,
+				cb,
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_RSSI),
+				NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+				SPINEL_DATATYPE_INT8_S
+			)
+		))) {
+			cb(kWPANTUNDStatus_InvalidForCurrentState, boost::any());
+		}
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadLeaderAddress)) {
+		if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				this,
+				cb,
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_THREAD_LEADER_ADDR),
+				NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+				SPINEL_DATATYPE_IPv6ADDR_S
+			)
+		))) {
+			cb(kWPANTUNDStatus_InvalidForCurrentState, boost::any());
+		}
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadLeaderRouterID)) {
+		if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				this,
+				cb,
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_THREAD_LEADER_RID),
+				NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+				SPINEL_DATATYPE_UINT8_S
+			)
+		))) {
+			cb(kWPANTUNDStatus_InvalidForCurrentState, boost::any());
+		}
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadLeaderWeight)) {
+		if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				this,
+				cb,
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_THREAD_LEADER_WEIGHT),
+				NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+				SPINEL_DATATYPE_UINT8_S
+			)
+		))) {
+			cb(kWPANTUNDStatus_InvalidForCurrentState, boost::any());
+		}
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadLeaderLocalWeight)) {
+		if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				this,
+				cb,
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_THREAD_LOCAL_LEADER_WEIGHT),
+				NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+				SPINEL_DATATYPE_UINT8_S
+			)
+		))) {
+			cb(kWPANTUNDStatus_InvalidForCurrentState, boost::any());
+		}
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadNetworkData)) {
+		if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				this,
+				cb,
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_THREAD_NETWORK_DATA),
+				NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+				SPINEL_DATATYPE_DATA_S
+			)
+		))) {
+			cb(kWPANTUNDStatus_InvalidForCurrentState, boost::any());
+		}
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadNetworkDataVersion)) {
+		if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				this,
+				cb,
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_THREAD_NETWORK_DATA_VERSION),
+				NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+				SPINEL_DATATYPE_UINT8_S
+			)
+		))) {
+			cb(kWPANTUNDStatus_InvalidForCurrentState, boost::any());
+		}
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadStableNetworkData)) {
+		if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				this,
+				cb,
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_THREAD_STABLE_NETWORK_DATA),
+				NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+				SPINEL_DATATYPE_DATA_S
+			)
+		))) {
+			cb(kWPANTUNDStatus_InvalidForCurrentState, boost::any());
+		}
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadStableNetworkDataVersion)) {
+		if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				this,
+				cb,
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_THREAD_STABLE_NETWORK_DATA_VERSION),
+				NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+				SPINEL_DATATYPE_UINT8_S
+			)
+		))) {
+			cb(kWPANTUNDStatus_InvalidForCurrentState, boost::any());
+		}
+
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_IPv6MeshLocalPrefix) && !buffer_is_nonzero(mNCPV6Prefix, sizeof(mNCPV6Prefix))) {
+		if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				this,
+				cb,
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_IPV6_ML_PREFIX),
+				NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+				SPINEL_DATATYPE_IPv6ADDR_S
+			)
+		))) {
+			cb(kWPANTUNDStatus_InvalidForCurrentState, boost::any());
+		}
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_IPv6MeshLocalAddress) && !buffer_is_nonzero(mNCPV6Prefix, sizeof(mNCPV6Prefix))) {
+		if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				this,
+				cb,
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_IPV6_ML_ADDR),
+				NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+				SPINEL_DATATYPE_IPv6ADDR_S
+			)
+		))) {
+			cb(kWPANTUNDStatus_InvalidForCurrentState, boost::any());
+		}
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_IPv6LinkLocalAddress) && !IN6_IS_ADDR_LINKLOCAL(&mNCPLinkLocalAddress)) {
+		if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				this,
+				cb,
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_IPV6_ML_ADDR),
+				NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+				SPINEL_DATATYPE_IPv6ADDR_S
+			)
+		))) {
+			cb(kWPANTUNDStatus_InvalidForCurrentState, boost::any());
+		}
+
+	} else {
+		NCPInstanceBase::get_property(key, cb);
+	}
+}
+
+void
+SpinelNCPInstance::set_property(
+	const std::string& key,
+	const boost::any& value,
+	CallbackWithStatus cb
+) {
+	syslog(LOG_INFO, "set_property: key: \"%s\"", key.c_str());
+
+	// If we are disabled, then the only property we
+	// are allowed to set is kWPANTUNDProperty_DaemonEnabled.
+	if (!mEnabled && !strcaseequal(key.c_str(), kWPANTUNDProperty_DaemonEnabled)) {
+		cb(kWPANTUNDStatus_InvalidWhenDisabled);
+		return;
+	}
+
+	try {
+		if (strcaseequal(key.c_str(), kWPANTUNDProperty_NCPChannel)) {
+			int channel = any_to_int(value);
+			mCurrentNetworkInstance.channel = channel;
+
+			if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+				new SpinelNCPTaskSendCommand(
+					this,
+					boost::bind(cb,_1),
+					SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT8_S), SPINEL_PROP_PHY_CHAN, channel)
+				)
+			))) {
+				cb(kWPANTUNDStatus_InvalidForCurrentState);
+			}
+		} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NCPCCAThreshold)) {
+			int cca = any_to_int(value);
+
+			if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+				new SpinelNCPTaskSendCommand(
+					this,
+					boost::bind(cb,_1),
+					SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_INT8_S), SPINEL_PROP_PHY_CCA_THRESHOLD, cca)
+				)
+			))) {
+				cb(kWPANTUNDStatus_InvalidForCurrentState);
+			}
+		} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NetworkPANID)) {
+			uint16_t panid = any_to_int(value);
+
+			if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+				new SpinelNCPTaskSendCommand(
+					this,
+					boost::bind(cb,_1),
+					SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT16_S), SPINEL_PROP_MAC_15_4_PANID, panid)
+				)
+			))) {
+				cb(kWPANTUNDStatus_InvalidForCurrentState);
+			}
+		} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NetworkKey)) {
+			Data network_key = any_to_data(value);
+
+			if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+				new SpinelNCPTaskSendCommand(
+					this,
+					boost::bind(cb,_1),
+					SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_DATA_S), SPINEL_PROP_NET_MASTER_KEY, network_key.data(), network_key.size())
+				)
+			))) {
+				cb(kWPANTUNDStatus_InvalidForCurrentState);
+			}
+		} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NetworkXPANID)) {
+			Data xpanid = any_to_data(value);
+
+			if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+				new SpinelNCPTaskSendCommand(
+					this,
+					boost::bind(cb,_1),
+					SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_DATA_S), SPINEL_PROP_NET_XPANID, xpanid.data(), xpanid.size())
+				)
+			))) {
+				cb(kWPANTUNDStatus_InvalidForCurrentState);
+			}
+		} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NetworkKey)) {
+			Data network_key = any_to_data(value);
+
+			if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+				new SpinelNCPTaskSendCommand(
+					this,
+					boost::bind(cb,_1),
+					SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_DATA_S), SPINEL_PROP_NET_MASTER_KEY, network_key.data(), network_key.size())
+				)
+			))) {
+				cb(kWPANTUNDStatus_InvalidForCurrentState);
+			}
+		} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NetworkKeyIndex)) {
+			uint32_t key_index = any_to_int(value);
+
+			if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+				new SpinelNCPTaskSendCommand(
+					this,
+					boost::bind(cb,_1),
+					SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT32_S), SPINEL_PROP_NET_KEY_SEQUENCE, key_index)
+				)
+			))) {
+				cb(kWPANTUNDStatus_InvalidForCurrentState);
+			}
+		} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NetworkName)) {
+			std::string str = any_to_string(value);
+
+			if (-1 == start_new_task(boost::shared_ptr<SpinelNCPTask>(
+				new SpinelNCPTaskSendCommand(
+					this,
+					boost::bind(cb,_1),
+					SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UTF8_S), SPINEL_PROP_NET_NETWORK_NAME, str.c_str())
+				)
+			))) {
+				cb(kWPANTUNDStatus_InvalidForCurrentState);
+			}
+		} else {
+			NCPInstanceBase::set_property(key, value, cb);
+		}
+
+	} catch (const boost::bad_any_cast &x) {
+		// We will get a bad_any_cast exception if the property is of
+		// the wrong type.
+		syslog(LOG_ERR,"set_property: Bad type for property \"%s\" (%s)", key.c_str(), x.what());
+		cb(kWPANTUNDStatus_InvalidArgument);
+	} catch (const std::invalid_argument &x) {
+		// We will get a bad_any_cast exception if the property is of
+		// the wrong type.
+		syslog(LOG_ERR,"set_property: Invalid argument for property \"%s\" (%s)", key.c_str(), x.what());
+		cb(kWPANTUNDStatus_InvalidArgument);
+	}
+
+}
+
+void
+SpinelNCPInstance::reset_tasks(wpantund_status_t status)
+{
+	NCPInstanceBase::reset_tasks(status);
+	while(!mTaskQueue.empty()) {
+		mTaskQueue.front()->finish(status);
+		mTaskQueue.pop_front();
+	}
+}
+
+void
+SpinelNCPInstance::handle_ncp_spinel_value_is(spinel_prop_key_t key, const uint8_t* value_data_ptr, spinel_size_t value_data_len)
+{
+	if (key == SPINEL_PROP_LAST_STATUS) {
+		spinel_status_t status = SPINEL_STATUS_OK;
+		spinel_datatype_unpack(value_data_ptr, value_data_len, "i", &status);
+		if ((status >= SPINEL_STATUS_RESET__BEGIN) && (status <= SPINEL_STATUS_RESET__END)) {
+			syslog(LOG_NOTICE, "[-NCP-]: NCP was reset (%d)", status);
+			process_event(EVENT_NCP_RESET, status);
+			if (!mResetIsExpected && (mDriverState == NORMAL_OPERATION)) {
+				wpantund_status_t wstatus = kWPANTUNDStatus_NCP_Reset;
+				switch(status) {
+				case SPINEL_STATUS_RESET_CRASH:
+				case SPINEL_STATUS_RESET_FAULT:
+				case SPINEL_STATUS_RESET_ASSERT:
+				case SPINEL_STATUS_RESET_OTHER:
+					wstatus = kWPANTUNDStatus_NCP_Crashed;
+					break;
+				default:
+					break;
+				}
+				reinitialize_ncp();
+				reset_tasks(wstatus);
+			}
+			return;
+		} else if (status == SPINEL_STATUS_INVALID_COMMAND) {
+			syslog(LOG_NOTICE, "[-NCP-]: COMMAND NOT RECOGNIZED");
+		}
+	} else if (key == SPINEL_PROP_NCP_VERSION) {
+		const char* ncp_version = NULL;
+		spinel_datatype_unpack(value_data_ptr, value_data_len, "U", &ncp_version);
+
+		set_ncp_version_string(ncp_version);
+
+
+	} else if (key == SPINEL_PROP_INTERFACE_TYPE) {
+		unsigned int interface_type = 0;
+		spinel_datatype_unpack(value_data_ptr, value_data_len, "i", &interface_type);
+
+		if (interface_type != SPINEL_PROTOCOL_TYPE_THREAD) {
+			syslog(LOG_CRIT, "[-NCP-]: NCP is using unsupported protocol type (%d)", interface_type);
+			change_ncp_state(FAULT);
+			// TODO: Possible firmware update
+		}
+
+
+	} else if (key == SPINEL_PROP_PROTOCOL_VERSION) {
+		unsigned int protocol_version_major = 0;
+		unsigned int protocol_version_minor = 0;
+		spinel_datatype_unpack(value_data_ptr, value_data_len, "ii", &protocol_version_major, &protocol_version_minor);
+
+		if (protocol_version_major != SPINEL_PROTOCOL_VERSION_THREAD_MAJOR) {
+			syslog(LOG_CRIT, "[-NCP-]: NCP is using unsupported protocol version (NCP:%d, wpantund:%d)", protocol_version_major, SPINEL_PROTOCOL_VERSION_THREAD_MAJOR);
+			change_ncp_state(FAULT);
+			// TODO: Possible firmware update
+		}
+
+		if (protocol_version_minor != SPINEL_PROTOCOL_VERSION_THREAD_MINOR) {
+			syslog(LOG_WARNING, "[-NCP-]: NCP is using different protocol minor version (NCP:%d, wpantund:%d)", protocol_version_minor, SPINEL_PROTOCOL_VERSION_THREAD_MINOR);
+		}
+
+	} else if (key == SPINEL_PROP_CAPS) {
+		const uint8_t* data_ptr = value_data_ptr;
+		spinel_size_t data_len = value_data_len;
+		std::set<unsigned int> capabilities;
+
+		while(data_len != 0) {
+			unsigned int value = 0;
+			spinel_ssize_t parse_len = spinel_datatype_unpack(data_ptr, data_len, SPINEL_DATATYPE_UINT_PACKED_S, &value);
+			if (parse_len <= 0) {
+				syslog(LOG_WARNING, "[-NCP-]: Capability Parse failure");
+				break;
+			}
+			capabilities.insert(value);
+
+			data_ptr += parse_len;
+			data_len -= parse_len;
+		}
+
+		if (capabilities != mCapabilities) {
+			mCapabilities = capabilities;
+		}
+
+	} else if (key == SPINEL_PROP_NET_NETWORK_NAME) {
+		const char* value = NULL;
+		spinel_datatype_unpack(value_data_ptr, value_data_len, "U", &value);
+		if (value && (mCurrentNetworkInstance.name != value)) {
+			mCurrentNetworkInstance.name = value;
+			syslog(LOG_WARNING, "Network Name: %s", mCurrentNetworkInstance.name.c_str());
+			signal_property_changed(kWPANTUNDProperty_NetworkName, mCurrentNetworkInstance.name);
+		}
+
+	} else if (key == SPINEL_PROP_IPV6_LL_ADDR) {
+		spinel_ipv6addr_t *addr = NULL;
+		spinel_datatype_unpack(value_data_ptr, value_data_len, "6", &addr);
+		if (addr) {
+			memcpy(mNCPLinkLocalAddress.s6_addr, addr->s6_addr, sizeof(mNCPLinkLocalAddress));
+			signal_property_changed(kWPANTUNDProperty_IPv6LinkLocalAddress, in6_addr_to_string(*addr));
+		}
+
+	} else if (key == SPINEL_PROP_IPV6_ML_ADDR) {
+		spinel_ipv6addr_t *addr = NULL;
+		spinel_datatype_unpack(value_data_ptr, value_data_len, "6", &addr);
+		if (addr
+		 && buffer_is_nonzero(addr->s6_addr, 8)
+		 && (0 != memcmp(mNCPMeshLocalAddress.s6_addr, addr->s6_addr, sizeof(mNCPMeshLocalAddress)))
+		) {
+			memcpy(mNCPMeshLocalAddress.s6_addr, addr->s6_addr, sizeof(mNCPMeshLocalAddress));
+			signal_property_changed(kWPANTUNDProperty_IPv6MeshLocalAddress, in6_addr_to_string(*addr));
+			mPrimaryInterface->set_realm_local_address(addr);
+		}
+
+	} else if (key == SPINEL_PROP_IPV6_ML_PREFIX) {
+		spinel_ipv6addr_t *addr = NULL;
+		spinel_datatype_unpack(value_data_ptr, value_data_len, "6", &addr);
+		if (addr
+		 && buffer_is_nonzero(addr->s6_addr, 8)
+		 && (0 != memcmp(mNCPV6Prefix, addr, sizeof(mNCPV6Prefix)))
+		) {
+			memcpy(mNCPV6Prefix, addr, sizeof(mNCPV6Prefix));
+			struct in6_addr prefix_addr (mNCPMeshLocalAddress);
+			// Zero out the lower 64 bits.
+			memset(prefix_addr.s6_addr+8, 0, 8);
+			signal_property_changed(kWPANTUNDProperty_IPv6MeshLocalPrefix, in6_addr_to_string(prefix_addr) + "/64");
+		}
+
+	} else if (key == SPINEL_PROP_IPV6_ADDRESS_TABLE) {
+		std::map<struct in6_addr, GlobalAddressEntry>::const_iterator iter;
+		std::map<struct in6_addr, GlobalAddressEntry> global_addresses(mGlobalAddresses);
+		clear_nonpermanent_global_addresses();
+
+		while(value_data_len > 0) {
+			const uint8_t *entry_ptr = NULL;
+			spinel_size_t entry_len = 0;
+			spinel_ssize_t len = 0;
+			len = spinel_datatype_unpack(value_data_ptr, value_data_len, "D.", &entry_ptr, &entry_len);
+			if (len < 1) {
+				break;
+			}
+			global_addresses.erase(*reinterpret_cast<const struct in6_addr*>(entry_ptr));
+			handle_ncp_spinel_value_inserted(key, entry_ptr, entry_len);
+
+			value_data_ptr += len;
+			value_data_len -= len;
+		}
+
+		// Since this was the whole list, we need
+		// to remove the addresses that weren't in
+		// the list.
+		for (iter = global_addresses.begin(); iter!= global_addresses.end(); ++iter) {
+			if (!iter->second.mUserAdded) {
+				update_global_address(iter->first, 0, 0, 0);
+			}
+		}
+	} else if (key == SPINEL_PROP_HWADDR) {
+		nl::Data hwaddr(value_data_ptr, value_data_len);
+		if (value_data_len == sizeof(mNCPHardwareAddress)) {
+			if (0 != memcmp(value_data_ptr, mNCPHardwareAddress, sizeof(mNCPHardwareAddress))) {
+				set_hardware_address(value_data_ptr);
+			}
+		}
+
+	} else if (key == SPINEL_PROP_MAC_15_4_PANID) {
+		uint16_t panid;
+		spinel_datatype_unpack(value_data_ptr, value_data_len, SPINEL_DATATYPE_UINT16_S, &panid);
+		if (panid != mCurrentNetworkInstance.panid) {
+			mCurrentNetworkInstance.panid = panid;
+			signal_property_changed(kWPANTUNDProperty_NetworkPANID, panid);
+		}
+
+	} else if (key == SPINEL_PROP_NET_XPANID) {
+		nl::Data xpanid(value_data_ptr, value_data_len);
+		if ((value_data_len == 8) && 0 != memcmp(xpanid.data(), mCurrentNetworkInstance.xpanid, 8)) {
+			memcpy(mCurrentNetworkInstance.xpanid, xpanid.data(), 8);
+			signal_property_changed(kWPANTUNDProperty_NetworkXPANID, xpanid);
+		}
+
+	} else if (key == SPINEL_PROP_NET_MASTER_KEY) {
+		nl::Data network_key(value_data_ptr, value_data_len);
+		if (network_key != mNetworkKey) {
+			mNetworkKey = network_key;
+			signal_property_changed(kWPANTUNDProperty_NetworkKey, mNetworkKey);
+		}
+
+	} else if (key == SPINEL_PROP_NET_KEY_SEQUENCE) {
+		uint32_t network_key_index;
+		spinel_datatype_unpack(value_data_ptr, value_data_len, SPINEL_DATATYPE_UINT32_S, &network_key_index);
+		if (network_key_index != mNetworkKeyIndex) {
+			mNetworkKeyIndex = network_key_index;
+			signal_property_changed(kWPANTUNDProperty_NetworkKeyIndex, mNetworkKeyIndex);
+		}
+
+	} else if (key == SPINEL_PROP_PHY_CHAN) {
+		unsigned int value;
+		spinel_datatype_unpack(value_data_ptr, value_data_len, SPINEL_DATATYPE_UINT_PACKED_S, &value);
+		if (value != mCurrentNetworkInstance.channel) {
+			mCurrentNetworkInstance.channel = value;
+			signal_property_changed(kWPANTUNDProperty_NCPChannel, mCurrentNetworkInstance.channel);
+		}
+
+	} else if (key == SPINEL_PROP_PHY_TX_POWER) {
+		int8_t value;
+		spinel_datatype_unpack(value_data_ptr, value_data_len, SPINEL_DATATYPE_INT8_S, &value);
+		if (value != mTXPower) {
+			mTXPower = value;
+			signal_property_changed(kWPANTUNDProperty_NCPTXPower, mTXPower);
+		}
+
+	} else if (key == SPINEL_PROP_NET_ROLE) {
+		uint8_t value;
+		spinel_datatype_unpack(value_data_ptr, value_data_len, SPINEL_DATATYPE_UINT8_S, &value);
+
+		if (value == SPINEL_NET_ROLE_CHILD) {
+			if (mNodeType != END_DEVICE) {
+				mNodeType = END_DEVICE;
+				signal_property_changed(kWPANTUNDProperty_NetworkNodeType, node_type_to_string(mNodeType));
+			}
+		} else if (value == SPINEL_NET_ROLE_ROUTER) {
+			if (mNodeType != ROUTER) {
+				mNodeType = ROUTER;
+				signal_property_changed(kWPANTUNDProperty_NetworkNodeType, node_type_to_string(mNodeType));
+			}
+		} else if (value == SPINEL_NET_ROLE_LEADER) {
+			if (mNodeType != LEADER) {
+				mNodeType = LEADER;
+				signal_property_changed(kWPANTUNDProperty_NetworkNodeType, node_type_to_string(mNodeType));
+			}
+		}
+	} else if (key == SPINEL_PROP_NET_STATE) {
+		uint8_t value;
+		spinel_datatype_unpack(value_data_ptr, value_data_len, SPINEL_DATATYPE_UINT8_S, &value);
+
+		if (value == SPINEL_NET_STATE_OFFLINE) {
+			change_ncp_state(OFFLINE);
+		} else if (value == SPINEL_NET_STATE_DETACHED) {
+			change_ncp_state(COMMISSIONED);
+		} else if (value == SPINEL_NET_STATE_ATTACHING) {
+			change_ncp_state(ASSOCIATING);
+		} else if (value == SPINEL_NET_STATE_ATTACHED) {
+			if (!ncp_state_is_associated(get_ncp_state())) {
+				change_ncp_state(ASSOCIATED);
+			}
+		}
+
+
+	} else if ((key == SPINEL_PROP_STREAM_NET) || (key == SPINEL_PROP_STREAM_NET_INSECURE)) {
+		const uint8_t* frame_ptr(NULL);
+		unsigned int frame_len(0);
+		spinel_ssize_t ret;
+		uint8_t frame_data_type = FRAME_TYPE_DATA;
+
+		if (SPINEL_PROP_STREAM_NET_INSECURE == key) {
+			frame_data_type = FRAME_TYPE_INSECURE_DATA;
+		}
+
+		ret = spinel_datatype_unpack(
+			value_data_ptr,
+			value_data_len,
+			SPINEL_DATATYPE_DATA_S SPINEL_DATATYPE_DATA_S,
+			&frame_ptr,
+			&frame_len,
+			NULL,
+			NULL
+		);
+
+		__ASSERT_MACROS_check(ret > 0);
+
+		// Analyze the packet to determine if it should be dropped.
+		if ((ret > 0) && should_forward_hostbound_frame(&frame_data_type, frame_ptr, frame_len)) {
+			if (static_cast<bool>(mLegacyInterface) && (frame_data_type == FRAME_TYPE_LEGACY_DATA)) {
+				handle_alt_ipv6_from_ncp(frame_ptr, frame_len);
+			} else {
+				handle_normal_ipv6_from_ncp(frame_ptr, frame_len);
+			}
+		}
+	}
+	process_event(EVENT_NCP_PROP_VALUE_IS, key, value_data_ptr, value_data_len);
+}
+
+void
+SpinelNCPInstance::handle_ncp_spinel_value_inserted(spinel_prop_key_t key, const uint8_t* value_data_ptr, spinel_size_t value_data_len)
+{
+	if (key == SPINEL_PROP_IPV6_ADDRESS_TABLE) {
+			spinel_ipv6addr_t *addr = NULL;
+			uint8_t prefix_len = 0;
+			uint32_t valid_lifetime = 0xFFFFFFFF;
+			uint32_t preferred_lifetime = 0xFFFFFFFF;
+
+			spinel_datatype_unpack(value_data_ptr, value_data_len, "6CLL", &addr, &prefix_len, &valid_lifetime, &preferred_lifetime);
+
+			if (addr != NULL
+				&& buffer_is_nonzero(addr->s6_addr, 8)
+				&& !IN6_IS_ADDR_UNSPECIFIED(addr)
+			) {
+				static const uint8_t rloc_bytes[] = {0x00,0x00,0x00,0xFF,0xFE,0x00};
+				if (IN6_IS_ADDR_LINKLOCAL(addr)) {
+					if (0 != memcmp(rloc_bytes, addr->s6_addr+8, sizeof(rloc_bytes))) {
+						handle_ncp_spinel_value_is(SPINEL_PROP_IPV6_LL_ADDR, addr->s6_addr, sizeof(*addr));
+					}
+				} else if (0 == memcmp(mNCPV6Prefix, addr, sizeof(mNCPV6Prefix))) {
+					if (0 != memcmp(rloc_bytes, addr->s6_addr+8, sizeof(rloc_bytes))) {
+						handle_ncp_spinel_value_is(SPINEL_PROP_IPV6_ML_ADDR, addr->s6_addr, sizeof(*addr));
+					}
+				} else {
+					update_global_address(*addr, valid_lifetime, preferred_lifetime, 0);
+				}
+			}
+	}
+
+
+	process_event(EVENT_NCP_PROP_VALUE_INSERTED, key, value_data_ptr, value_data_len);
+}
+
+void
+SpinelNCPInstance::handle_ncp_state_change(NCPState new_ncp_state, NCPState old_ncp_state)
+{
+	NCPInstanceBase::handle_ncp_state_change(new_ncp_state, old_ncp_state);
+
+	if (ncp_state_is_associated(new_ncp_state)
+	 && !ncp_state_is_associated(old_ncp_state)
+	) {
+		if (!buffer_is_nonzero(mNCPV6Prefix, 8)) {
+			start_new_task(boost::shared_ptr<SpinelNCPTask>(
+				new SpinelNCPTaskSendCommand(
+					this,
+					NilReturn(),
+					SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_IPV6_ML_PREFIX)
+				)
+			));
+		}
+		start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				this,
+				NilReturn(),
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_NET_XPANID)
+			)
+		));
+		start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				this,
+				NilReturn(),
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_MAC_15_4_PANID)
+			)
+		));
+		start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				this,
+				NilReturn(),
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_MAC_15_4_LADDR)
+			)
+		));
+		start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				this,
+				NilReturn(),
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_NET_ROLE)
+			)
+		));
+		start_new_task(boost::shared_ptr<SpinelNCPTask>(
+			new SpinelNCPTaskSendCommand(
+				this,
+				NilReturn(),
+				SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_CHAN)
+			)
+		));
+	} else if (ncp_state_is_joining(new_ncp_state)
+	 && !ncp_state_is_joining(old_ncp_state)
+	) {
+		if (!buffer_is_nonzero(mNCPV6Prefix, 8)) {
+			start_new_task(boost::shared_ptr<SpinelNCPTask>(
+				new SpinelNCPTaskSendCommand(
+					this,
+					NilReturn(),
+					SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_IPV6_ML_PREFIX)
+				)
+			));
+		}
+	}
+}
+
+void
+SpinelNCPInstance::handle_ncp_spinel_value_removed(spinel_prop_key_t key, const uint8_t* value_data_ptr, spinel_size_t value_data_len)
+{
+	process_event(EVENT_NCP_PROP_VALUE_REMOVED, key, value_data_ptr, value_data_len);
+}
+
+void
+SpinelNCPInstance::handle_ncp_spinel_callback(unsigned int command, const uint8_t* cmd_data_ptr, spinel_size_t cmd_data_len)
+{
+	switch (command) {
+	case SPINEL_CMD_PROP_VALUE_IS:
+		{
+			spinel_prop_key_t key;
+			uint8_t* value_data_ptr = NULL;
+			spinel_size_t value_data_len = 0;
+			spinel_ssize_t ret;
+
+			ret = spinel_datatype_unpack(cmd_data_ptr, cmd_data_len, "CiiD", NULL, NULL, &key, &value_data_ptr, &value_data_len);
+
+			__ASSERT_MACROS_check(ret != -1);
+
+			if (ret == -1) {
+				return;
+			}
+
+			syslog(LOG_INFO, "[NCP->] CMD_PROP_VALUE_IS(%s)", spinel_prop_key_to_cstr(key));
+
+			return handle_ncp_spinel_value_is(key, value_data_ptr, value_data_len);
+		}
+		break;
+
+	case SPINEL_CMD_PROP_VALUE_INSERTED:
+		{
+			spinel_prop_key_t key;
+			uint8_t* value_data_ptr;
+			spinel_size_t value_data_len;
+			spinel_ssize_t ret;
+
+			ret = spinel_datatype_unpack(cmd_data_ptr, cmd_data_len, "CiiD", NULL, NULL, &key, &value_data_ptr, &value_data_len);
+
+			__ASSERT_MACROS_check(ret != -1);
+
+			if (ret == -1) {
+				return;
+			}
+
+			syslog(LOG_INFO, "[NCP->] CMD_PROP_VALUE_INSERTED(%s)", spinel_prop_key_to_cstr(key));
+
+			return handle_ncp_spinel_value_inserted(key, value_data_ptr, value_data_len);
+		}
+		break;
+
+	case SPINEL_CMD_PROP_VALUE_REMOVED:
+		{
+			spinel_prop_key_t key;
+			uint8_t* value_data_ptr;
+			spinel_size_t value_data_len;
+			spinel_ssize_t ret;
+
+			ret = spinel_datatype_unpack(cmd_data_ptr, cmd_data_len, "CiiD", NULL, NULL, &key, &value_data_ptr, &value_data_len);
+
+			__ASSERT_MACROS_check(ret != -1);
+
+			if (ret == -1) {
+				return;
+			}
+
+			syslog(LOG_INFO, "[NCP->] CMD_PROP_VALUE_REMOVED(%s)", spinel_prop_key_to_cstr(key));
+
+			return handle_ncp_spinel_value_removed(key, value_data_ptr, value_data_len);
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	process_event(EVENT_NCP(command), cmd_data_ptr[0], cmd_data_ptr, cmd_data_len);
+}
diff --git a/src/ncp-spinel/SpinelNCPInstance.h b/src/ncp-spinel/SpinelNCPInstance.h
new file mode 100644
index 0000000..b05be37
--- /dev/null
+++ b/src/ncp-spinel/SpinelNCPInstance.h
@@ -0,0 +1,238 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __wpantund__SpinelNCPInstance__
+#define __wpantund__SpinelNCPInstance__
+
+#include "NCPInstanceBase.h"
+#include "SpinelNCPControlInterface.h"
+#include "nlpt.h"
+#include "SocketWrapper.h"
+#include "SocketAsyncOp.h"
+
+#include <queue>
+#include <set>
+#include <map>
+#include <errno.h>
+#include "spinel.h"
+
+WPANTUND_DECLARE_NCPINSTANCE_PLUGIN(spinel, SpinelNCPInstance);
+
+#define EVENT_NCP_MARKER         0xAB000000
+#define EVENT_NCP(x)             ((x)|EVENT_NCP_MARKER)
+#define IS_EVENT_FROM_NCP(x)     (((x)&~0xFFFFFF) == EVENT_NCP_MARKER)
+
+
+#define EVENT_NCP_RESET                (0xFF0000|EVENT_NCP_MARKER)
+#define EVENT_NCP_PROP_VALUE_IS        (0xFF0001|EVENT_NCP_MARKER)
+#define EVENT_NCP_PROP_VALUE_INSERTED  (0xFF0002|EVENT_NCP_MARKER)
+#define EVENT_NCP_PROP_VALUE_REMOVED   (0xFF0003|EVENT_NCP_MARKER)
+
+#define NCP_FRAMING_OVERHEAD 3
+
+#define CONTROL_REQUIRE_EMPTY_OUTBOUND_BUFFER_WITHIN(seconds, error_label) do { \
+		EH_WAIT_UNTIL_WITH_TIMEOUT(seconds, (GetInstance(this)->mOutboundBufferLen <= 0) && GetInstance(this)->mOutboundCallback.empty()); \
+		require_string(!eh_did_timeout, error_label, "Timed out while waiting " # seconds " seconds for empty outbound buffer"); \
+	} while (0)
+
+#define CONTROL_REQUIRE_OUTBOUND_BUFFER_FLUSHED_WITHIN(seconds, error_label) do { \
+		static const int ___crsw_send_finished = 0xFF000000 | __LINE__; \
+		static const int ___crsw_send_failed = 0xFE000000 | __LINE__; \
+		__ASSERT_MACROS_check(GetInstance(this)->mOutboundCallback.empty()); \
+		require(GetInstance(this)->mOutboundBufferLen > 0, error_label); \
+		GetInstance(this)->mOutboundCallback = CALLBACK_FUNC_SPLIT( \
+			boost::bind(&NCPInstanceBase::process_event_helper, GetInstance(this), ___crsw_send_finished), \
+			boost::bind(&NCPInstanceBase::process_event_helper, GetInstance(this), ___crsw_send_failed) \
+		); \
+		GetInstance(this)->mOutboundBuffer[0] = mLastHeader; \
+		EH_WAIT_UNTIL_WITH_TIMEOUT(seconds, (event == ___crsw_send_finished) || (event == ___crsw_send_failed)); \
+		require_string(!eh_did_timeout, error_label, "Timed out while trying to send command"); \
+		require_string(event == ___crsw_send_finished, error_label, "Failure while trying to send command"); \
+	} while (0)
+
+#define CONTROL_REQUIRE_PREP_TO_SEND_COMMAND_WITHIN(timeout, error_label) do { \
+		CONTROL_REQUIRE_EMPTY_OUTBOUND_BUFFER_WITHIN(timeout, error_label); \
+		GetInstance(this)->mLastTID = SPINEL_GET_NEXT_TID(GetInstance(this)->mLastTID); \
+		mLastHeader = (SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0 | (GetInstance(this)->mLastTID << SPINEL_HEADER_TID_SHIFT)); \
+	} while (false)
+
+#define CONTROL_REQUIRE_COMMAND_RESPONSE_WITHIN(timeout, error_label) do { \
+		EH_REQUIRE_WITHIN(	\
+			timeout,	\
+			IS_EVENT_FROM_NCP(event) && GetInstance(this)->mInboundHeader == mLastHeader, \
+			error_label	\
+		);	\
+	} while (false)
+
+namespace nl {
+namespace wpantund {
+
+class SpinelNCPTask;
+class SpinelNCPControlInterface;
+
+class SpinelNCPInstance : public NCPInstanceBase {
+	friend class SpinelNCPControlInterface;
+	friend class SpinelNCPTask;
+	friend class SpinelNCPTaskDeepSleep;
+	friend class SpinelNCPTaskWake;
+	friend class SpinelNCPTaskJoin;
+	friend class SpinelNCPTaskForm;
+	friend class SpinelNCPTaskScan;
+	friend class SpinelNCPTaskLeave;
+	friend class SpinelNCPTaskSendCommand;
+public:
+
+	enum DriverState {
+		INITIALIZING,
+		INITIALIZING_WAITING_FOR_RESET,
+		NORMAL_OPERATION
+	};
+
+public:
+	SpinelNCPInstance(const Settings& settings = Settings());
+
+	virtual ~SpinelNCPInstance();
+
+	virtual SpinelNCPControlInterface& get_control_interface();
+
+	virtual int vprocess_event(int event, va_list args);
+
+
+protected:
+	virtual char ncp_to_driver_pump();
+	virtual char driver_to_ncp_pump();
+
+	int start_new_task(const boost::shared_ptr<SpinelNCPTask> &task);
+
+protected:
+
+	int vprocess_init(int event, va_list args);
+	int vprocess_disabled(int event, va_list args);
+	int vprocess_associated(int event, va_list args);
+	int vprocess_resume(int event, va_list args);
+	int vprocess_offline(int event, va_list args);
+
+	void handle_ncp_spinel_callback(unsigned int command, const uint8_t* cmd_data_ptr, spinel_size_t cmd_data_len);
+	void handle_ncp_spinel_value_is(spinel_prop_key_t key, const uint8_t* value_data_ptr, spinel_size_t value_data_len);
+	void handle_ncp_spinel_value_inserted(spinel_prop_key_t key, const uint8_t* value_data_ptr, spinel_size_t value_data_len);
+	void handle_ncp_spinel_value_removed(spinel_prop_key_t key, const uint8_t* value_data_ptr, spinel_size_t value_data_len);
+	void handle_ncp_state_change(NCPState new_ncp_state, NCPState old_ncp_state);
+
+public:
+	static bool setup_property_supported_by_class(const std::string& prop_name);
+
+	virtual std::set<std::string> get_supported_property_keys()const;
+
+	virtual void get_property(
+	    const std::string& key,
+	    CallbackWithStatusArg1 cb
+	);
+
+	virtual void set_property(
+	    const std::string& key,
+	    const boost::any& value,
+	    CallbackWithStatus cb
+	);
+
+	virtual cms_t get_ms_to_next_event(void);
+
+	virtual void reset_tasks(wpantund_status_t status = kWPANTUNDStatus_Canceled);
+
+private:
+	SpinelNCPControlInterface mControlInterface;
+
+	uint8_t mLastTID;
+
+	uint8_t mLastHeader;
+
+	uint8_t mInboundFrame[SPINEL_FRAME_MAX_SIZE];
+	uint8_t mInboundHeader;
+	spinel_size_t mInboundFrameSize;
+	uint8_t mInboundFrameDataType;
+	const uint8_t* mInboundFrameDataPtr;
+	spinel_size_t mInboundFrameDataLen;
+	uint16_t mInboundFrameHDLCCRC;
+
+	uint8_t mOutboundBufferHeader[3];
+	uint8_t mOutboundBuffer[SPINEL_FRAME_MAX_SIZE];
+	uint8_t mOutboundBufferType;
+	spinel_ssize_t mOutboundBufferLen;
+	spinel_ssize_t mOutboundBufferSent;
+	uint8_t mOutboundBufferEscaped[SPINEL_FRAME_MAX_SIZE*2];
+	spinel_ssize_t mOutboundBufferEscapedLen;
+	boost::function<void(int)> mOutboundCallback;
+
+	int mTXPower;
+
+	std::set<unsigned int> mCapabilities;
+	uint32_t mDefaultChannelMask;
+
+
+	DriverState mDriverState;
+
+	// Protothreads and related state
+	PT mSleepPT;
+	PT mSubPT;
+
+	int mSubPTIndex;
+
+	Data mNetworkKey;
+	uint32_t mNetworkKeyIndex;
+
+	bool mResetIsExpected;
+
+	// Task management
+	std::list<boost::shared_ptr<SpinelNCPTask> > mTaskQueue;
+
+}; // class SpinelNCPInstance
+
+extern class SpinelNCPInstance* gNCPInstance;
+
+template<class C>
+inline SpinelNCPInstance* GetInstance(C *x)
+{
+	return x->mInstance;
+}
+
+template<>
+inline SpinelNCPInstance* GetInstance<SpinelNCPInstance>(SpinelNCPInstance *x)
+{
+	return x;
+}
+
+template<class C>
+inline nl::wpantund::SpinelNCPControlInterface* GetInterface(C *x)
+{
+	return x->mInterface;
+}
+
+template<>
+inline nl::wpantund::SpinelNCPControlInterface* GetInterface<nl::wpantund::SpinelNCPControlInterface>(nl::wpantund::SpinelNCPControlInterface *x)
+{
+	return x;
+}
+
+bool ncp_event_matches_header_from_args(int event, va_list args, uint8_t last_header);
+
+int peek_ncp_callback_status(int event, va_list args);
+
+}; // namespace wpantund
+}; // namespace nl
+
+#endif /* defined(__wpantund__SpinelNCPInstance__) */
diff --git a/src/ncp-spinel/SpinelNCPTask.cpp b/src/ncp-spinel/SpinelNCPTask.cpp
new file mode 100644
index 0000000..76131f0
--- /dev/null
+++ b/src/ncp-spinel/SpinelNCPTask.cpp
@@ -0,0 +1,125 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "assert-macros.h"
+#include <syslog.h>
+#include <errno.h>
+#include "SpinelNCPTask.h"
+#include "SpinelNCPInstance.h"
+
+using namespace nl;
+using namespace nl::wpantund;
+
+SpinelNCPTask::SpinelNCPTask(SpinelNCPInstance* _instance, CallbackWithStatusArg1 cb):
+	mInstance(_instance), mCB(cb), mNextCommandTimeout(NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT)
+{
+}
+
+SpinelNCPTask::~SpinelNCPTask()
+{
+	finish(kWPANTUNDStatus_Canceled);
+}
+
+void
+SpinelNCPTask::finish(int status, const boost::any& value)
+{
+	if (!mCB.empty()) {
+		mCB(status, value);
+		mCB = CallbackWithStatusArg1();
+	}
+}
+
+static bool
+spinel_callback_is_reset(int event, va_list args)
+{
+	int status = peek_ncp_callback_status(event, args);
+	return (status >= SPINEL_STATUS_RESET__BEGIN)
+	    && (status < SPINEL_STATUS_RESET__END);
+}
+
+int
+SpinelNCPTask::vprocess_send_command(int event, va_list args)
+{
+	EH_BEGIN_SUB(&mSubPT);
+
+	require(mNextCommand.size() < sizeof(GetInstance(this)->mOutboundBuffer), on_error);
+
+	CONTROL_REQUIRE_PREP_TO_SEND_COMMAND_WITHIN(NCP_DEFAULT_COMMAND_SEND_TIMEOUT, on_error);
+	memcpy(GetInstance(this)->mOutboundBuffer, mNextCommand.data(), mNextCommand.size());
+	GetInstance(this)->mOutboundBufferLen = static_cast<spinel_ssize_t>(mNextCommand.size());
+	CONTROL_REQUIRE_OUTBOUND_BUFFER_FLUSHED_WITHIN(NCP_DEFAULT_COMMAND_SEND_TIMEOUT, on_error);
+
+	if (mNextCommand[1] == SPINEL_CMD_RESET) {
+		mInstance->mResetIsExpected = true;
+		EH_REQUIRE_WITHIN(
+			mNextCommandTimeout,
+			IS_EVENT_FROM_NCP(event)
+			  && ( (GetInstance(this)->mInboundHeader == mLastHeader)
+			    || spinel_callback_is_reset(event, args)
+			  ),
+			on_error
+		);
+		mInstance->mResetIsExpected = false;
+	} else {
+		CONTROL_REQUIRE_COMMAND_RESPONSE_WITHIN(mNextCommandTimeout, on_error);
+	}
+
+	mNextCommandRet = peek_ncp_callback_status(event, args);
+
+	if (mNextCommandRet) {
+		mNextCommandRet = WPANTUND_NCPERROR_TO_STATUS(mNextCommandRet);
+	}
+
+	EH_EXIT();
+
+on_error:
+	mNextCommandRet = kWPANTUNDStatus_Timeout;
+
+	EH_END();
+}
+
+nl::Data
+nl::wpantund::SpinelPackData(const char* pack_format, ...)
+{
+	Data ret(64);
+
+	va_list args;
+	va_start(args, pack_format);
+
+	do {
+		spinel_ssize_t packed_size = spinel_datatype_vpack(ret.data(), (spinel_size_t)ret.size(), pack_format, args);
+
+		if (packed_size < 0) {
+			ret.clear();
+			break;
+		} else if (packed_size > ret.size()) {
+			ret.resize(packed_size);
+			continue;
+		} else {
+			ret.resize(packed_size);
+		}
+	} while(false);
+
+	va_end(args);
+	return ret;
+}
diff --git a/src/ncp-spinel/SpinelNCPTask.h b/src/ncp-spinel/SpinelNCPTask.h
new file mode 100644
index 0000000..5ad5bfc
--- /dev/null
+++ b/src/ncp-spinel/SpinelNCPTask.h
@@ -0,0 +1,73 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __wpantund__SpinelNCPTask__
+#define __wpantund__SpinelNCPTask__
+
+#include "NCPInstanceBase.h"
+#include "nlpt.h"
+#include "SocketWrapper.h"
+#include "SocketAsyncOp.h"
+
+#include <boost/enable_shared_from_this.hpp>
+#include <queue>
+#include <set>
+#include <map>
+#include <errno.h>
+#include "spinel.h"
+
+namespace nl {
+namespace wpantund {
+
+class SpinelNCPInstance;
+
+class SpinelNCPTask
+	: public boost::enable_shared_from_this<SpinelNCPTask>
+	, public boost::signals2::trackable
+	, public nl::EventHandler
+{
+public:
+	SpinelNCPTask(SpinelNCPInstance* _instance, CallbackWithStatusArg1 cb);
+	virtual ~SpinelNCPTask();
+
+	virtual int vprocess_event(int event, va_list args) = 0;
+
+	virtual void finish(int status, const boost::any& value = boost::any());
+
+	bool peek_callback_is_prop_value_is(int event, va_list args, spinel_prop_key_t);
+
+	SpinelNCPInstance* mInstance;
+
+	int vprocess_send_command(int event, va_list args);
+
+protected:
+	CallbackWithStatusArg1 mCB;
+	uint8_t mLastHeader;
+	PT mSubPT;
+	Data mNextCommand;
+	int mNextCommandRet;
+	int mNextCommandTimeout;
+};
+
+nl::Data SpinelPackData(const char* pack_format, ...);
+
+}; // namespace wpantund
+}; // namespace nl
+
+#endif /* defined(__wpantund__SpinelNCPInstance__) */
diff --git a/src/ncp-spinel/SpinelNCPTaskDeepSleep.cpp b/src/ncp-spinel/SpinelNCPTaskDeepSleep.cpp
new file mode 100644
index 0000000..947fabb
--- /dev/null
+++ b/src/ncp-spinel/SpinelNCPTaskDeepSleep.cpp
@@ -0,0 +1,124 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *		This file contains the code that handles transitioning the
+ *      NCP into a deep-sleep state.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "assert-macros.h"
+#include <syslog.h>
+#include <errno.h>
+#include "SpinelNCPTaskDeepSleep.h"
+#include "SpinelNCPInstance.h"
+#include "spinel-extra.h"
+
+using namespace nl;
+using namespace nl::wpantund;
+
+nl::wpantund::SpinelNCPTaskDeepSleep::SpinelNCPTaskDeepSleep(
+	SpinelNCPInstance* instance,
+	CallbackWithStatusArg1 cb
+):	SpinelNCPTask(instance, cb)
+{
+}
+
+void
+nl::wpantund::SpinelNCPTaskDeepSleep::finish(int status, const boost::any& value)
+{
+	mInstance->mResetIsExpected = false;
+
+	SpinelNCPTask::finish(status, value);
+}
+
+
+int
+nl::wpantund::SpinelNCPTaskDeepSleep::vprocess_event(int event, va_list args)
+{
+	int ret = kWPANTUNDStatus_Failure;
+
+	EH_BEGIN();
+
+	// The first event to a task is EVENT_STARTING_TASK. The following
+	// line makes sure that we don't start processing this task
+	// until it is properly scheduled. All tasks immediately receive
+	// the initial `EVENT_STARTING_TASK` event, but further events
+	// will only be received by that task once it is that task's turn
+	// to execute.
+	EH_WAIT_UNTIL(EVENT_STARTING_TASK != event);
+
+	// If we are still initializing, wait until we are finished.
+	EH_WAIT_UNTIL_WITH_TIMEOUT(mInstance->mDriverState == SpinelNCPInstance::NORMAL_OPERATION, NCP_DEFAULT_COMMAND_SEND_TIMEOUT);
+
+	if (mInstance->can_set_ncp_power()) {
+		mNextCommand = SpinelPackData(SPINEL_FRAME_PACK_CMD_NOOP);
+		EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+
+		// Wait for half a second after the last ncp-generated event before
+		// manually cutting the power, just to be conservative.
+		do {
+			EH_WAIT_UNTIL(!IS_EVENT_FROM_NCP(event));
+			EH_WAIT_UNTIL_WITH_TIMEOUT(0.5, IS_EVENT_FROM_NCP(event));
+		} while(!eh_did_timeout);
+
+		if (mInstance->set_ncp_power(false) == kWPANTUNDStatus_Ok) {
+			mInstance->change_ncp_state(DEEP_SLEEP);
+		} else {
+			syslog(LOG_ERR, "DeepSleep: set_ncp_power(false) failed.");
+
+			// Turning off the power manually didn't work for some reason.
+			// Turn it back on and we will try to do it via the API.
+			mInstance->set_ncp_power(true);
+		}
+	}
+
+	if (mInstance->get_ncp_state() != DEEP_SLEEP) {
+		syslog(LOG_NOTICE, "DeepSleep: Putting NCP to sleep.");
+
+		mNextCommand = SpinelPackData(
+			SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT8_S),
+			SPINEL_PROP_POWER_STATE,
+			SPINEL_POWER_STATE_DEEP_SLEEP
+		);
+		EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+		ret = mNextCommandRet;
+		require_noerr(ret, on_error);
+
+		mInstance->change_ncp_state(DEEP_SLEEP);
+	}
+
+on_error:
+
+	if (mInstance->get_ncp_state() == DEEP_SLEEP) {
+		syslog(LOG_NOTICE, "NCP is asleep.");
+		ret = kWPANTUNDStatus_Ok;
+	} else {
+		syslog(LOG_WARNING, "NCP DID NOT GO TO SLEEP!");
+		if (kWPANTUNDStatus_Ok == ret) {
+			ret = kWPANTUNDStatus_Failure;
+		}
+	}
+
+	finish(ret);
+
+	EH_END();
+}
diff --git a/src/ncp-spinel/SpinelNCPTaskDeepSleep.h b/src/ncp-spinel/SpinelNCPTaskDeepSleep.h
new file mode 100644
index 0000000..7c269c8
--- /dev/null
+++ b/src/ncp-spinel/SpinelNCPTaskDeepSleep.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __wpantund__SpinelNCPTaskDeepSleep__
+#define __wpantund__SpinelNCPTaskDeepSleep__
+
+#include "SpinelNCPTask.h"
+#include "SpinelNCPInstance.h"
+
+using namespace nl;
+using namespace nl::wpantund;
+
+namespace nl {
+namespace wpantund {
+
+class SpinelNCPTaskDeepSleep : public SpinelNCPTask
+{
+public:
+	SpinelNCPTaskDeepSleep(
+		SpinelNCPInstance* instance,
+		CallbackWithStatusArg1 cb
+	);
+	virtual int vprocess_event(int event, va_list args);
+	virtual void finish(int status, const boost::any& value = boost::any());
+};
+
+}; // namespace wpantund
+}; // namespace nl
+
+
+#endif /* defined(__wpantund__SpinelNCPTaskDeepSleep__) */
diff --git a/src/ncp-spinel/SpinelNCPTaskForm.cpp b/src/ncp-spinel/SpinelNCPTaskForm.cpp
new file mode 100644
index 0000000..f62d101
--- /dev/null
+++ b/src/ncp-spinel/SpinelNCPTaskForm.cpp
@@ -0,0 +1,258 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "assert-macros.h"
+#include <syslog.h>
+#include <errno.h>
+#include "SpinelNCPTaskForm.h"
+#include "SpinelNCPInstance.h"
+#include "any-to.h"
+#include "spinel-extra.h"
+
+using namespace nl;
+using namespace nl::wpantund;
+
+nl::wpantund::SpinelNCPTaskForm::SpinelNCPTaskForm(
+	SpinelNCPInstance* instance,
+	CallbackWithStatusArg1 cb,
+	const ValueMap& options
+):	SpinelNCPTask(instance, cb), mOptions(options), mLastState(instance->get_ncp_state())
+{
+}
+
+void
+nl::wpantund::SpinelNCPTaskForm::finish(int status, const boost::any& value)
+{
+	if (!ncp_state_is_associated(mInstance->get_ncp_state())) {
+		mInstance->change_ncp_state(mLastState);
+	}
+	SpinelNCPTask::finish(status, value);
+}
+
+int
+nl::wpantund::SpinelNCPTaskForm::vprocess_event(int event, va_list args)
+{
+	int ret = kWPANTUNDStatus_Failure;
+
+	EH_BEGIN();
+
+
+	if (!mInstance->mEnabled) {
+		ret = kWPANTUNDStatus_InvalidWhenDisabled;
+		finish(ret);
+		EH_EXIT();
+	}
+
+	if (mInstance->get_ncp_state() == UPGRADING) {
+		ret = kWPANTUNDStatus_InvalidForCurrentState;
+		finish(ret);
+		EH_EXIT();
+	}
+
+	// Wait for a bit to see if the NCP will enter the right state.
+	EH_REQUIRE_WITHIN(
+		NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+		!ncp_state_is_initializing(mInstance->get_ncp_state()),
+		on_error
+	);
+
+	if (ncp_state_is_associated(mInstance->get_ncp_state())) {
+		ret = kWPANTUNDStatus_Already;
+		finish(ret);
+		EH_EXIT();
+	}
+
+	if (!mInstance->mCapabilities.count(SPINEL_CAP_ROLE_ROUTER)) {
+		// We can't form unless we are router-capable
+		ret = kWPANTUNDStatus_FeatureNotSupported;
+		finish(ret);
+		EH_EXIT();
+	}
+
+	// The first event to a task is EVENT_STARTING_TASK. The following
+	// line makes sure that we don't start processing this task
+	// until it is properly scheduled. All tasks immediately receive
+	// the initial `EVENT_STARTING_TASK` event, but further events
+	// will only be received by that task once it is that task's turn
+	// to execute.
+	EH_WAIT_UNTIL(EVENT_STARTING_TASK != event);
+
+	mLastState = mInstance->get_ncp_state();
+	mInstance->change_ncp_state(ASSOCIATING);
+
+	if (mOptions.count(kWPANTUNDProperty_NetworkPANID)) {
+		mNextCommand = SpinelPackData(
+			SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT16_S),
+			SPINEL_PROP_MAC_15_4_PANID,
+			any_to_int(mOptions[kWPANTUNDProperty_NetworkPANID])
+		);
+
+		EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+
+		ret = mNextCommandRet;
+
+		require_noerr(ret, on_error);
+	}
+
+	if (mOptions.count(kWPANTUNDProperty_NetworkXPANID)) {
+		{
+			uint64_t xpanid(any_to_uint64((mOptions[kWPANTUNDProperty_NetworkXPANID])));
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+			reverse_bytes(reinterpret_cast<uint8_t*>(&xpanid), sizeof(xpanid));
+#endif
+
+			mNextCommand = SpinelPackData(
+				SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_DATA_S),
+				SPINEL_PROP_NET_XPANID,
+				&xpanid,
+				sizeof(xpanid)
+			);
+		}
+
+		EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+
+		ret = mNextCommandRet;
+
+		require_noerr(ret, on_error);
+	}
+
+	if (mOptions.count(kWPANTUNDProperty_NetworkName)) {
+		mNextCommand = SpinelPackData(
+			SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UTF8_S),
+			SPINEL_PROP_NET_NETWORK_NAME,
+			any_to_string(mOptions[kWPANTUNDProperty_NetworkName]).c_str()
+		);
+
+		EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+
+		ret = mNextCommandRet;
+
+		require_noerr(ret, on_error);
+	}
+
+	if (mOptions.count(kWPANTUNDProperty_NetworkKey)) {
+		{
+			nl::Data data(any_to_data(mOptions[kWPANTUNDProperty_NetworkKey]));
+			mNextCommand = SpinelPackData(
+				SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_DATA_S),
+				SPINEL_PROP_NET_MASTER_KEY,
+				data.data(),
+				data.size()
+			);
+		}
+
+		EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+
+		ret = mNextCommandRet;
+
+		require_noerr(ret, on_error);
+	}
+
+	if (mOptions.count(kWPANTUNDProperty_NetworkKeyIndex)) {
+		mNextCommand = SpinelPackData(
+			SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT32_S),
+			SPINEL_PROP_NET_KEY_SEQUENCE,
+			any_to_int(mOptions[kWPANTUNDProperty_NetworkKeyIndex])
+		);
+
+		EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+
+		ret = mNextCommandRet;
+
+		require_noerr(ret, on_error);
+	}
+
+	if (mOptions.count(kWPANTUNDProperty_IPv6MeshLocalAddress)) {
+		{
+			struct in6_addr addr = any_to_ipv6(mOptions[kWPANTUNDProperty_IPv6MeshLocalAddress]);
+			mNextCommand = SpinelPackData(
+				SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_IPv6ADDR_S SPINEL_DATATYPE_UINT8_S),
+				SPINEL_PROP_IPV6_ML_PREFIX,
+				&addr,
+				64
+			);
+		}
+
+		EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+
+		ret = mNextCommandRet;
+
+		require_noerr(ret, on_error);
+	} else if (mOptions.count(kWPANTUNDProperty_IPv6MeshLocalPrefix)) {
+		{
+			struct in6_addr addr = any_to_ipv6(mOptions[kWPANTUNDProperty_IPv6MeshLocalPrefix]);
+			mNextCommand = SpinelPackData(
+				SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_IPv6ADDR_S SPINEL_DATATYPE_UINT8_S),
+				SPINEL_PROP_IPV6_ML_PREFIX,
+				&addr,
+				64
+			);
+		}
+
+		EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+
+		ret = mNextCommandRet;
+
+		require_noerr(ret, on_error);
+	}
+
+	// Now we can try associating
+	mNextCommand = SpinelPackData(
+		SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT8_S),
+		SPINEL_PROP_NET_STATE,
+		SPINEL_NET_STATE_ATTACHED
+	);
+
+	EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+
+	ret = mNextCommandRet;
+
+	require_noerr(ret, on_error);
+
+	EH_REQUIRE_WITHIN(
+		NCP_FORM_TIMEOUT,
+		ncp_state_is_associated(mInstance->get_ncp_state()),
+		on_error
+	);
+
+	ret = kWPANTUNDStatus_Ok;
+
+	finish(ret);
+
+	EH_EXIT();
+
+on_error:
+
+	if (ret == kWPANTUNDStatus_Ok) {
+		ret = kWPANTUNDStatus_Failure;
+	}
+
+	syslog(LOG_ERR, "Form failed: %d", ret);
+
+	//mInstance->reinitialize_ncp();
+
+	finish(ret);
+
+	EH_END();
+}
diff --git a/src/ncp-spinel/SpinelNCPTaskForm.h b/src/ncp-spinel/SpinelNCPTaskForm.h
new file mode 100644
index 0000000..1fef8f4
--- /dev/null
+++ b/src/ncp-spinel/SpinelNCPTaskForm.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __wpantund__SpinelNCPTaskForm__
+#define __wpantund__SpinelNCPTaskForm__
+
+#include "SpinelNCPTask.h"
+#include "SpinelNCPInstance.h"
+
+using namespace nl;
+using namespace nl::wpantund;
+
+namespace nl {
+namespace wpantund {
+
+class SpinelNCPTaskForm : public SpinelNCPTask
+{
+public:
+	SpinelNCPTaskForm(
+		SpinelNCPInstance* instance,
+		CallbackWithStatusArg1 cb,
+		const ValueMap& options
+	);
+	virtual int vprocess_event(int event, va_list args);
+	virtual void finish(int status, const boost::any& value = boost::any());
+
+private:
+	ValueMap mOptions;
+	NCPState mLastState;
+};
+
+}; // namespace wpantund
+}; // namespace nl
+
+
+#endif /* defined(__wpantund__SpinelNCPTaskForm__) */
diff --git a/src/ncp-spinel/SpinelNCPTaskJoin.cpp b/src/ncp-spinel/SpinelNCPTaskJoin.cpp
new file mode 100644
index 0000000..b659294
--- /dev/null
+++ b/src/ncp-spinel/SpinelNCPTaskJoin.cpp
@@ -0,0 +1,253 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "assert-macros.h"
+#include <syslog.h>
+#include <errno.h>
+#include "SpinelNCPTaskJoin.h"
+#include "SpinelNCPInstance.h"
+#include "any-to.h"
+#include "spinel-extra.h"
+
+using namespace nl;
+using namespace nl::wpantund;
+
+nl::wpantund::SpinelNCPTaskJoin::SpinelNCPTaskJoin(
+	SpinelNCPInstance* instance,
+	CallbackWithStatusArg1 cb,
+	const ValueMap& options
+):	SpinelNCPTask(instance, cb), mOptions(options), mLastState(instance->get_ncp_state())
+{
+}
+
+void
+nl::wpantund::SpinelNCPTaskJoin::finish(int status, const boost::any& value)
+{
+	SpinelNCPTask::finish(status, value);
+	if (!ncp_state_is_associated(mInstance->get_ncp_state())) {
+		mInstance->change_ncp_state(mLastState);
+	}
+}
+
+
+int
+nl::wpantund::SpinelNCPTaskJoin::vprocess_event(int event, va_list args)
+{
+	int ret = kWPANTUNDStatus_Failure;
+
+	EH_BEGIN();
+
+
+	if (!mInstance->mEnabled) {
+		ret = kWPANTUNDStatus_InvalidWhenDisabled;
+		finish(ret);
+		EH_EXIT();
+	}
+
+	if (mInstance->get_ncp_state() == UPGRADING) {
+		ret = kWPANTUNDStatus_InvalidForCurrentState;
+		finish(ret);
+		EH_EXIT();
+	}
+
+	// Wait for a bit to see if the NCP will enter the right state.
+	EH_REQUIRE_WITHIN(
+		NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+		!ncp_state_is_initializing(mInstance->get_ncp_state()),
+		on_error
+	);
+
+	if (ncp_state_is_associated(mInstance->get_ncp_state())) {
+		ret = kWPANTUNDStatus_Already;
+		finish(ret);
+		EH_EXIT();
+	}
+
+	// The first event to a task is EVENT_STARTING_TASK. The following
+	// line makes sure that we don't start processing this task
+	// until it is properly scheduled. All tasks immediately receive
+	// the initial `EVENT_STARTING_TASK` event, but further events
+	// will only be received by that task once it is that task's turn
+	// to execute.
+	EH_WAIT_UNTIL(EVENT_STARTING_TASK != event);
+
+	mLastState = mInstance->get_ncp_state();
+	mInstance->change_ncp_state(ASSOCIATING);
+
+	if (mOptions.count(kWPANTUNDProperty_NetworkPANID)) {
+		mNextCommand = SpinelPackData(
+			SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT16_S),
+			SPINEL_PROP_MAC_15_4_PANID,
+			any_to_int(mOptions[kWPANTUNDProperty_NetworkPANID])
+		);
+
+		EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+
+		ret = mNextCommandRet;
+
+		require_noerr(ret, on_error);
+	}
+
+	if (mOptions.count(kWPANTUNDProperty_NetworkXPANID)) {
+		{
+			uint64_t xpanid(any_to_uint64((mOptions[kWPANTUNDProperty_NetworkXPANID])));
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+			reverse_bytes(reinterpret_cast<uint8_t*>(&xpanid), sizeof(xpanid));
+#endif
+
+			mNextCommand = SpinelPackData(
+				SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_DATA_S),
+				SPINEL_PROP_NET_XPANID,
+				&xpanid,
+				sizeof(xpanid)
+			);
+		}
+
+		EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+
+		ret = mNextCommandRet;
+
+		require_noerr(ret, on_error);
+	}
+
+	if (mOptions.count(kWPANTUNDProperty_NetworkName)) {
+		mNextCommand = SpinelPackData(
+			SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UTF8_S),
+			SPINEL_PROP_NET_NETWORK_NAME,
+			any_to_string(mOptions[kWPANTUNDProperty_NetworkName]).c_str()
+		);
+
+		EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+
+		ret = mNextCommandRet;
+
+		require_noerr(ret, on_error);
+	}
+
+	if (mOptions.count(kWPANTUNDProperty_NetworkKey)) {
+		{
+			nl::Data data(any_to_data(mOptions[kWPANTUNDProperty_NetworkKey]));
+			mNextCommand = SpinelPackData(
+				SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_DATA_S),
+				SPINEL_PROP_NET_MASTER_KEY,
+				data.data(),
+				data.size()
+			);
+		}
+
+		EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+
+		ret = mNextCommandRet;
+
+		require_noerr(ret, on_error);
+	}
+
+	if (mOptions.count(kWPANTUNDProperty_NetworkKeyIndex)) {
+		mNextCommand = SpinelPackData(
+			SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT32_S),
+			SPINEL_PROP_NET_KEY_SEQUENCE,
+			any_to_int(mOptions[kWPANTUNDProperty_NetworkKeyIndex])
+		);
+
+		EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+
+		ret = mNextCommandRet;
+
+		require_noerr(ret, on_error);
+	}
+
+	if (mOptions.count(kWPANTUNDProperty_IPv6MeshLocalAddress)) {
+		{
+			struct in6_addr addr = any_to_ipv6(mOptions[kWPANTUNDProperty_IPv6MeshLocalAddress]);
+			mNextCommand = SpinelPackData(
+				SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_IPv6ADDR_S SPINEL_DATATYPE_UINT8_S),
+				SPINEL_PROP_IPV6_ML_PREFIX,
+				&addr,
+				64
+			);
+		}
+
+		EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+
+		ret = mNextCommandRet;
+
+		require_noerr(ret, on_error);
+	} else if (mOptions.count(kWPANTUNDProperty_IPv6MeshLocalPrefix)) {
+		{
+			struct in6_addr addr = any_to_ipv6(mOptions[kWPANTUNDProperty_IPv6MeshLocalPrefix]);
+			mNextCommand = SpinelPackData(
+				SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_IPv6ADDR_S SPINEL_DATATYPE_UINT8_S),
+				SPINEL_PROP_IPV6_ML_PREFIX,
+				&addr,
+				64
+			);
+		}
+
+		EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+
+		ret = mNextCommandRet;
+
+		require_noerr(ret, on_error);
+	}
+
+
+	// Now we can try associating
+	mNextCommand = SpinelPackData(
+		SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT8_S),
+		SPINEL_PROP_NET_STATE,
+		SPINEL_NET_STATE_ATTACHED
+	);
+
+	EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+
+	ret = mNextCommandRet;
+
+	require_noerr(ret, on_error);
+
+	EH_REQUIRE_WITHIN(
+		NCP_JOIN_TIMEOUT,
+		ncp_state_is_associated(mInstance->get_ncp_state()),
+		on_error
+	);
+
+	ret = kWPANTUNDStatus_Ok;
+
+	finish(ret);
+
+	EH_EXIT();
+
+on_error:
+
+	if (ret == kWPANTUNDStatus_Ok) {
+		ret = kWPANTUNDStatus_Failure;
+	}
+
+	syslog(LOG_ERR, "Join failed: %d", ret);
+
+	//mInstance->reinitialize_ncp();
+
+	finish(ret);
+
+	EH_END();
+}
diff --git a/src/ncp-spinel/SpinelNCPTaskJoin.h b/src/ncp-spinel/SpinelNCPTaskJoin.h
new file mode 100644
index 0000000..e5534f4
--- /dev/null
+++ b/src/ncp-spinel/SpinelNCPTaskJoin.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __wpantund__SpinelNCPTaskJoin__
+#define __wpantund__SpinelNCPTaskJoin__
+
+#include "SpinelNCPTask.h"
+#include "SpinelNCPInstance.h"
+#include "ValueMap.h"
+
+using namespace nl;
+using namespace nl::wpantund;
+
+namespace nl {
+namespace wpantund {
+
+class SpinelNCPTaskJoin : public SpinelNCPTask
+{
+public:
+	SpinelNCPTaskJoin(
+		SpinelNCPInstance* instance,
+		CallbackWithStatusArg1 cb,
+		const ValueMap& options
+	);
+	virtual int vprocess_event(int event, va_list args);
+	virtual void finish(int status, const boost::any& value = boost::any());
+
+
+private:
+	ValueMap mOptions;
+	NCPState mLastState;
+};
+
+}; // namespace wpantund
+}; // namespace nl
+
+
+#endif /* defined(__wpantund__SpinelNCPTaskJoin__) */
diff --git a/src/ncp-spinel/SpinelNCPTaskLeave.cpp b/src/ncp-spinel/SpinelNCPTaskLeave.cpp
new file mode 100644
index 0000000..da02cda
--- /dev/null
+++ b/src/ncp-spinel/SpinelNCPTaskLeave.cpp
@@ -0,0 +1,92 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "assert-macros.h"
+#include <syslog.h>
+#include <errno.h>
+#include "SpinelNCPTaskLeave.h"
+#include "SpinelNCPInstance.h"
+#include "spinel-extra.h"
+
+using namespace nl;
+using namespace nl::wpantund;
+
+nl::wpantund::SpinelNCPTaskLeave::SpinelNCPTaskLeave(
+	SpinelNCPInstance* instance,
+	CallbackWithStatusArg1 cb
+):	SpinelNCPTask(instance, cb)
+{
+}
+
+int
+nl::wpantund::SpinelNCPTaskLeave::vprocess_event(int event, va_list args)
+{
+	int ret = kWPANTUNDStatus_Failure;
+
+	EH_BEGIN();
+
+	// The first event to a task is EVENT_STARTING_TASK. The following
+	// line makes sure that we don't start processing this task
+	// until it is properly scheduled. All tasks immediately receive
+	// the initial `EVENT_STARTING_TASK` event, but further events
+	// will only be received by that task once it is that task's turn
+	// to execute.
+	EH_WAIT_UNTIL(EVENT_STARTING_TASK != event);
+
+	mNextCommand = SpinelPackData(
+		SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT8_S),
+		SPINEL_PROP_NET_STATE,
+		SPINEL_NET_STATE_OFFLINE
+	);
+	EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+	ret = mNextCommandRet;
+	require_noerr(ret, on_error);
+
+
+	if (mInstance->mCapabilities.count(SPINEL_CAP_NET_SAVE)) {
+		mNextCommand = SpinelPackData(SPINEL_FRAME_PACK_CMD_NET_CLEAR);
+		EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+		ret = mNextCommandRet;
+		require_noerr(ret, on_error);
+	}
+
+	ret = kWPANTUNDStatus_Ok;
+
+	finish(ret);
+
+	EH_EXIT();
+
+on_error:
+
+	if (ret == kWPANTUNDStatus_Ok) {
+		ret = kWPANTUNDStatus_Failure;
+	}
+
+	syslog(LOG_ERR, "Leave failed: %d", ret);
+
+	mInstance->reinitialize_ncp();
+
+	finish(ret);
+
+	EH_END();
+}
diff --git a/src/ncp-spinel/SpinelNCPTaskLeave.h b/src/ncp-spinel/SpinelNCPTaskLeave.h
new file mode 100644
index 0000000..964b373
--- /dev/null
+++ b/src/ncp-spinel/SpinelNCPTaskLeave.h
@@ -0,0 +1,46 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __wpantund__SpinelNCPTaskLeave__
+#define __wpantund__SpinelNCPTaskLeave__
+
+#include "SpinelNCPTask.h"
+#include "SpinelNCPInstance.h"
+
+using namespace nl;
+using namespace nl::wpantund;
+
+namespace nl {
+namespace wpantund {
+
+class SpinelNCPTaskLeave : public SpinelNCPTask
+{
+public:
+	SpinelNCPTaskLeave(
+		SpinelNCPInstance* instance,
+		CallbackWithStatusArg1 cb
+	);
+	virtual int vprocess_event(int event, va_list args);
+};
+
+}; // namespace wpantund
+}; // namespace nl
+
+
+#endif /* defined(__wpantund__SpinelNCPTaskLeave__) */
diff --git a/src/ncp-spinel/SpinelNCPTaskScan.cpp b/src/ncp-spinel/SpinelNCPTaskScan.cpp
new file mode 100644
index 0000000..6f65334
--- /dev/null
+++ b/src/ncp-spinel/SpinelNCPTaskScan.cpp
@@ -0,0 +1,222 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "assert-macros.h"
+#include <syslog.h>
+#include <errno.h>
+#include "SpinelNCPTaskScan.h"
+#include "SpinelNCPInstance.h"
+#include "spinel-extra.h"
+
+using namespace nl;
+using namespace nl::wpantund;
+
+nl::wpantund::SpinelNCPTaskScan::SpinelNCPTaskScan(
+	SpinelNCPInstance* instance,
+	CallbackWithStatusArg1 cb,
+	uint32_t channel_mask
+):	SpinelNCPTask(instance, cb), mChannelMaskLen(0), mChannelDelayPeriod(200)
+{
+	uint8_t i;
+
+	for (i = 0; i < 32; i++) {
+		if (channel_mask & (1<<i)) {
+			mChannelMaskData[mChannelMaskLen++] = i;
+		}
+	}
+}
+
+void
+nl::wpantund::SpinelNCPTaskScan::finish(int status, const boost::any& value)
+{
+	SpinelNCPTask::finish(status, value);
+}
+
+
+int
+nl::wpantund::SpinelNCPTaskScan::vprocess_event(int event, va_list args)
+{
+	int ret = 0;
+
+	EH_BEGIN();
+
+
+	if (!mInstance->mEnabled) {
+		ret = kWPANTUNDStatus_InvalidWhenDisabled;
+		finish(ret);
+		EH_EXIT();
+	}
+
+	if (mInstance->get_ncp_state() == UPGRADING) {
+		ret = kWPANTUNDStatus_InvalidForCurrentState;
+		finish(ret);
+		EH_EXIT();
+	}
+
+	// Wait for a bit to see if the NCP will enter the right state.
+	EH_REQUIRE_WITHIN(
+		NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+		!ncp_state_is_initializing(mInstance->get_ncp_state())
+		&& (mInstance->get_ncp_state() != ASSOCIATING)
+		&& (mInstance->get_ncp_state() != CREDENTIALS_NEEDED),
+		on_error
+	);
+
+	// The first event to a task is EVENT_STARTING_TASK. The following
+	// line makes sure that we don't start processing this task
+	// until it is properly scheduled. All tasks immediately receive
+	// the initial `EVENT_STARTING_TASK` event, but further events
+	// will only be received by that task once it is that task's turn
+	// to execute.
+	EH_WAIT_UNTIL(EVENT_STARTING_TASK != event);
+
+
+	// Set channel mask
+	mNextCommand = SpinelPackData(
+		SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_DATA_S),
+		SPINEL_PROP_MAC_SCAN_MASK,
+		mChannelMaskData,
+		mChannelMaskLen
+	);
+	EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+	ret = mNextCommandRet;
+	require_noerr(ret, on_error);
+
+
+	// Set delay period
+	mNextCommand = SpinelPackData(
+		SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT16_S),
+		SPINEL_PROP_MAC_SCAN_PERIOD,
+		mChannelDelayPeriod
+	);
+	EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+	ret = mNextCommandRet;
+	require_noerr(ret, on_error);
+
+
+	// Start the scan.
+	mNextCommand = SpinelPackData(
+		SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT8_S),
+		SPINEL_PROP_MAC_SCAN_STATE,
+		SPINEL_SCAN_STATE_BEACON
+	);
+	EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+	ret = mNextCommandRet;
+	require_noerr(ret, on_error);
+
+
+	do {
+		EH_REQUIRE_WITHIN(
+			15,
+			event == EVENT_NCP_PROP_VALUE_IS
+			|| event == EVENT_NCP_PROP_VALUE_INSERTED,
+			on_error
+		);
+
+		spinel_prop_key_t prop_key = va_arg_small(args, spinel_prop_key_t);
+		const uint8_t* data_ptr = va_arg(args, const uint8_t*);
+		spinel_size_t data_len = va_arg(args, spinel_size_t);
+
+		if (prop_key == SPINEL_PROP_MAC_SCAN_BEACON) {
+			const spinel_eui64_t* laddr = NULL;
+			const char* networkid = "";
+			const uint8_t* xpanid = NULL;
+			unsigned int xpanid_len = 0;
+			unsigned int proto = 0;
+			uint16_t panid = 0xFFFF;
+			uint16_t saddr = 0xFFFF;
+			uint8_t chan = 0;
+			uint8_t lqi = 0x00;
+			int8_t rssi = 0x00;
+			uint8_t flags = 0x00;
+
+			syslog(LOG_DEBUG, "Got a beacon");
+
+			spinel_datatype_unpack(
+				data_ptr,
+				data_len,
+				"CcT(ESSC.)T(iCUD.).",
+				&chan,
+				&rssi,
+
+				&laddr,
+				&saddr,
+				&panid,
+				&lqi,
+
+				&proto,
+				&flags,
+				&networkid,
+				&xpanid, &xpanid_len
+			);
+
+			if ((xpanid_len != 8) && (xpanid_len != 0)) {
+				break;
+			}
+
+			WPAN::NetworkInstance network(
+				networkid,
+				xpanid,
+				panid,
+				chan,
+				(flags & SPINEL_BEACON_THREAD_FLAG_JOINABLE)
+			);
+			network.rssi = rssi;
+			network.type = proto;
+			network.lqi = lqi;
+			network.saddr = saddr;
+
+			if (laddr) {
+				memcpy(network.hwaddr, laddr, sizeof(network.hwaddr));
+			}
+
+			mInstance->get_control_interface().mOnNetScanBeacon(network);
+
+		} else if (prop_key == SPINEL_PROP_MAC_SCAN_STATE) {
+			int scan_state;
+			spinel_datatype_unpack(data_ptr, data_len, "i", &scan_state);
+			if (scan_state == SPINEL_SCAN_STATE_IDLE) {
+				break;
+			}
+		}
+
+		// Change the event type to 'IDLE' so that we
+		// don't try to process this event once than once.
+		event = EVENT_IDLE;
+	} while(true);
+
+	finish(ret);
+
+	EH_EXIT();
+
+on_error:
+	if (ret == kWPANTUNDStatus_Ok) {
+		ret = kWPANTUNDStatus_Failure;
+	}
+
+	syslog(LOG_ERR, "Scan failed: %d", ret);
+
+	finish(ret);
+
+	EH_END();
+}
diff --git a/src/ncp-spinel/SpinelNCPTaskScan.h b/src/ncp-spinel/SpinelNCPTaskScan.h
new file mode 100644
index 0000000..05fb48e
--- /dev/null
+++ b/src/ncp-spinel/SpinelNCPTaskScan.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __wpantund__SpinelNCPTaskScan__
+#define __wpantund__SpinelNCPTaskScan__
+
+#include "SpinelNCPTask.h"
+#include "SpinelNCPInstance.h"
+
+using namespace nl;
+using namespace nl::wpantund;
+
+namespace nl {
+namespace wpantund {
+
+class SpinelNCPTaskScan : public SpinelNCPTask
+{
+public:
+	SpinelNCPTaskScan(
+		SpinelNCPInstance* instance,
+		CallbackWithStatusArg1 cb,
+		uint32_t channel_mask
+	);
+	virtual int vprocess_event(int event, va_list args);
+	virtual void finish(int status, const boost::any& value = boost::any());
+
+private:
+	uint8_t mChannelMaskData[32];
+	uint8_t mChannelMaskLen;
+	uint16_t mChannelDelayPeriod;
+};
+
+}; // namespace wpantund
+}; // namespace nl
+
+
+#endif /* defined(__wpantund__SpinelNCPTaskScan__) */
diff --git a/src/ncp-spinel/SpinelNCPTaskSendCommand.cpp b/src/ncp-spinel/SpinelNCPTaskSendCommand.cpp
new file mode 100644
index 0000000..2020214
--- /dev/null
+++ b/src/ncp-spinel/SpinelNCPTaskSendCommand.cpp
@@ -0,0 +1,251 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "assert-macros.h"
+#include <syslog.h>
+#include <errno.h>
+#include "SpinelNCPTaskSendCommand.h"
+#include "SpinelNCPInstance.h"
+#include "spinel-extra.h"
+
+using namespace nl;
+using namespace nl::wpantund;
+
+
+nl::wpantund::SpinelNCPTaskSendCommand::SpinelNCPTaskSendCommand(
+	SpinelNCPInstance* instance,
+	CallbackWithStatusArg1 cb,
+	const Data& send_command,
+	int timeout,
+	const std::string& packed_format
+):	SpinelNCPTask(instance, cb), mPackedFormat(packed_format)
+{
+	mNextCommandTimeout = timeout;
+	mNextCommand = send_command;
+}
+
+static boost::any
+spinel_iter_to_any(spinel_datatype_iter_t *iter)
+{
+	boost::any ret;
+	spinel_status_t status;
+
+	switch(iter->pack_format[0]) {
+	case SPINEL_DATATYPE_BOOL_C:
+		{
+			bool val(0);
+			status = spinel_datatype_iter_unpack(iter, &val);
+			require_noerr(status, bail);
+			ret = val;
+		}
+		break;
+
+	case SPINEL_DATATYPE_UINT8_C:
+		{
+			uint8_t val(0);
+			status = spinel_datatype_iter_unpack(iter, &val);
+			require_noerr(status, bail);
+			ret = val;
+		}
+		break;
+
+	case SPINEL_DATATYPE_INT8_C:
+		{
+			int8_t val(0);
+			status = spinel_datatype_iter_unpack(iter, &val);
+			require_noerr(status, bail);
+			ret = (int)val;
+		}
+		break;
+
+	case SPINEL_DATATYPE_UINT16_C:
+		{
+			uint16_t val(0);
+			status = spinel_datatype_iter_unpack(iter, &val);
+			require_noerr(status, bail);
+			ret = val;
+		}
+		break;
+
+	case SPINEL_DATATYPE_INT16_C:
+		{
+			int16_t val(0);
+			status = spinel_datatype_iter_unpack(iter, &val);
+			require_noerr(status, bail);
+			ret = val;
+		}
+		break;
+
+	case SPINEL_DATATYPE_UINT32_C:
+		{
+			uint32_t val(0);
+			status = spinel_datatype_iter_unpack(iter, &val);
+			require_noerr(status, bail);
+			ret = val;
+		}
+		break;
+
+	case SPINEL_DATATYPE_INT32_C:
+		{
+			int32_t val(0);
+			status = spinel_datatype_iter_unpack(iter, &val);
+			require_noerr(status, bail);
+			ret = val;
+		}
+		break;
+
+	case SPINEL_DATATYPE_UINT_PACKED_C:
+		{
+			unsigned int val(0);
+			status = spinel_datatype_iter_unpack(iter, &val);
+			require_noerr(status, bail);
+			ret = val;
+		}
+		break;
+
+	case SPINEL_DATATYPE_IPv6ADDR_C:
+		{
+			const spinel_ipv6addr_t *val(NULL);
+			status = spinel_datatype_iter_unpack(iter, &val);
+			require_noerr(status, bail);
+			ret = in6_addr_to_string(*val);
+		}
+		break;
+
+	case SPINEL_DATATYPE_EUI64_C:
+		{
+			const spinel_eui64_t *val(NULL);
+			status = spinel_datatype_iter_unpack(iter, &val);
+			require_noerr(status, bail);
+			ret = Data(val->bytes, sizeof(val->bytes));
+		}
+		break;
+
+	case SPINEL_DATATYPE_EUI48_C:
+		{
+			const spinel_eui48_t *val(NULL);
+			status = spinel_datatype_iter_unpack(iter, &val);
+			require_noerr(status, bail);
+			ret = Data(val->bytes, sizeof(val->bytes));
+		}
+		break;
+
+	case SPINEL_DATATYPE_DATA_C:
+		{
+			const uint8_t *val_ptr(NULL);
+			spinel_size_t val_len;
+			status = spinel_datatype_iter_unpack(iter, &val_ptr, &val_len);
+			require_noerr(status, bail);
+			ret = Data(val_ptr, val_len);
+		}
+		break;
+
+	case SPINEL_DATATYPE_UTF8_C:
+		{
+			const char *val(NULL);
+			status = spinel_datatype_iter_unpack(iter, &val);
+			require_noerr(status, bail);
+			ret = std::string(val);
+		}
+		break;
+
+	case SPINEL_DATATYPE_STRUCT_C:
+		goto bail;
+
+	case SPINEL_DATATYPE_ARRAY_C:
+		// TODO: Recursively parse this
+		goto bail;
+
+	default:
+		goto bail;
+
+	}
+
+bail:
+	return ret;
+}
+
+static boost::any
+spinel_packed_to_any(const uint8_t* data_in, spinel_size_t data_len, const char* pack_format)
+{
+	spinel_datatype_iter_t spinel_iter = {};
+	spinel_datatype_iter_start(&spinel_iter, data_in, data_len, pack_format);
+
+	return spinel_iter_to_any(&spinel_iter);
+}
+
+
+int
+nl::wpantund::SpinelNCPTaskSendCommand::vprocess_event(int event, va_list args)
+{
+	int ret = kWPANTUNDStatus_Failure;
+
+	EH_BEGIN();
+
+	// The first event to a task is EVENT_STARTING_TASK. The following
+	// line makes sure that we don't start processing this task
+	// until it is properly scheduled. All tasks immediately receive
+	// the initial `EVENT_STARTING_TASK` event, but further events
+	// will only be received by that task once it is that task's turn
+	// to execute.
+	EH_WAIT_UNTIL(EVENT_STARTING_TASK != event);
+
+	require(mNextCommand.size() < sizeof(GetInstance(this)->mOutboundBuffer), on_error);
+
+	EH_SPAWN(&mSubPT, vprocess_send_command(event, args));
+	ret = mNextCommandRet;
+
+	if (mPackedFormat.size()) {
+		require_noerr(ret, on_error);
+
+		require(EVENT_NCP_PROP_VALUE_IS == event, on_error);
+
+		unsigned int key = va_arg(args, unsigned int);
+		const uint8_t* data_in = va_arg(args, const uint8_t*);
+		spinel_size_t data_len = va_arg_small(args, spinel_size_t);
+
+		(void) key;
+
+		ret = kWPANTUNDStatus_Ok;
+
+		finish(ret, spinel_packed_to_any(data_in, data_len, mPackedFormat.c_str()));
+
+		EH_EXIT();
+	}
+
+	finish(ret);
+
+	EH_EXIT();
+
+on_error:
+
+	if (ret == kWPANTUNDStatus_Ok) {
+		ret = kWPANTUNDStatus_Failure;
+	}
+
+	syslog(LOG_ERR, "SendCommand failed: %d", ret);
+
+	finish(ret);
+
+	EH_END();
+}
diff --git a/src/ncp-spinel/SpinelNCPTaskSendCommand.h b/src/ncp-spinel/SpinelNCPTaskSendCommand.h
new file mode 100644
index 0000000..fb37ed5
--- /dev/null
+++ b/src/ncp-spinel/SpinelNCPTaskSendCommand.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __wpantund__SpinelNCPTaskSendCommand__
+#define __wpantund__SpinelNCPTaskSendCommand__
+
+#include "SpinelNCPTask.h"
+#include "SpinelNCPInstance.h"
+
+using namespace nl;
+using namespace nl::wpantund;
+
+namespace nl {
+namespace wpantund {
+
+class SpinelNCPTaskSendCommand : public SpinelNCPTask
+{
+public:
+	SpinelNCPTaskSendCommand(SpinelNCPInstance* instance,
+		CallbackWithStatusArg1 cb,
+		const Data& send_command,
+		int timeout = NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+		const std::string& packed_format = SPINEL_DATATYPE_NULL_S
+	);
+
+	virtual int vprocess_event(int event, va_list args);
+
+private:
+	int mTimeout;
+	std::string mPackedFormat;
+};
+
+}; // namespace wpantund
+}; // namespace nl
+
+
+#endif /* defined(__wpantund__SpinelNCPTaskSendCommand__) */
diff --git a/src/ncp-spinel/SpinelNCPTaskWake.cpp b/src/ncp-spinel/SpinelNCPTaskWake.cpp
new file mode 100644
index 0000000..8e20672
--- /dev/null
+++ b/src/ncp-spinel/SpinelNCPTaskWake.cpp
@@ -0,0 +1,97 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "assert-macros.h"
+#include <syslog.h>
+#include <errno.h>
+#include "SpinelNCPTaskWake.h"
+#include "SpinelNCPInstance.h"
+
+using namespace nl;
+using namespace nl::wpantund;
+
+nl::wpantund::SpinelNCPTaskWake::SpinelNCPTaskWake(
+	SpinelNCPInstance* instance,
+	CallbackWithStatusArg1 cb
+):	SpinelNCPTask(instance, cb)
+{
+}
+
+void
+nl::wpantund::SpinelNCPTaskWake::finish(int status, const boost::any& value)
+{
+	mInstance->mResetIsExpected = false;
+
+	SpinelNCPTask::finish(status, value);
+}
+
+
+int
+nl::wpantund::SpinelNCPTaskWake::vprocess_event(int event, va_list args)
+{
+	int ret = kWPANTUNDStatus_Failure;
+
+	EH_BEGIN();
+
+	// The first event to a task is EVENT_STARTING_TASK. The following
+	// line makes sure that we don't start processing this task
+	// until it is properly scheduled. All tasks immediately receive
+	// the initial `EVENT_STARTING_TASK` event, but further events
+	// will only be received by that task once it is that task's turn
+	// to execute.
+	EH_WAIT_UNTIL(EVENT_STARTING_TASK != event);
+
+	mInstance->set_ncp_power(true);
+	mInstance->mResetIsExpected = true;
+
+	CONTROL_REQUIRE_PREP_TO_SEND_COMMAND_WITHIN(NCP_DEFAULT_COMMAND_SEND_TIMEOUT, on_error);
+	GetInstance(this)->mOutboundBufferLen = spinel_datatype_pack(GetInstance(this)->mOutboundBuffer, sizeof(GetInstance(this)->mOutboundBuffer), "Ci", 0, SPINEL_CMD_NOOP);
+	CONTROL_REQUIRE_OUTBOUND_BUFFER_FLUSHED_WITHIN(NCP_DEFAULT_COMMAND_SEND_TIMEOUT, on_error);
+
+	EH_REQUIRE_WITHIN(
+		NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT,
+		!ncp_state_is_sleeping(mInstance->get_ncp_state())
+		  && (!ncp_state_is_initializing(mInstance->get_ncp_state())),
+		on_error
+	);
+
+	ret = kWPANTUNDStatus_Ok;
+
+	finish(ret);
+
+	EH_EXIT();
+
+on_error:
+
+	if (ret == kWPANTUNDStatus_Ok) {
+		ret = kWPANTUNDStatus_Failure;
+	}
+
+	syslog(LOG_ERR, "Wake failed: %d", ret);
+
+	mInstance->reinitialize_ncp();
+
+	finish(ret);
+
+	EH_END();
+}
diff --git a/src/ncp-spinel/SpinelNCPTaskWake.h b/src/ncp-spinel/SpinelNCPTaskWake.h
new file mode 100644
index 0000000..52bd07f
--- /dev/null
+++ b/src/ncp-spinel/SpinelNCPTaskWake.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __wpantund__SpinelNCPTaskWake__
+#define __wpantund__SpinelNCPTaskWake__
+
+#include "SpinelNCPTask.h"
+#include "SpinelNCPInstance.h"
+
+using namespace nl;
+using namespace nl::wpantund;
+
+namespace nl {
+namespace wpantund {
+
+class SpinelNCPTaskWake : public SpinelNCPTask
+{
+public:
+	SpinelNCPTaskWake(
+		SpinelNCPInstance* instance,
+		CallbackWithStatusArg1 cb
+	);
+	virtual int vprocess_event(int event, va_list args);
+	virtual void finish(int status, const boost::any& value = boost::any());
+};
+
+}; // namespace wpantund
+}; // namespace nl
+
+
+#endif /* defined(__wpantund__SpinelNCPTaskWake__) */
diff --git a/src/ncp-spinel/spinel-extra.c b/src/ncp-spinel/spinel-extra.c
new file mode 100644
index 0000000..dbef2cf
--- /dev/null
+++ b/src/ncp-spinel/spinel-extra.c
@@ -0,0 +1,379 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// ----------------------------------------------------------------------------
+// MARK: -
+// MARK: Headers
+
+#include "spinel-extra.h"
+
+#include <assert.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <stdlib.h>
+
+// ----------------------------------------------------------------------------
+// MARK: -
+
+#ifndef assert_printf
+#define assert_printf(fmt, ...) \
+fprintf(stderr, \
+        __FILE__ ":%d: " fmt "\n", \
+        __LINE__, \
+        __VA_ARGS__)
+#endif
+
+#ifndef require_action
+#define require_action(c, l, a) \
+    do { if (!(c)) { \
+        assert_printf("Requirement Failed (%s)", # c); \
+        a; \
+        goto l; \
+    } } while (0)
+#endif
+
+#ifndef require
+#define require(c, l)   require_action(c, l, {})
+#endif
+
+// ----------------------------------------------------------------------------
+// MARK: -
+// MARK: Datatype Iterator
+
+spinel_status_t
+spinel_datatype_iter_start(spinel_datatype_iter_t* iter, const uint8_t* data_in, spinel_size_t data_len, const char* pack_format)
+{
+	iter->data_ptr = data_in;
+	iter->data_len = data_len;
+	iter->pack_format = pack_format;
+
+	return SPINEL_STATUS_OK;
+}
+
+spinel_status_t
+spinel_datatype_iter_next(spinel_datatype_iter_t* iter)
+{
+	spinel_status_t ret = SPINEL_STATUS_PARSE_ERROR;
+	spinel_datatype_iter_t scratchpad = *iter;
+
+	if (!iter->data_ptr || !iter->data_len || !iter->pack_format) {
+		ret = SPINEL_STATUS_EMPTY;
+		goto bail;
+	}
+
+	switch ((spinel_datatype_t)*scratchpad.pack_format) {
+	case SPINEL_DATATYPE_INT8_C:
+	case SPINEL_DATATYPE_UINT8_C:
+		require(scratchpad.data_len >= sizeof(uint8_t), bail);
+		scratchpad.data_ptr += sizeof(uint8_t);
+		scratchpad.data_len -= sizeof(uint8_t);
+		scratchpad.pack_format++;
+		break;
+
+	case SPINEL_DATATYPE_INT16_C:
+	case SPINEL_DATATYPE_UINT16_C:
+		require(scratchpad.data_len >= sizeof(uint16_t), bail);
+		scratchpad.data_ptr += sizeof(uint16_t);
+		scratchpad.data_len -= sizeof(uint16_t);
+		scratchpad.pack_format++;
+		break;
+
+	case SPINEL_DATATYPE_INT32_C:
+	case SPINEL_DATATYPE_UINT32_C:
+		require(scratchpad.data_len >= sizeof(uint32_t), bail);
+		scratchpad.data_ptr += sizeof(uint32_t);
+		scratchpad.data_len -= sizeof(uint32_t);
+		scratchpad.pack_format++;
+		break;
+
+	case SPINEL_DATATYPE_IPv6ADDR_C:
+		require(scratchpad.data_len >= sizeof(spinel_ipv6addr_t), bail);
+		scratchpad.data_ptr += sizeof(spinel_ipv6addr_t);
+		scratchpad.data_len -= sizeof(spinel_ipv6addr_t);
+		scratchpad.pack_format++;
+		break;
+
+	case SPINEL_DATATYPE_EUI64_C:
+		require(scratchpad.data_len >= sizeof(spinel_eui64_t), bail);
+		scratchpad.data_ptr += sizeof(spinel_eui64_t);
+		scratchpad.data_len -= sizeof(spinel_eui64_t);
+		scratchpad.pack_format++;
+		break;
+
+	case SPINEL_DATATYPE_EUI48_C:
+		require(scratchpad.data_len >= sizeof(spinel_eui48_t), bail);
+		scratchpad.data_ptr += sizeof(spinel_eui48_t);
+		scratchpad.data_len -= sizeof(spinel_eui48_t);
+		scratchpad.pack_format++;
+		break;
+
+	case SPINEL_DATATYPE_UINT_PACKED_C:
+		{
+			spinel_ssize_t pui_len = spinel_packed_uint_decode(scratchpad.data_ptr, scratchpad.data_len, NULL);
+			require(pui_len > 0, bail);
+			scratchpad.data_ptr += pui_len;
+			scratchpad.data_len -= pui_len;
+			scratchpad.pack_format++;
+		}
+		break;
+
+
+	case SPINEL_DATATYPE_UTF8_C:
+		break;
+
+	case SPINEL_DATATYPE_STRUCT_C:
+	case SPINEL_DATATYPE_ARRAY_C:
+		scratchpad.pack_format = spinel_next_packed_datatype(scratchpad.pack_format)-1;
+
+	case SPINEL_DATATYPE_DATA_C:
+		{
+			if ((scratchpad.pack_format[1] == ')')
+			 || (scratchpad.pack_format[1] == 0)
+			) {
+				// Special case: data is size of the rest of the buffer!
+				scratchpad.data_ptr += scratchpad.data_len;
+				scratchpad.data_len -= scratchpad.data_len;
+			} else {
+				uint32_t block_len = 0;
+				spinel_ssize_t pui_len = spinel_packed_uint_decode(scratchpad.data_ptr, scratchpad.data_len, &block_len);
+
+				require(pui_len > 0, bail);
+				require(block_len < SPINEL_FRAME_MAX_SIZE, bail);
+
+				scratchpad.data_ptr += pui_len+block_len;
+				scratchpad.data_len -= pui_len+block_len;
+
+				require(scratchpad.data_len >= block_len, bail);
+			}
+		}
+	case SPINEL_DATATYPE_VOID_C:
+		scratchpad.pack_format++;
+
+	default:
+		// Unsupported Type!
+		goto bail;
+	}
+
+	while (scratchpad.pack_format[0] == SPINEL_DATATYPE_VOID_C) {
+		scratchpad.pack_format++;
+	}
+
+	if ((*scratchpad.pack_format == ')')
+	 || (*scratchpad.pack_format == 0)
+	 || (scratchpad.data_len == 0)
+	) {
+		ret = SPINEL_STATUS_EMPTY;
+	} else {
+		ret = SPINEL_STATUS_OK;
+	}
+
+	if (iter->container == SPINEL_DATATYPE_ARRAY_C) {
+		iter->data_ptr = scratchpad.data_ptr;
+		iter->data_len = scratchpad.data_len;
+	} else {
+		*iter = scratchpad;
+	}
+
+bail:
+	return ret;
+}
+
+spinel_status_t
+spinel_datatype_iter_open_container(const spinel_datatype_iter_t* iter, spinel_datatype_iter_t* subiter)
+{
+	spinel_status_t ret = SPINEL_STATUS_PARSE_ERROR;
+	int depth = 0;
+
+	require(iter->data_len > 2, bail);
+
+	switch ((spinel_datatype_t)*iter->pack_format) {
+	case SPINEL_DATATYPE_STRUCT_C:
+	case SPINEL_DATATYPE_ARRAY_C:
+		break;
+
+	default:
+		ret = SPINEL_STATUS_INVALID_ARGUMENT;
+		goto bail;
+		break;
+	}
+
+	*subiter = *iter;
+	subiter->container = iter->pack_format[0];
+
+	do {
+		switch(subiter->pack_format[1]) {
+		case '(': depth++; break;
+		case ')': depth++; break;
+		case 0: depth = 0; break;
+		}
+	} while(depth > 0);
+
+	require(subiter->pack_format[1] == ')', bail);
+	subiter->pack_format++;
+
+	if ((subiter->pack_format[1] != ')') && (subiter->pack_format[1] != 0))
+	{
+		// We aren't the special case. Extract the length.
+		uint32_t block_len = 0;
+		spinel_ssize_t pui_len = spinel_packed_uint_decode(subiter->data_ptr, subiter->data_len, &block_len);
+
+		require(pui_len > 0, bail);
+		require(block_len < SPINEL_FRAME_MAX_SIZE, bail);
+
+		subiter->data_ptr += pui_len;
+		subiter->data_len -= pui_len;
+
+		require(block_len <= subiter->data_len, bail);
+
+		subiter->data_len = block_len;
+	}
+	subiter->pack_format = iter->pack_format+2;
+
+	ret = SPINEL_STATUS_OK;
+
+bail:
+	return ret;
+}
+
+spinel_status_t
+spinel_datatype_iter_unpack(const spinel_datatype_iter_t* iter, ...)
+{
+	spinel_status_t ret = SPINEL_STATUS_PARSE_ERROR;
+	va_list args;
+	va_start(args, iter);
+	const char pack_format[2] = { iter->pack_format[0], 0 };
+
+	if (0 <= spinel_datatype_vunpack(iter->data_ptr, iter->data_len, pack_format, args)) {
+		ret = SPINEL_STATUS_OK;
+	}
+
+	va_end(args);
+	return ret;
+}
+
+spinel_status_t
+spinel_datatype_iter_vunpack(const spinel_datatype_iter_t* iter, va_list args)
+{
+	spinel_status_t ret = SPINEL_STATUS_PARSE_ERROR;
+
+	if (0 <= spinel_datatype_vunpack(iter->data_ptr, iter->data_len, iter->pack_format, args)) {
+		ret = SPINEL_STATUS_OK;
+	}
+
+	return ret;
+}
+
+spinel_datatype_t
+spinel_datatype_iter_get_type(const spinel_datatype_iter_t* iter)
+{
+	return iter->pack_format != NULL
+		? (spinel_datatype_t)*iter->pack_format
+		: SPINEL_DATATYPE_NULL_C;
+}
+
+// ----------------------------------------------------------------------------
+// MARK: -
+// MARK: Command Generators
+
+spinel_ssize_t
+spinel_cmd_prop_value_set_uint(uint8_t* cmd_data_ptr, spinel_size_t cmd_data_len, spinel_prop_key_t prop_key, unsigned int x)
+{
+    return spinel_datatype_pack(
+        cmd_data_ptr,
+        cmd_data_len,
+        SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT_PACKED_S),
+        prop_key,
+        x
+    );
+}
+
+spinel_ssize_t
+spinel_cmd_prop_value_set_data(uint8_t* cmd_data_ptr, spinel_size_t cmd_data_len, spinel_prop_key_t prop_key, const uint8_t* x_ptr, spinel_size_t x_len)
+{
+    return spinel_datatype_pack(
+        cmd_data_ptr,
+        cmd_data_len,
+        SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_DATA_S),
+        prop_key,
+        x_ptr,
+        x_len
+    );
+}
+
+spinel_ssize_t
+spinel_cmd_prop_value_set_utf8(uint8_t* cmd_data_ptr, spinel_size_t cmd_data_len, spinel_prop_key_t prop_key, const char* x)
+{
+    return spinel_datatype_pack(
+        cmd_data_ptr,
+        cmd_data_len,
+        SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_DATA_S),
+        prop_key,
+        x
+    );
+}
+
+spinel_ssize_t
+spinel_cmd_prop_value_set_uint16(uint8_t* cmd_data_ptr, spinel_size_t cmd_data_len, spinel_prop_key_t prop_key, uint16_t x)
+{
+    return spinel_datatype_pack(
+        cmd_data_ptr,
+        cmd_data_len,
+        SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_UINT16_S),
+        prop_key,
+        x
+    );
+}
+
+spinel_ssize_t
+spinel_cmd_prop_value_set_ipv6addr(uint8_t* cmd_data_ptr, spinel_size_t cmd_data_len, spinel_prop_key_t prop_key, const spinel_ipv6addr_t* x_ptr)
+{
+    return spinel_datatype_pack(
+        cmd_data_ptr,
+        cmd_data_len,
+        SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_IPv6ADDR_S),
+        prop_key,
+        x_ptr
+    );
+}
+
+spinel_ssize_t
+spinel_cmd_prop_value_set_eui64(uint8_t* cmd_data_ptr, spinel_size_t cmd_data_len, spinel_prop_key_t prop_key, const spinel_eui64_t* x_ptr)
+{
+    return spinel_datatype_pack(
+        cmd_data_ptr,
+        cmd_data_len,
+        SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(SPINEL_DATATYPE_EUI64_S),
+        prop_key,
+        x_ptr
+    );
+}
+
+spinel_ssize_t
+spinel_cmd_prop_value_get(uint8_t* cmd_data_ptr, spinel_size_t cmd_data_len, spinel_prop_key_t prop_key)
+{
+    spinel_ssize_t ret;
+    ret = spinel_datatype_pack(
+        cmd_data_ptr,
+        cmd_data_len,
+        SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET,
+        prop_key
+    );
+    return ret;
+}
diff --git a/src/ncp-spinel/spinel-extra.h b/src/ncp-spinel/spinel-extra.h
new file mode 100644
index 0000000..25f3e71
--- /dev/null
+++ b/src/ncp-spinel/spinel-extra.h
@@ -0,0 +1,79 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef SPINEL_EXTRA_HEADER_INCLUDED
+#define SPINEL_EXTRA_HEADER_INCLUDED 1
+
+#include "spinel.h"
+
+__BEGIN_DECLS
+
+// ----------------------------------------------------------------------------
+
+typedef struct {
+	const uint8_t* data_ptr;
+	int data_len;
+	const char* pack_format;
+	spinel_datatype_t container;
+} spinel_datatype_iter_t;
+
+SPINEL_API_EXTERN spinel_status_t spinel_datatype_iter_start(spinel_datatype_iter_t* iter, const uint8_t* data_in, spinel_size_t data_len, const char* pack_format);
+SPINEL_API_EXTERN spinel_status_t spinel_datatype_iter_next(spinel_datatype_iter_t* iter);
+SPINEL_API_EXTERN spinel_status_t spinel_datatype_iter_open_container(const spinel_datatype_iter_t* iter, spinel_datatype_iter_t* subiter);
+SPINEL_API_EXTERN spinel_status_t spinel_datatype_iter_unpack(const spinel_datatype_iter_t* iter, ...);
+SPINEL_API_EXTERN spinel_status_t spinel_datatype_iter_vunpack(const spinel_datatype_iter_t* iter, va_list args);
+SPINEL_API_EXTERN spinel_datatype_t spinel_datatype_iter_get_type(const spinel_datatype_iter_t* iter);
+
+// ----------------------------------------------------------------------------
+
+#define SPINEL_FRAME_PACK_CMD(x)                     "Ci" x,SPINEL_HEADER_FLAG
+#define SPINEL_FRAME_PACK_CMD_NOOP                   SPINEL_FRAME_PACK_CMD(SPINEL_DATATYPE_NULL_S),SPINEL_CMD_NOOP
+#define SPINEL_FRAME_PACK_CMD_RESET                  SPINEL_FRAME_PACK_CMD(SPINEL_DATATYPE_NULL_S),SPINEL_CMD_RESET
+#define SPINEL_FRAME_PACK_CMD_NET_CLEAR              SPINEL_FRAME_PACK_CMD(SPINEL_DATATYPE_NULL_S),SPINEL_CMD_NET_CLEAR
+#define SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET         SPINEL_FRAME_PACK_CMD(SPINEL_DATATYPE_UINT_PACKED_S),SPINEL_CMD_PROP_VALUE_GET
+#define SPINEL_FRAME_PACK_CMD_PROP_TYPE_GET          SPINEL_FRAME_PACK_CMD(SPINEL_DATATYPE_UINT_PACKED_S),SPINEL_CMD_PROP_TYPE_GET
+#define SPINEL_FRAME_PACK_CMD_PROP_VALUE_SET(x)      SPINEL_FRAME_PACK_CMD(SPINEL_DATATYPE_UINT_PACKED_S x),SPINEL_CMD_PROP_VALUE_SET
+#define SPINEL_FRAME_PACK_CMD_PROP_VALUE_INSERT(x)   SPINEL_FRAME_PACK_CMD(SPINEL_DATATYPE_UINT_PACKED_S x),SPINEL_CMD_PROP_VALUE_INSERT
+#define SPINEL_FRAME_PACK_CMD_PROP_VALUE_REMOVE(x)   SPINEL_FRAME_PACK_CMD(SPINEL_DATATYPE_UINT_PACKED_S x),SPINEL_CMD_PROP_VALUE_REMOVE
+#define SPINEL_FRAME_PACK_CMD_PROP_VALUE_IS(x)       SPINEL_FRAME_PACK_CMD(SPINEL_DATATYPE_UINT_PACKED_S x),SPINEL_CMD_PROP_VALUE_IS
+#define SPINEL_FRAME_PACK_CMD_PROP_TYPE_IS           SPINEL_FRAME_PACK_CMD(SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT8_S),SPINEL_CMD_PROP_TYPE_IS
+#define SPINEL_FRAME_PACK_CMD_PROP_VALUE_INSERTED(x) SPINEL_FRAME_PACK_CMD(SPINEL_DATATYPE_UINT_PACKED_S x),SPINEL_CMD_PROP_VALUE_INSERTED
+#define SPINEL_FRAME_PACK_CMD_PROP_VALUE_REMOVED(x)  SPINEL_FRAME_PACK_CMD(SPINEL_DATATYPE_UINT_PACKED_S x),SPINEL_CMD_PROP_VALUE_REMOVED
+
+SPINEL_API_EXTERN spinel_ssize_t spinel_cmd_prop_value_set_data(uint8_t* cmd_data_ptr, spinel_size_t cmd_data_len, spinel_prop_key_t prop_key, const uint8_t* x_ptr, spinel_size_t x_len);
+
+SPINEL_API_EXTERN spinel_ssize_t spinel_cmd_prop_value_set_utf8(uint8_t* cmd_data_ptr, spinel_size_t cmd_data_len, spinel_prop_key_t prop_key, const char* x);
+
+SPINEL_API_EXTERN spinel_ssize_t spinel_cmd_prop_value_set_uint(uint8_t* cmd_data_ptr, spinel_size_t cmd_data_len, spinel_prop_key_t prop_key, unsigned int x);
+
+SPINEL_API_EXTERN spinel_ssize_t spinel_cmd_prop_value_set_uint16(uint8_t* cmd_data_ptr, spinel_size_t cmd_data_len, spinel_prop_key_t prop_key, uint16_t x);
+
+SPINEL_API_EXTERN spinel_ssize_t spinel_cmd_prop_value_set_ipv6addr(uint8_t* cmd_data_ptr, spinel_size_t cmd_data_len, spinel_prop_key_t prop_key, const spinel_ipv6addr_t* x_ptr);
+
+SPINEL_API_EXTERN spinel_ssize_t spinel_cmd_prop_value_set_eui64(uint8_t* cmd_data_ptr, spinel_size_t cmd_data_len, spinel_prop_key_t prop_key, const spinel_eui64_t* x_ptr);
+
+SPINEL_API_EXTERN spinel_ssize_t spinel_cmd_prop_value_get(uint8_t* cmd_data_ptr, spinel_size_t cmd_data_len, spinel_prop_key_t prop_key);
+
+SPINEL_API_EXTERN spinel_ssize_t spinel_cmd_prop_type_get(uint8_t* cmd_data_ptr, spinel_size_t cmd_data_len, spinel_prop_key_t prop_key);
+
+SPINEL_API_EXTERN spinel_ssize_t spinel_cmd_prop_type_get(uint8_t* cmd_data_ptr, spinel_size_t cmd_data_len, spinel_prop_key_t prop_key);
+
+__END_DECLS
+
+#endif // SPINEL_EXTRA_HEADER_INCLUDED
diff --git a/src/util/CallbackStore.hpp b/src/util/CallbackStore.hpp
new file mode 100644
index 0000000..f0b4163
--- /dev/null
+++ b/src/util/CallbackStore.hpp
@@ -0,0 +1,115 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      Class for handling one-time callbacks. Deprecated.
+ *
+ */
+
+#ifndef wpantund_callback_store_h
+#define wpantund_callback_store_h
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <map>
+#include <boost/signals2/signal.hpp>
+#include <boost/bind.hpp>
+#include <boost/ptr_container/ptr_map.hpp>
+
+template <class Key = std::string>
+class CallbackStore {
+public:
+	typedef boost::signals2::signal<void(int, const uint8_t* data, size_t len)>  signal_type;
+	typedef boost::shared_ptr<signal_type>  mapped_type;
+	typedef Key key_type;
+	typedef std::map<key_type, mapped_type> map_type;
+
+private:
+	map_type _map;
+
+public:
+	CallbackStore() {
+	}
+
+	void
+	add(const Key& name, const boost::function<void(int)>& func)
+	{
+		if(!_map[name])
+			_map[name] = mapped_type(new signal_type());
+
+		_map[name]->connect(boost::bind(func, _1));
+	}
+
+	void
+	add( const Key& name, const boost::function3<void, int, const uint8_t*, size_t>& func)
+	{
+		if(!_map[name])
+			_map[name] = mapped_type(new signal_type());
+
+		_map[name]->connect(func);
+	}
+
+	void
+	set(const Key& name, const mapped_type& signal)
+	{
+		if (static_cast<bool>(signal))
+			_map[name] = signal;
+	}
+
+
+	size_t
+	count(const Key& name)const {
+		if (_map.count(name) == 0 || NULL == _map.find(name)->second.get())
+			return 0;
+		return _map.find(name)->second->num_slots();
+	}
+
+	void
+	handle(const Key& name, int val, const uint8_t* data, size_t len)
+	{
+		mapped_type signal;
+		if(_map[name]) {
+			signal.swap(_map[name]);
+			_map.erase(name);
+			(*signal)(val, data, len);
+		}
+	}
+
+	mapped_type
+	unhandle(const Key& name)
+	{
+		mapped_type signal;
+		if (_map[name]) {
+			signal.swap(_map[name]);
+			_map.erase(name);
+		}
+		return signal;
+	}
+
+	void
+	handle_all(int val)
+	{
+		map_type map(_map);
+		for (typename map_type::iterator iter=map.begin(); iter!=map.end(); iter++) {
+			handle(iter->first, val, NULL, 0);
+		}
+	}
+};
+
+#endif
diff --git a/src/util/Callbacks.h b/src/util/Callbacks.h
new file mode 100644
index 0000000..2d4b433
--- /dev/null
+++ b/src/util/Callbacks.h
@@ -0,0 +1,60 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      Type definitions and utility functions related to callback objects.
+ *
+ */
+
+#ifndef wpantund_Callbacks_h
+#define wpantund_Callbacks_h
+
+#include <boost/signals2/signal.hpp>
+#include <boost/bind.hpp>
+#include <boost/any.hpp>
+
+namespace nl {
+
+typedef boost::function<void(void)> CallbackSimple;
+typedef boost::function<void(int)> CallbackWithStatus;
+typedef boost::function<void(int, const boost::any&)> CallbackWithStatusArg1;
+
+typedef boost::signals2::signal<void(int)> SignalWithStatus;
+
+static inline void
+split_cb_on_status(int status, CallbackSimple cb_success, CallbackWithStatus cb_error = nl::NilReturn())
+{
+	if (status == 0) {
+		cb_success();
+	} else {
+		cb_error(status);
+	}
+}
+
+#define CALLBACK_FUNC_SPLIT(success,failure) \
+		CallbackWithStatus( \
+			boost::bind( \
+				&split_cb_on_status, \
+				_1, \
+				CallbackSimple(success), \
+				CallbackWithStatus(failure) \
+			) \
+		)
+
+}; // namespace nl
+
+#endif
diff --git a/src/util/DBUSHelpers.cpp b/src/util/DBUSHelpers.cpp
new file mode 100644
index 0000000..e394ab9
--- /dev/null
+++ b/src/util/DBUSHelpers.cpp
@@ -0,0 +1,458 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      Implementation of various helper functions related to DBus
+ *      funcitonality.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "DBUSHelpers.h"
+#include "Data.h"
+#include <syslog.h>
+#include <string>
+#include <list>
+#include <map>
+#include <set>
+#include <exception>
+#include <stdexcept>
+#include "ValueMap.h"
+
+using namespace DBUSHelpers;
+
+nl::ValueMap
+DBUSHelpers::value_map_from_dbus_iter(DBusMessageIter *iter)
+{
+	nl::ValueMap ret;
+	DBusMessageIter sub_iter, dict_iter;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+		throw std::invalid_argument("Wrong type for value map");
+	}
+
+	dbus_message_iter_recurse(iter, &sub_iter);
+
+	if (dbus_message_iter_get_arg_type(&sub_iter) != DBUS_TYPE_DICT_ENTRY) {
+		throw std::invalid_argument("Wrong type for value map");
+	}
+
+	do {
+		const char* key_cstr;
+		dbus_message_iter_recurse(&sub_iter, &dict_iter);
+
+		if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) {
+			throw std::invalid_argument("Wrong type for value map");
+		}
+
+		dbus_message_iter_get_basic(&dict_iter, &key_cstr);
+		dbus_message_iter_next(&dict_iter);
+
+		ret[key_cstr] = any_from_dbus_iter(&dict_iter);
+	} while (dbus_message_iter_next(&sub_iter));
+
+	return ret;
+}
+
+boost::any
+DBUSHelpers::any_from_dbus_iter(DBusMessageIter *iter)
+{
+	boost::any ret;
+
+	switch (dbus_message_iter_get_arg_type(iter)) {
+	case DBUS_TYPE_ARRAY: {
+		DBusMessageIter sub_iter;
+		dbus_message_iter_recurse(iter, &sub_iter);
+		if (dbus_message_iter_get_arg_type(&sub_iter) == DBUS_TYPE_BYTE) {
+			const uint8_t* value = NULL;
+			int nelements = 0;
+			dbus_message_iter_get_fixed_array(&sub_iter, &value, &nelements);
+			ret = nl::Data(value, nelements);
+		} else if (dbus_message_iter_get_arg_type(&sub_iter) == DBUS_TYPE_DICT_ENTRY) {
+			ret = value_map_from_dbus_iter(iter);
+		} else {
+			syslog(LOG_NOTICE,
+			       "Unsupported DBUS array type for any: %d",
+			       dbus_message_iter_get_arg_type(&sub_iter));
+		}
+	} break;
+	case DBUS_TYPE_VARIANT: {
+		DBusMessageIter sub_iter;
+		dbus_message_iter_recurse(iter, &sub_iter);
+		ret = any_from_dbus_iter(&sub_iter);
+	} break;
+	case DBUS_TYPE_STRING: {
+		const char* v;
+		dbus_message_iter_get_basic(iter, &v);
+		ret = std::string(v);
+	} break;
+	case DBUS_TYPE_BOOLEAN: {
+		dbus_bool_t v;
+		dbus_message_iter_get_basic(iter, &v);
+		ret = bool(v);
+	} break;
+	case DBUS_TYPE_BYTE: {
+		uint8_t v;
+		dbus_message_iter_get_basic(iter, &v);
+		ret = v;
+	} break;
+	case DBUS_TYPE_DOUBLE: {
+		double v;
+		dbus_message_iter_get_basic(iter, &v);
+		ret = v;
+	} break;
+	case DBUS_TYPE_UINT16: {
+		uint16_t v;
+		dbus_message_iter_get_basic(iter, &v);
+		ret = v;
+	} break;
+	case DBUS_TYPE_INT16: {
+		int16_t v;
+		dbus_message_iter_get_basic(iter, &v);
+		ret = v;
+	} break;
+	case DBUS_TYPE_UINT32: {
+		uint32_t v;
+		dbus_message_iter_get_basic(iter, &v);
+		ret = v;
+	} break;
+	case DBUS_TYPE_INT32: {
+		int32_t v;
+		dbus_message_iter_get_basic(iter, &v);
+		ret = v;
+	} break;
+	case DBUS_TYPE_UINT64: {
+		uint16_t v;
+		dbus_message_iter_get_basic(iter, &v);
+		ret = v;
+	} break;
+	case DBUS_TYPE_INT64: {
+		int64_t v;
+		dbus_message_iter_get_basic(iter, &v);
+		ret = v;
+	} break;
+	}
+
+	return ret;
+}
+
+void
+DBUSHelpers::append_any_to_dbus_iter(
+    DBusMessageIter *iter, const boost::any &value
+    )
+{
+	if (value.type() == typeid(std::string)) {
+		std::string v = boost::any_cast<std::string>(value);
+		const char* cstr = v.c_str();
+		dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cstr);
+	} else if (value.type() == typeid(char*)) {
+		std::string v = boost::any_cast<char*>(value);
+		const char* cstr = v.c_str();
+		dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cstr);
+	} else if (value.type() == typeid(bool)) {
+		dbus_bool_t v = boost::any_cast<bool>(value);
+		dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &v);
+	} else if (value.type() == typeid(uint8_t)) {
+		uint8_t v = boost::any_cast<uint8_t>(value);
+		dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &v);
+	} else if (value.type() == typeid(int8_t)) {
+		int16_t v = boost::any_cast<int8_t>(value);
+		dbus_message_iter_append_basic(iter, DBUS_TYPE_INT16, &v);
+	} else if (value.type() == typeid(uint16_t)) {
+		uint16_t v = boost::any_cast<uint16_t>(value);
+		dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &v);
+	} else if (value.type() == typeid(int16_t)) {
+		int16_t v = boost::any_cast<int16_t>(value);
+		dbus_message_iter_append_basic(iter, DBUS_TYPE_INT16, &v);
+	} else if (value.type() == typeid(uint32_t)) {
+		uint32_t v = boost::any_cast<uint32_t>(value);
+		dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &v);
+	} else if (value.type() == typeid(int32_t)) {
+		int32_t v = boost::any_cast<int32_t>(value);
+		dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &v);
+	} else if (value.type() == typeid(uint64_t)) {
+		uint64_t v = boost::any_cast<uint64_t>(value);
+		dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &v);
+	} else if (value.type() == typeid(int64_t)) {
+		int64_t v = boost::any_cast<int64_t>(value);
+		dbus_message_iter_append_basic(iter, DBUS_TYPE_INT64, &v);
+	} else if (value.type() == typeid(double)) {
+		double v = boost::any_cast<double>(value);
+		dbus_message_iter_append_basic(iter, DBUS_TYPE_DOUBLE, &v);
+	} else if (value.type() == typeid(float)) {
+		double v = boost::any_cast<float>(value);
+		dbus_message_iter_append_basic(iter, DBUS_TYPE_DOUBLE, &v);
+	} else if (value.type() == typeid(std::list<std::string>)) {
+		DBusMessageIter array_iter;
+		const std::list<std::string>& list_of_strings =
+		    boost::any_cast< std::list<std::string> >(value);
+		std::list<std::string>::const_iterator list_iter;
+		dbus_message_iter_open_container(
+		    iter,
+		    DBUS_TYPE_ARRAY,
+		    DBUS_TYPE_STRING_AS_STRING,
+		    &array_iter
+		    );
+
+		for (list_iter = list_of_strings.begin();
+		     list_iter != list_of_strings.end();
+		     list_iter++) {
+			const char* cstr = list_iter->c_str();
+			dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING,
+			                               &cstr);
+		}
+
+		dbus_message_iter_close_container(iter, &array_iter);
+
+	} else if (value.type() == typeid(std::set<std::string>)) {
+		DBusMessageIter array_iter;
+		const std::set<std::string>& set_of_strings =
+		    boost::any_cast< std::set<std::string> >(value);
+		std::set<std::string>::const_iterator set_iter;
+		dbus_message_iter_open_container(
+		    iter,
+		    DBUS_TYPE_ARRAY,
+		    DBUS_TYPE_STRING_AS_STRING,
+		    &array_iter
+		    );
+
+		for (set_iter = set_of_strings.begin();
+		     set_iter != set_of_strings.end();
+		     set_iter++) {
+			const char* cstr = set_iter->c_str();
+			dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING,
+			                               &cstr);
+		}
+
+		dbus_message_iter_close_container(iter, &array_iter);
+	} else if (value.type() == typeid(nl::Data)) {
+		DBusMessageIter array_iter;
+		const nl::Data& vector = boost::any_cast< nl::Data >(value);
+		nl::Data::const_iterator vector_iter;
+		dbus_message_iter_open_container(
+		    iter,
+		    DBUS_TYPE_ARRAY,
+		    DBUS_TYPE_BYTE_AS_STRING,
+		    &array_iter
+		    );
+
+		for (vector_iter = vector.begin();
+		     vector_iter != vector.end();
+		     vector_iter++) {
+			dbus_message_iter_append_basic(&array_iter,
+			                               DBUS_TYPE_BYTE,
+			                               &*vector_iter);
+		}
+
+		dbus_message_iter_close_container(iter, &array_iter);
+	} else if (value.type() == typeid(std::vector<uint8_t>)) {
+		DBusMessageIter array_iter;
+		const std::vector<uint8_t>& vector =
+		    boost::any_cast< std::vector<uint8_t> >(value);
+		std::vector<uint8_t>::const_iterator vector_iter;
+		dbus_message_iter_open_container(
+		    iter,
+		    DBUS_TYPE_ARRAY,
+		    DBUS_TYPE_BYTE_AS_STRING,
+		    &array_iter
+		    );
+
+		for (vector_iter = vector.begin();
+		     vector_iter != vector.end();
+		     vector_iter++) {
+			dbus_message_iter_append_basic(&array_iter,
+			                               DBUS_TYPE_BYTE,
+			                               &*vector_iter);
+		}
+
+		dbus_message_iter_close_container(iter, &array_iter);
+	} else if (value.type() == typeid(std::set<int>)) {
+		DBusMessageIter array_iter;
+		const std::set<int>& container =
+		    boost::any_cast< std::set<int> >(value);
+		std::set<int>::const_iterator container_iter;
+		dbus_message_iter_open_container(
+		    iter,
+		    DBUS_TYPE_ARRAY,
+		    DBUS_TYPE_INT32_AS_STRING,
+		    &array_iter
+		    );
+
+		for (container_iter = container.begin();
+		     container_iter != container.end();
+		     container_iter++) {
+			dbus_message_iter_append_basic(&array_iter,
+			                               DBUS_TYPE_INT32,
+			                               &*container_iter);
+		}
+
+		dbus_message_iter_close_container(iter, &array_iter);
+	} else if (value.type() == typeid(nl::ValueMap)) {
+		DBusMessageIter array_iter;
+		const nl::ValueMap& value_map = boost::any_cast<nl::ValueMap>(value);
+		nl::ValueMap::const_iterator value_map_iter;
+
+		// Open a container as "Dictionary/Array of Strings to Variants" (dbus type "a{sv}")
+		dbus_message_iter_open_container(
+			iter,
+			DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+				DBUS_TYPE_STRING_AS_STRING
+				DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+			&array_iter
+			);
+
+		for (value_map_iter = value_map.begin(); value_map_iter != value_map.end(); ++value_map_iter) {
+			append_dict_entry(&array_iter, value_map_iter->first.c_str(), value_map_iter->second);
+		}
+
+		dbus_message_iter_close_container(iter, &array_iter);
+	} else if (value.type() == typeid(std::list<nl::ValueMap>)) {
+		DBusMessageIter array_iter;
+		const std::list<nl::ValueMap>& value_map_list = boost::any_cast< std::list<nl::ValueMap> >(value);
+		std::list<nl::ValueMap>::const_iterator list_iter;
+
+		// Open a container as "Array of Dictionaries/Arrays of Strings to Variants" (dbus type "aa{sv}")
+		dbus_message_iter_open_container(
+			iter,
+			DBUS_TYPE_ARRAY,
+			DBUS_TYPE_ARRAY_AS_STRING
+				DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_VARIANT_AS_STRING
+				DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+			&array_iter
+			);
+
+		for (list_iter = value_map_list.begin(); list_iter != value_map_list.end(); ++list_iter) {
+			append_any_to_dbus_iter(&array_iter, *list_iter);
+		}
+
+		dbus_message_iter_close_container(iter, &array_iter);
+	} else {
+		throw std::invalid_argument("Unsupported type");
+	}
+}
+
+std::string
+DBUSHelpers::any_to_dbus_type_string(const boost::any &value)
+{
+	if (value.type() == typeid(std::string)) {
+		return DBUS_TYPE_STRING_AS_STRING;
+	} else if (value.type() == typeid(bool)) {
+		return DBUS_TYPE_BOOLEAN_AS_STRING;
+	} else if (value.type() == typeid(uint8_t)) {
+		return DBUS_TYPE_BYTE_AS_STRING;
+	} else if (value.type() == typeid(int8_t)) {
+		return DBUS_TYPE_INT16_AS_STRING;
+	} else if (value.type() == typeid(uint16_t)) {
+		return DBUS_TYPE_UINT16_AS_STRING;
+	} else if (value.type() == typeid(int16_t)) {
+		return DBUS_TYPE_INT16_AS_STRING;
+	} else if (value.type() == typeid(uint32_t)) {
+		return DBUS_TYPE_UINT32_AS_STRING;
+	} else if (value.type() == typeid(int32_t)) {
+		return DBUS_TYPE_INT32_AS_STRING;
+	} else if (value.type() == typeid(uint64_t)) {
+		return DBUS_TYPE_UINT64_AS_STRING;
+	} else if (value.type() == typeid(int64_t)) {
+		return DBUS_TYPE_INT64_AS_STRING;
+	} else if (value.type() == typeid(double)) {
+		return DBUS_TYPE_DOUBLE_AS_STRING;
+	} else if (value.type() == typeid(float)) {
+		return DBUS_TYPE_DOUBLE_AS_STRING;
+	} else if (value.type() == typeid(nl::Data)) {
+		return DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING;
+	} else if (value.type() == typeid(std::vector<uint8_t>)) {
+		return DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING;
+	} else if (value.type() == typeid(std::list<std::string>)) {
+		return DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING;
+	} else if (value.type() == typeid(std::set<std::string>)) {
+		return DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING;
+	} else if (value.type() == typeid(nl::ValueMap)) {
+		return std::string(DBUS_TYPE_ARRAY_AS_STRING) +
+					DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING +
+						DBUS_TYPE_STRING_AS_STRING +
+						DBUS_TYPE_VARIANT_AS_STRING +
+					DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
+	} else if (value.type() == typeid(std::list<nl::ValueMap>)) {
+		return  std::string(DBUS_TYPE_ARRAY_AS_STRING) +
+					DBUS_TYPE_ARRAY_AS_STRING +
+						DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING +
+							DBUS_TYPE_STRING_AS_STRING +
+							DBUS_TYPE_VARIANT_AS_STRING +
+						DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
+	}
+
+	return "";
+}
+
+void
+DBUSHelpers::append_dict_entry(
+    DBusMessageIter *dict, const char *key, const boost::any& value
+    )
+{
+	DBusMessageIter entry;
+	DBusMessageIter value_iter;
+	std::string sig = any_to_dbus_type_string(value);
+
+	dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
+
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+	dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+	                                 sig.c_str(), &value_iter);
+
+	append_any_to_dbus_iter(&value_iter, value);
+
+	dbus_message_iter_close_container(&entry, &value_iter);
+
+	dbus_message_iter_close_container(dict, &entry);
+}
+
+void
+DBUSHelpers::append_dict_entry(
+    DBusMessageIter *dict, const char *key, char type, void *val
+    )
+{
+	DBusMessageIter entry;
+	DBusMessageIter value_iter;
+	char sig[2] = { type, '\0' };
+
+	if (type == DBUS_TYPE_STRING) {
+		const char *str = *((const char**)val);
+		if (str == NULL)
+			return;
+	}
+
+	dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
+
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+	dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, sig,
+	                                 &value_iter);
+
+	dbus_message_iter_append_basic(&value_iter, type, val);
+
+	dbus_message_iter_close_container(&entry, &value_iter);
+
+	dbus_message_iter_close_container(dict, &entry);
+}
diff --git a/src/util/DBUSHelpers.h b/src/util/DBUSHelpers.h
new file mode 100644
index 0000000..3925319
--- /dev/null
+++ b/src/util/DBUSHelpers.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      Declaration of various helper functions related to DBus
+ *      funcitonality.
+ *
+ */
+
+#ifndef __wpantund__DBUSHelpers__
+#define __wpantund__DBUSHelpers__
+
+#include <boost/any.hpp>
+#include <dbus/dbus.h>
+#include <string>
+#include "ValueMap.h"
+
+namespace DBUSHelpers {
+nl::ValueMap value_map_from_dbus_iter(DBusMessageIter *iter);
+boost::any any_from_dbus_iter(DBusMessageIter *iter);
+void append_any_to_dbus_iter(DBusMessageIter *iter, const boost::any &value);
+std::string any_to_dbus_type_string(const boost::any &value);
+void append_dict_entry(DBusMessageIter *dict, const char *key, const boost::any& value);
+void append_dict_entry(DBusMessageIter *dict, const char *key, char type, void *val);
+};
+
+#endif /* defined(__wpantund__DBUSHelpers__) */
diff --git a/src/util/Data.cpp b/src/util/Data.cpp
new file mode 100644
index 0000000..e4541ae
--- /dev/null
+++ b/src/util/Data.cpp
@@ -0,0 +1,23 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      Empty.
+ *
+ */
+
+#include "Data.h"
diff --git a/src/util/Data.h b/src/util/Data.h
new file mode 100644
index 0000000..f94637f
--- /dev/null
+++ b/src/util/Data.h
@@ -0,0 +1,71 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      Class definition for binary data container.
+ *
+ */
+
+#ifndef __wpantund__Data__
+#define __wpantund__Data__
+
+#include <vector>
+#include <stdint.h>
+#include <stdlib.h>
+
+namespace nl {
+class Data : public std::vector<uint8_t> {
+public:
+	Data(const std::vector<uint8_t>& x) : std::vector<uint8_t>(x) {
+	}
+	Data(
+	    const uint8_t* ptr, size_t len
+	    ) : std::vector<uint8_t>(ptr, ptr + len) {
+	}
+
+	template<typename _InputIterator>
+	Data(_InputIterator __first, _InputIterator __last)
+	: std::vector<uint8_t>(__first, __last) { }
+
+	Data(size_t len = 0) : std::vector<uint8_t>(len) {
+	}
+
+	Data& append(const Data& d) {
+		insert(end(),d.begin(),d.end());
+		return *this;
+	}
+
+	Data& append(const uint8_t* ptr, size_t len) {
+		insert(end(),ptr,ptr+len);
+		return *this;
+	}
+
+	void pop_front(size_t len) {
+		erase(begin(), begin()+len);
+	}
+
+	uint8_t* data() {
+		return &*begin();
+	}
+
+	const uint8_t* data() const {
+		return &*begin();
+	}
+};
+};
+
+#endif /* defined(__wpantund__Data__) */
diff --git a/src/util/EventHandler.cpp b/src/util/EventHandler.cpp
new file mode 100644
index 0000000..3ddbe59
--- /dev/null
+++ b/src/util/EventHandler.cpp
@@ -0,0 +1,76 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "EventHandler.h"
+
+using namespace nl;
+
+EventHandler::EventHandler():
+	mControlPT(), mControlTime()
+{
+	PT_INIT(&mControlPT);
+}
+
+EventHandler::~EventHandler()
+{
+}
+
+cms_t
+EventHandler::get_ms_to_next_event()
+{
+	cms_t cms = CMS_DISTANT_FUTURE;
+
+	if (mControlTime) {
+		cms = mControlTime - time_ms();
+
+		if (cms < 0) {
+			cms = 0;
+		}
+	}
+
+	return cms;
+
+}
+
+void
+EventHandler::schedule_next_event(float seconds_until_event)
+{
+	mControlTime = time_ms() + (cms_t)(seconds_until_event * MSEC_PER_SEC);
+}
+
+void
+EventHandler::unschedule_next_event(void)
+{
+	mControlTime = 0;
+}
+
+int
+EventHandler::process_event(int event, ...)
+{
+	int ret;
+	va_list args;
+	va_start(args, event);
+	ret = vprocess_event(event, args);
+	va_end(args);
+	return ret;
+}
diff --git a/src/util/EventHandler.h b/src/util/EventHandler.h
new file mode 100644
index 0000000..af430a5
--- /dev/null
+++ b/src/util/EventHandler.h
@@ -0,0 +1,110 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __wpantund__EventHandler__
+#define __wpantund__EventHandler__
+
+#include <cstdio>
+#include <cstdarg>
+#include <boost/signals2/signal.hpp>
+
+#include "assert-macros.h"
+#include "time-utils.h"
+#include "nlpt.h"
+
+namespace nl {
+
+#define EVENT_NULL		0
+#define EVENT_IDLE		1
+#define EVENT_STARTING_TASK		2
+
+#define EH_BEGIN_SUB(pt)	{ bool eh_did_timeout(false); PT*const __eh_pt = (pt); (void)eh_did_timeout; PT_BEGIN(__eh_pt)
+#define EH_BEGIN()			EH_BEGIN_SUB(&mControlPT)
+#define EH_END()			PT_END(__eh_pt) }
+
+#define EH_SPAWN(child, proc)	PT_SPAWN(__eh_pt, (child), (proc))
+
+// Explicitly terminate the protothread.
+#define EH_EXIT()		PT_EXIT(__eh_pt)
+
+// Explicitly restart the protothread.
+#define EH_RESTART()	PT_RESTART(__eh_pt)
+
+// Yield execution for one cycle.
+#define EH_YIELD()		PT_YIELD(__eh_pt)
+
+// Suspend execution until condition `c` is satisfied.
+#define EH_WAIT_UNTIL(c) PT_WAIT_UNTIL(__eh_pt, c)
+
+// Suspend execution until condition `c` is satisfied.
+#define EH_YIELD_UNTIL(c) PT_YIELD_UNTIL(__eh_pt, c)
+
+// Unconditionally suspend execution for `s` seconds.
+#define EH_SLEEP_FOR(s) do { \
+		schedule_next_event(s); \
+		EH_YIELD_UNTIL(EventHandler::get_ms_to_next_event() == 0); \
+		unschedule_next_event(); \
+	} while (0)
+
+// Wait for condition `c` be satisfied for no more than `s` seconds.
+#define EH_WAIT_UNTIL_WITH_TIMEOUT(s, c) do { \
+		eh_did_timeout = false; \
+		schedule_next_event(s); \
+		EH_WAIT_UNTIL((c) || (eh_did_timeout=(EventHandler::get_ms_to_next_event() == 0))); \
+		unschedule_next_event(); \
+	} while (0)
+
+// Require that condition `c` be satisfied within `s` seconds. If not, goto `l`.
+#define EH_REQUIRE_WITHIN(s, c, l) do { \
+		EH_WAIT_UNTIL_WITH_TIMEOUT(s, c); \
+		require_string(!eh_did_timeout, l, # c); \
+	} while (0)
+
+
+// This class is the base class for tasks (like joining or forming).
+// They allow you to implement complex, stateful processes without being
+// too unwieldly.
+class EventHandler
+{
+public:
+	typedef struct pt PT;
+
+	EventHandler();
+
+	virtual ~EventHandler();
+
+	virtual cms_t get_ms_to_next_event();
+
+	virtual int vprocess_event(int event, va_list args) = 0;
+
+	int process_event(int event, ...);
+
+	void schedule_next_event(float seconds_until_event);
+	void unschedule_next_event(void);
+
+public:
+	PT mControlPT;
+
+private:
+	cms_t mControlTime;
+};
+
+}; // namespace nl
+
+#endif /* defined(__wpantund__EventHandler__) */
diff --git a/src/util/IPv6Helpers.h b/src/util/IPv6Helpers.h
new file mode 100644
index 0000000..95a2f38
--- /dev/null
+++ b/src/util/IPv6Helpers.h
@@ -0,0 +1,81 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      This file contains helper glue code for manipulating IPv6 addresses.
+ *
+ */
+
+#ifndef wpantund_IPv6Helpers_h
+#define wpantund_IPv6Helpers_h
+
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <string>
+
+#define MINIMUM_IPV6_PACKET_SIZE	40
+
+static inline bool
+operator==(const struct in6_addr &lhs, const struct in6_addr &rhs)
+{
+	return 0 == memcmp((const void*)&lhs, (const void*)&rhs, sizeof(rhs));
+}
+
+static inline bool
+operator!=(const struct in6_addr &lhs, const struct in6_addr &rhs)
+{
+	return !(lhs == rhs);
+}
+
+static inline bool
+operator<(const struct in6_addr &lhs, const struct in6_addr &rhs)
+{
+	return memcmp(lhs.s6_addr, rhs.s6_addr, sizeof(struct in6_addr)) < 0;
+}
+
+static inline std::string
+in6_addr_to_string(const struct in6_addr &addr) {
+	char address_string[INET6_ADDRSTRLEN] = "::";
+	inet_ntop(AF_INET6, (const void *)&addr, address_string, sizeof(address_string));
+	return std::string(address_string);
+}
+
+static inline void
+in6_addr_apply_mask(struct in6_addr &address, uint8_t mask)
+{
+	if (mask > 128) {
+		mask = 128;
+	}
+
+	memset(
+		(void*)(address.s6_addr + ((mask + 7) / 8)),
+		0,
+		16 - ((mask + 7) / 8)
+	);
+
+	if (mask % 8) {
+		address.s6_addr[mask / 8] &= ~(0xFF >> (mask % 8));
+	}
+}
+
+static inline bool
+is_valid_ipv6_packet(const uint8_t* packet, ssize_t len) {
+	return (len > MINIMUM_IPV6_PACKET_SIZE)
+		&& (packet[0] & 0xF0) == 0x60; // IPv6 Version
+}
+#endif
diff --git a/src/util/IPv6PacketMatcher.cpp b/src/util/IPv6PacketMatcher.cpp
new file mode 100644
index 0000000..9455ad7
--- /dev/null
+++ b/src/util/IPv6PacketMatcher.cpp
@@ -0,0 +1,550 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "IPv6PacketMatcher.h"
+#include <syslog.h>
+#include <stdio.h>
+
+#ifndef IPV6_PACKET_MATCHER_DEBUG
+#define IPV6_PACKET_MATCHER_DEBUG 0
+#endif
+
+using namespace nl;
+
+const uint8_t IPv6PacketMatcherRule::TYPE_ALL = 0xFF;
+const uint8_t IPv6PacketMatcherRule::TYPE_NONE = 0xFE;
+const uint8_t IPv6PacketMatcherRule::TYPE_UDP = 17;
+const uint8_t IPv6PacketMatcherRule::TYPE_TCP = 6;
+const uint8_t IPv6PacketMatcherRule::TYPE_ICMP = 58;
+const uint8_t IPv6PacketMatcherRule::TYPE_HOP_BY_HOP = 0;
+
+const uint8_t IPv6PacketMatcherRule::SUBTYPE_ALL = 0xFF;
+const uint8_t IPv6PacketMatcherRule::SUBTYPE_ICMP_NEIGHBOR_ADV = 136;
+const uint8_t IPv6PacketMatcherRule::SUBTYPE_ICMP_NEIGHBOR_SOL = 135;
+const uint8_t IPv6PacketMatcherRule::SUBTYPE_ICMP_ROUTER_SOL = 133;
+const uint8_t IPv6PacketMatcherRule::SUBTYPE_ICMP_ROUTER_ADV = 134;
+
+#define IPV6_HEADER_LENGTH              40
+#define IPV6_TCP_HEADER_CHECKSUM_OFFSET (IPV6_HEADER_LENGTH + 16)
+#define IPV6_UDP_HEADER_CHECKSUM_OFFSET (IPV6_HEADER_LENGTH + 6)
+
+#define PACKET_IS_IPV6(x)        ((static_cast<const uint8_t*>(x)[0] & 0xF0) == 0x60)
+#define IPV6_GET_TYPE(x)         (static_cast<const uint8_t*>(x)[6])
+#define IPV6_GET_SRC_PORT(x)     (in_port_t)(*(const uint16_t*)((const uint8_t*)(x)+40))
+#define IPV6_GET_DEST_PORT(x)    (in_port_t)(*(const uint16_t*)((const uint8_t*)(x)+42))
+#define IPV6_GET_SRC_ADDR(t,f)   memcpy(&t, static_cast<const uint8_t*>(f) + 8, 16)
+#define IPV6_GET_DEST_ADDR(t,f)  memcpy(&t, static_cast<const uint8_t*>(f) + 24, 16)
+#define IPV6_ICMP_GET_SUBTYPE(x) (static_cast<const uint8_t*>(x)[40])
+#define IPv6_TCP_GET_CHECKSUM(p,l)  IPV6_GET_UINT16(p, l, IPV6_TCP_HEADER_CHECKSUM_OFFSET)
+#define IPv6_UDP_GET_CHECKSUM(p,l)  IPV6_GET_UINT16(p, l, IPV6_UDP_HEADER_CHECKSUM_OFFSET)
+
+static inline uint16_t IPV6_GET_UINT16(const uint8_t *packet, ssize_t len, size_t offset)
+{
+	uint16_t ret;
+
+	ret = (len >= offset + sizeof(uint16_t))
+		? (packet[offset]<<8) | (packet[offset + 1]<<0)
+		: 0;
+
+	return ret;
+}
+
+static void ipv6_add_extra_description(char *buffer, size_t buffer_size, const uint8_t *packet, ssize_t len)
+{
+	uint8_t type(IPV6_GET_TYPE(packet));
+
+	switch (type)
+	{
+	case IPv6PacketMatcherRule::TYPE_TCP:
+		snprintf(buffer, buffer_size, "(cksum 0x%04x)", IPv6_TCP_GET_CHECKSUM(packet, len));
+		break;
+
+	case IPv6PacketMatcherRule::TYPE_UDP:
+		snprintf(buffer, buffer_size, "(cksum 0x%04x)", IPv6_UDP_GET_CHECKSUM(packet, len));
+		break;
+
+	default:
+		buffer[0] = 0;
+	}
+}
+
+void
+IPv6PacketMatcherRule::clear()
+{
+	memset((void*)this, 0, sizeof(*this));
+	type = TYPE_ALL;
+	subtype = SUBTYPE_ALL;
+}
+
+IPv6PacketMatcherRule&
+IPv6PacketMatcherRule::update_from_inbound_packet(const uint8_t* packet)
+{
+	struct in6_addr address;
+
+	clear();
+
+	if (!PACKET_IS_IPV6(packet)) {
+		goto bail;
+	}
+
+	type = IPV6_GET_TYPE(packet);
+
+	subtype = IPv6PacketMatcherRule::SUBTYPE_ALL;
+
+	if (type == IPv6PacketMatcherRule::TYPE_TCP || type == IPv6PacketMatcherRule::TYPE_UDP) {
+		remote_port = IPV6_GET_SRC_PORT(packet);
+		remote_port_match = true;
+
+		local_port = IPV6_GET_DEST_PORT(packet);
+		local_port_match = true;
+	} else {
+		remote_port = 0;
+		remote_port_match = false;
+		local_port = 0;
+		local_port_match = false;
+		if (type == IPv6PacketMatcherRule::TYPE_ICMP) {
+			subtype = IPV6_ICMP_GET_SUBTYPE(packet);
+		}
+	}
+
+	IPV6_GET_DEST_ADDR(address, packet);
+	if (!IN6_IS_ADDR_MULTICAST(&address)) {
+		local_address = address;
+		local_match_mask = 128;
+	} else {
+		local_match_mask = 0;
+	}
+
+	IPV6_GET_SRC_ADDR(address, packet);
+	remote_address = address;
+	remote_match_mask = 128;
+
+bail:
+	return *this;
+}
+
+
+bool
+IPv6PacketMatcherRule::match_inbound(const uint8_t* packet) const
+{
+	if (!PACKET_IS_IPV6(packet)) {
+		return false;
+	}
+
+	if (type == TYPE_NONE) {
+		return false;
+	}
+
+	if (type != IPv6PacketMatcherRule::TYPE_ALL) {
+		if (type != IPV6_GET_TYPE(packet)) {
+			return false;
+		}
+		if (subtype != IPv6PacketMatcherRule::SUBTYPE_ALL) {
+			if (subtype != IPV6_ICMP_GET_SUBTYPE(packet)) {
+				return false;
+			}
+		}
+	}
+
+	if (local_port_match) {
+		in_port_t port(IPV6_GET_DEST_PORT(packet));
+		if (port != local_port)
+			return false;
+	}
+
+	if (remote_port_match) {
+		in_port_t port(IPV6_GET_SRC_PORT(packet));
+		if (port != remote_port)
+			return false;
+	}
+
+	if (local_match_mask) {
+		struct in6_addr address;
+		IPV6_GET_DEST_ADDR(address, packet);
+		in6_addr_apply_mask(address, local_match_mask);
+		if (address != local_address)
+			return false;
+	}
+
+	if (remote_match_mask) {
+		struct in6_addr address;
+		IPV6_GET_SRC_ADDR(address, packet);
+		in6_addr_apply_mask(address, remote_match_mask);
+		if (address != remote_address)
+			return false;
+	}
+	return true;
+}
+
+IPv6PacketMatcherRule&
+IPv6PacketMatcherRule::update_from_outbound_packet(const uint8_t* packet)
+{
+	struct in6_addr address;
+
+	clear();
+
+	if (!PACKET_IS_IPV6(packet)) {
+		goto bail;
+	}
+
+	type = IPV6_GET_TYPE(packet);
+
+	subtype = IPv6PacketMatcherRule::SUBTYPE_ALL;
+
+	if (type == IPv6PacketMatcherRule::TYPE_TCP || type == IPv6PacketMatcherRule::TYPE_UDP) {
+		remote_port = IPV6_GET_DEST_PORT(packet);
+		remote_port_match = true;
+
+		local_port = IPV6_GET_SRC_PORT(packet);
+		local_port_match = true;
+	} else {
+		remote_port = 0;
+		remote_port_match = false;
+		local_port = 0;
+		local_port_match = false;
+		if (type == IPv6PacketMatcherRule::TYPE_ICMP) {
+			subtype = IPV6_ICMP_GET_SUBTYPE(packet);
+		}
+	}
+
+	IPV6_GET_SRC_ADDR(address, packet);
+	local_address = address;
+	local_match_mask = 128;
+
+	IPV6_GET_DEST_ADDR(address, packet);
+	remote_address = address;
+	remote_match_mask = 128;
+
+bail:
+	return *this;
+}
+
+bool
+IPv6PacketMatcherRule::match_outbound(const uint8_t* packet) const
+{
+	if (!PACKET_IS_IPV6(packet)) {
+		return false;
+	}
+
+	if (type == TYPE_NONE) {
+		return false;
+	}
+
+	if (type != IPv6PacketMatcherRule::TYPE_ALL) {
+		if (type != IPV6_GET_TYPE(packet)) {
+			return false;
+		}
+		if (subtype != IPv6PacketMatcherRule::SUBTYPE_ALL) {
+			if (subtype != IPV6_ICMP_GET_SUBTYPE(packet)) {
+				return false;
+			}
+		}
+	}
+
+	if (local_port_match) {
+		in_port_t port(IPV6_GET_SRC_PORT(packet));
+		if (port != local_port) {
+			return false;
+		}
+	}
+
+	if (remote_port_match) {
+		in_port_t port(IPV6_GET_DEST_PORT(packet));
+		if (port != remote_port) {
+			return false;
+		}
+	}
+
+	if (local_match_mask) {
+		struct in6_addr address;
+		IPV6_GET_SRC_ADDR(address, packet);
+		in6_addr_apply_mask(address, local_match_mask);
+		if (address != local_address) {
+			return false;
+		}
+	}
+
+	if (remote_match_mask) {
+		struct in6_addr address;
+		IPV6_GET_DEST_ADDR(address, packet);
+		in6_addr_apply_mask(address, remote_match_mask);
+		if (address != remote_address) {
+			return false;
+		}
+	}
+	return true;
+}
+
+bool
+IPv6PacketMatcherRule::operator==(const IPv6PacketMatcherRule& lhs) const
+{
+#if IPV6_PACKET_MATCHER_DEBUG
+	syslog(LOG_DEBUG, "IPv6PacketMatcherRule operator==():\n");
+
+#define __DUMP(x,f,t) syslog(LOG_DEBUG, "\t lhs." #x "=" f "\t%s\trhs." #x "=" f "\n",(t)lhs.x,(lhs.x < x)?">":(x > lhs.x)?">":"==",(t)x);
+#define __DUMP_NTOHS(x,f) syslog(LOG_DEBUG, "\t lhs." #x "=" f "\t%s\trhs." #x "=" f "\n",ntohs(lhs.x),(ntohs(lhs.x) < ntohs(x))?">":(ntohs(x) > ntohs(lhs.x))?">":"==",ntohs(x));
+#define __DUMP_MEM(x) syslog(LOG_DEBUG, "\t lhs." #x "\t%s\trhs." #x "\n",(memcmp(&x, &lhs.x, 16) < 0)?">":(memcmp(&x, &lhs.x, 16) > 0)?">":"==");
+
+	__DUMP(type,"%d",int);
+	__DUMP(subtype,"%d",int);
+	__DUMP_NTOHS(local_port,"%d");
+	__DUMP(local_port_match,"%d",int);
+	__DUMP(local_match_mask,"%d",int);
+	__DUMP_MEM(local_address);
+
+	__DUMP_NTOHS(remote_port,"%d");
+	__DUMP(remote_port_match,"%d",int);
+	__DUMP(remote_match_mask,"%d",int);
+	__DUMP_MEM(remote_address);
+
+#undef __DUMP
+#undef __DUMP_NTOHS
+#undef __DUMP_MEM
+
+#endif // IPV6_PACKET_MATCHER_DEBUG
+
+	if (type != lhs.type) {
+		return false;
+	}
+	if (subtype != lhs.subtype) {
+		return false;
+	}
+	if (local_port != lhs.local_port) {
+		return false;
+	}
+	if (local_port_match != lhs.local_port_match) {
+		return false;
+	}
+	if (local_match_mask != lhs.local_match_mask) {
+		return false;
+	}
+	if (local_address != lhs.local_address) {
+		return false;
+	}
+	if (remote_port != lhs.remote_port) {
+		return false;
+	}
+	if (remote_port_match != lhs.remote_port_match) {
+		return false;
+	}
+	if (remote_match_mask != lhs.remote_match_mask) {
+		return false;
+	}
+	if (remote_address != lhs.remote_address) {
+		return false;
+	}
+	return true;
+
+	return 0 == memcmp(this,
+	                   &lhs,
+	                   (uint8_t*)&remote_match_mask - (uint8_t*)this + 1);
+}
+
+bool
+IPv6PacketMatcherRule::operator<(const IPv6PacketMatcherRule& lhs) const
+{
+	if (type < lhs.type) {
+		return true;
+	} else if (type > lhs.type) {
+		return false;
+	}
+
+	if (subtype < lhs.subtype) {
+		return true;
+	} else if (subtype > lhs.subtype) {
+		return false;
+	}
+
+	if (local_port < lhs.local_port) {
+		return true;
+	} else if (local_port > lhs.local_port) {
+		return false;
+	}
+
+	if (local_port_match < lhs.local_port_match) {
+		return true;
+	} else if (local_port_match > lhs.local_port_match) {
+		return false;
+	}
+
+	if (local_match_mask < lhs.local_match_mask) {
+		return true;
+	} else if (local_match_mask > lhs.local_match_mask) {
+		return false;
+	}
+
+	if (memcmp(&local_address, &lhs.local_address, 16) < 0) {
+		return true;
+	} else if (memcmp(&local_address, &lhs.local_address, 16) > 0) {
+		return false;
+	}
+
+	if (remote_port < lhs.remote_port) {
+		return true;
+	} else if (remote_port > lhs.remote_port) {
+		return false;
+	}
+
+	if (remote_port_match < lhs.remote_port_match) {
+		return true;
+	} else if (remote_port_match > lhs.remote_port_match) {
+		return false;
+	}
+
+	if (remote_match_mask < lhs.remote_match_mask) {
+		return true;
+	} else if (remote_match_mask > lhs.remote_match_mask) {
+		return false;
+	}
+
+	if (memcmp(&remote_address, &lhs.remote_address, 16) < 0) {
+		return true;
+	} else if (memcmp(&remote_address, &lhs.remote_address, 16) > 0) {
+		return false;
+	}
+
+	return false;
+}
+
+IPv6PacketMatcher::const_iterator
+IPv6PacketMatcher::match_outbound(const uint8_t* packet) const
+{
+	iterator iter;
+	for(iter = begin(); iter != end(); ++iter) {
+		if(iter->match_outbound(packet)) {
+			break;
+		}
+	}
+	return iter;
+}
+
+IPv6PacketMatcher::const_iterator
+IPv6PacketMatcher::match_inbound(const uint8_t* packet) const
+{
+	iterator iter;
+	for(iter = begin(); iter != end(); ++iter) {
+		if(iter->match_inbound(packet)) {
+			break;
+		}
+	}
+	return iter;
+}
+
+void
+nl::dump_outbound_ipv6_packet(const uint8_t* packet, ssize_t len, const char* extra, bool dropped)
+{
+	if(!(setlogmask(0)&LOG_MASK(LOG_INFO))) {
+		return;
+	}
+	char to_addr_cstr[INET6_ADDRSTRLEN] = "::";
+	char from_addr_cstr[INET6_ADDRSTRLEN] = "::";
+	uint8_t type(IPV6_GET_TYPE(packet));
+	char type_extra[32];
+	struct in6_addr addr;
+
+	ipv6_add_extra_description(type_extra, sizeof(type_extra), packet, len);
+
+	syslog(LOG_INFO,
+		   "[->NCP] IPv6 len:%d type:%d%s [%s]%s",
+		   (int)len,
+		   type,
+		   type_extra,
+		   extra,
+		   dropped?" [DROPPED]":""
+	);
+
+	IPV6_GET_SRC_ADDR(addr, packet);
+	inet_ntop(AF_INET6, addr.s6_addr, from_addr_cstr, sizeof(from_addr_cstr));
+
+	IPV6_GET_DEST_ADDR(addr, packet);
+	inet_ntop(AF_INET6, addr.s6_addr, to_addr_cstr, sizeof(to_addr_cstr));
+
+	if ((type == IPv6PacketMatcherRule::TYPE_TCP)
+		|| (type == IPv6PacketMatcherRule::TYPE_UDP)
+	) {
+		in_port_t to_port(IPV6_GET_DEST_PORT(packet));
+		in_port_t from_port(IPV6_GET_SRC_PORT(packet));
+
+		syslog(LOG_INFO,
+			   "\tto(remote):[%s]:%d",
+			   to_addr_cstr,
+			   htons(to_port));
+
+		syslog(LOG_INFO,
+			   "\tfrom(local):[%s]:%d",
+			   from_addr_cstr,
+			   htons(from_port));
+	} else {
+		syslog(LOG_INFO, "\tto(remote):[%s]", to_addr_cstr);
+		syslog(LOG_INFO, "\tfrom(local):[%s]", from_addr_cstr);
+	}
+}
+
+void
+nl::dump_inbound_ipv6_packet(const uint8_t* packet, ssize_t len, const char* extra, bool dropped)
+{
+	if(!(setlogmask(0)&LOG_MASK(LOG_INFO))) {
+		return;
+	}
+	char to_addr_cstr[INET6_ADDRSTRLEN] = "::";
+	char from_addr_cstr[INET6_ADDRSTRLEN] = "::";
+	uint8_t type(IPV6_GET_TYPE(packet));
+	char type_extra[32];
+	struct in6_addr addr;
+
+	ipv6_add_extra_description(type_extra, sizeof(type_extra), packet, len);
+
+	syslog(LOG_INFO,
+		   "[NCP->] IPv6 len:%d type:%d%s [%s]%s",
+		   (int)len,
+		   type,
+		   type_extra,
+		   extra,
+		   dropped?" [DROPPED]":""
+	);
+
+	IPV6_GET_SRC_ADDR(addr, packet);
+	inet_ntop(AF_INET6, addr.s6_addr, from_addr_cstr, sizeof(from_addr_cstr));
+
+	IPV6_GET_DEST_ADDR(addr, packet);
+	inet_ntop(AF_INET6, addr.s6_addr, to_addr_cstr, sizeof(to_addr_cstr));
+	if ((type == IPv6PacketMatcherRule::TYPE_TCP)
+		|| (type == IPv6PacketMatcherRule::TYPE_UDP)
+	) {
+		in_port_t to_port(IPV6_GET_DEST_PORT(packet));
+		in_port_t from_port(IPV6_GET_SRC_PORT(packet));
+
+		syslog(LOG_INFO,
+			   "\tto(local):[%s]:%d",
+			   to_addr_cstr,
+			   htons(to_port));
+
+		syslog(LOG_INFO,
+			   "\tfrom(remote):[%s]:%d",
+			   from_addr_cstr,
+			   htons(from_port));
+	} else {
+		syslog(LOG_INFO, "\tto(local):[%s]", to_addr_cstr);
+		syslog(LOG_INFO, "\tfrom(remote):[%s]", from_addr_cstr);
+	}
+}
diff --git a/src/util/IPv6PacketMatcher.h b/src/util/IPv6PacketMatcher.h
new file mode 100644
index 0000000..f8e218a
--- /dev/null
+++ b/src/util/IPv6PacketMatcher.h
@@ -0,0 +1,84 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __wpantund__IPv6PacketMatcherRule__
+#define __wpantund__IPv6PacketMatcherRule__
+
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <set>
+#include "IPv6Helpers.h"
+
+
+namespace nl {
+
+void dump_outbound_ipv6_packet(const uint8_t* packet, ssize_t len, const char* extra, bool dropped = false);
+void dump_inbound_ipv6_packet(const uint8_t* packet, ssize_t len, const char* extra, bool dropped = false);
+
+struct IPv6PacketMatcherRule {
+	static const uint8_t TYPE_ALL;
+	static const uint8_t TYPE_NONE;
+	static const uint8_t TYPE_UDP;
+	static const uint8_t TYPE_TCP;
+	static const uint8_t TYPE_ICMP;
+	static const uint8_t TYPE_HOP_BY_HOP;
+	static const uint8_t SUBTYPE_ALL;
+	static const uint8_t SUBTYPE_ICMP_NEIGHBOR_ADV;
+	static const uint8_t SUBTYPE_ICMP_NEIGHBOR_SOL;
+	static const uint8_t SUBTYPE_ICMP_ROUTER_ADV;
+	static const uint8_t SUBTYPE_ICMP_ROUTER_SOL;
+
+	uint8_t type;
+	uint8_t subtype;
+	in_port_t local_port;
+	bool local_port_match;
+	struct in6_addr local_address;
+	uint8_t local_match_mask;
+
+	in_port_t remote_port;
+	bool remote_port_match;
+	struct in6_addr remote_address;
+	uint8_t remote_match_mask;
+
+	void clear();
+	IPv6PacketMatcherRule&        update_from_inbound_packet(const uint8_t* packet);
+	bool                            match_inbound(const uint8_t* packet) const;
+	IPv6PacketMatcherRule&        update_from_outbound_packet(const uint8_t* packet);
+	bool                            match_outbound(const uint8_t* packet) const;
+	bool operator==(const IPv6PacketMatcherRule& lhs) const;
+	bool operator<(const IPv6PacketMatcherRule& lhs) const;
+
+	bool operator!=(const IPv6PacketMatcherRule& lhs) const { return !(*this == lhs); }
+	bool operator>=(const IPv6PacketMatcherRule& lhs) const { return !(*this < lhs); }
+
+	bool operator<=(const IPv6PacketMatcherRule& lhs) const { return (*this < lhs) || (*this == lhs); }
+	bool operator>(const IPv6PacketMatcherRule& lhs) const { return !(*this <= lhs); }
+};
+
+class IPv6PacketMatcher : public std::set<IPv6PacketMatcherRule> {
+public:
+
+	const_iterator match_outbound(const uint8_t* packet) const;
+	const_iterator match_inbound(const uint8_t* packet) const;
+};
+
+}; // namespace nl
+
+#endif /* defined(__wpantund__IPv6PacketMatcherRule__) */
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
new file mode 100644
index 0000000..4fa54e0
--- /dev/null
+++ b/src/util/Makefile.am
@@ -0,0 +1,69 @@
+#
+# Copyright (c) 2016 Nest Labs, Inc.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+EXTRA_DIST = \
+	config-file.c \
+	nlpt-select.c \
+	socket-utils.c \
+	string-utils.c \
+	time-utils.c \
+	tunnel.c \
+	DBUSHelpers.cpp \
+	Data.cpp \
+	EventHandler.cpp \
+	IPv6PacketMatcher.cpp \
+	SocketAdapter.cpp \
+	SocketWrapper.cpp \
+	SuperSocket.cpp \
+	TunnelIPv6Interface.cpp \
+	UnixSocket.cpp \
+	any-to.cpp \
+	Callbacks.h \
+	DBUSHelpers.h \
+	Data.h \
+	EventHandler.h \
+	IPv6Helpers.h \
+	IPv6PacketMatcher.h \
+	NilReturn.h \
+	SocketAdapter.h \
+	SocketAsyncOp.h \
+	SocketWrapper.h \
+	SuperSocket.h \
+	TunnelIPv6Interface.h \
+	UnixSocket.h \
+	any-to.h \
+	args.h \
+	config-file.h \
+	nlpt-select.h \
+	nlpt.h \
+	socket-utils.h \
+	string-utils.h \
+	time-utils.h \
+	tunnel.h \
+	CallbackStore.hpp \
+	RingBuffer.h \
+	ValueMap.h \
+	ValueMap.cpp \
+	ObjectPool.h \
+	Timer.h \
+	Timer.cpp \
+	$(NULL)
+
+DISTCLEANFILES = \
+	.deps \
+	Makefile \
+	$(NULL)
diff --git a/src/util/NilReturn.h b/src/util/NilReturn.h
new file mode 100644
index 0000000..c736be8
--- /dev/null
+++ b/src/util/NilReturn.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef wpantund_NilReturn_h
+#define wpantund_NilReturn_h
+
+
+namespace nl {
+class NilReturn {
+public:
+	void operator()(void) {
+	}
+	void operator()(int) {
+	}
+	template<typename ARG_X, typename ARG_Y> void operator()(ARG_X, ARG_Y) {
+	}
+};
+};
+
+#endif
diff --git a/src/util/ObjectPool.h b/src/util/ObjectPool.h
new file mode 100644
index 0000000..493b3a1
--- /dev/null
+++ b/src/util/ObjectPool.h
@@ -0,0 +1,92 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      A simple object pool implementation
+ *
+ */
+
+#ifndef wpantund_ObjectPool_h
+#define wpantund_ObjectPool_h
+
+#include <list>
+
+namespace nl {
+
+template <typename T, int I = 64>
+class ObjectPool
+{
+public:
+	typedef T element_type;
+	typedef int size_type;
+
+	static const size_type pool_size = I;
+
+public:
+	ObjectPool(): mFreeElementList()
+	{
+		free_all();
+	}
+
+	// Free all elements in the object pool
+	void free_all(void)
+	{
+		size_type count = pool_size;
+		element_type *element_ptr;
+
+		mFreeElementList.clear();
+
+		element_ptr = &mElementPool[pool_size - 1];
+		while(count--) {
+			mFreeElementList.push_back(element_ptr);
+			element_ptr--;
+		}
+	}
+
+	// Attempts to allocate a new object from pool, returns NULL if no object available.
+	element_type *alloc(void)
+	{
+		element_type *element_ptr = NULL;
+		if (!mFreeElementList.empty()) {
+			element_ptr = mFreeElementList.front();
+			mFreeElementList.pop_front();
+		}
+		return element_ptr;
+	}
+
+	// Frees a previously allocated pool object.
+	void free(element_type *element_ptr)
+	{
+		if (is_ptr_in_pool(element_ptr)) {
+			mFreeElementList.push_back(element_ptr);
+		}
+	}
+
+private:
+	element_type mElementPool[pool_size];
+	std::list<element_type *>mFreeElementList;
+
+	bool is_ptr_in_pool(const element_type *ptr) const
+	{
+		return ((ptr >= mElementPool) && (ptr < mElementPool + pool_size));
+	}
+
+};
+
+}; // namespace nl
+
+#endif // wpantund_ObjectPool_h
diff --git a/src/util/RingBuffer.h b/src/util/RingBuffer.h
new file mode 100644
index 0000000..d842e48
--- /dev/null
+++ b/src/util/RingBuffer.h
@@ -0,0 +1,396 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      Ring-buffer implementation (not thread-safe)
+ *
+ */
+
+#ifndef wpantund_RingBuffer_h
+#define wpantund_RingBuffer_h
+
+#include <stdint.h>
+#include <stdexcept>
+
+namespace nl {
+
+// NOTE: The below implementation of RingBuffer<> is NOT thread-safe.
+
+template <typename T = uint8_t, int I = 512>
+class RingBuffer
+{
+public:
+
+	typedef T value_type;
+	typedef int size_type;
+
+	static const size_type buffer_size = I;
+
+public:
+
+	RingBuffer()
+	{
+		clear();
+	}
+
+	size_type size() const
+	{
+		return mCount;
+	}
+
+	size_type space_available() const
+	{
+		return buffer_size - mCount;
+	}
+
+	bool empty() const
+	{
+		return (mCount == 0);
+	}
+
+	bool full() const
+	{
+		return (mCount == buffer_size);
+	}
+
+	size_type max_size() const
+	{
+		return buffer_size;
+	}
+
+	const value_type* data_ptr()const {
+		return &mBuffer[mReadIdx];
+	}
+
+	size_type size_of_data_ptr()const {
+		size_type ret;
+
+		if (mWriteIdx >= mReadIdx) {
+			ret = mWriteIdx - mReadIdx;
+		} else {
+			ret = buffer_size - mReadIdx;
+		}
+
+		return ret;
+	}
+
+	template <typename S> void
+	push(const value_type* values, S value_count)
+	{
+		if (space_available() < value_count) {
+			throw std::overflow_error("not enough room in ring buffer");
+		}
+
+		while(value_count--) {
+			mBuffer[mWriteIdx] = *values++;
+			mWriteIdx++;
+			mWriteIdx %= buffer_size; // Should be optimized by compiler as a mask
+			mCount++;
+		}
+	}
+
+	template <typename S> size_type
+	pop(S value_count)
+	{
+		size_type bytes_read = size();
+
+		if (bytes_read < value_count) {
+			value_count = bytes_read;
+		} else {
+			bytes_read = static_cast<size_type>(value_count);
+		}
+
+		while (value_count--) {
+			mReadIdx++;
+			mReadIdx %= buffer_size; // Should be optimized by compiler as a mask
+			mCount--;
+		}
+
+		if (mReadIdx == mWriteIdx) {
+			mReadIdx = mWriteIdx = 0;
+		}
+
+		return bytes_read;
+	}
+
+	template <typename S> size_type
+	pull(value_type* values, S value_count)
+	{
+		size_type bytes_read = size();
+
+		if (bytes_read < value_count) {
+			value_count = bytes_read;
+		} else {
+			bytes_read = static_cast<size_type>(value_count);
+		}
+
+		while (value_count--) {
+			*values++ = mBuffer[mReadIdx];
+			mReadIdx++;
+			mReadIdx %= buffer_size; // Should be optimized by compiler as a mask
+			mCount--;
+		}
+
+		return bytes_read;
+	}
+
+	// Returns a pointer to the front (head) element in ring buffer if the ring buffer
+	// is not empty, or returns NULL if buffer is empty.
+	const value_type *front() const
+	{
+		return (mCount == 0)? NULL : &mBuffer[mReadIdx];
+	}
+
+	// Returns a pointer to the back (tail) element in ring buffer if the ring buffer
+	// is not empty, or returns NULL if buffer is empty.
+	const value_type *back() const
+	{
+		if (mCount == 0) {
+			return NULL;
+		}
+
+		if (mWriteIdx == 0)  {
+			return &mBuffer[buffer_size - 1];
+		}
+
+		return &mBuffer[mWriteIdx - 1];
+	}
+
+	// Attempts to write the new value in the ring buffer, if buffer is full
+	// and write fails, returns false, otherwise returns true.
+	bool write(const value_type& value)
+	{
+		if (mCount == buffer_size) {
+			return false;
+		}
+
+		mBuffer[mWriteIdx] = value;
+		mWriteIdx++;
+		mWriteIdx %= buffer_size;
+		mCount++;
+
+		return true;
+	}
+
+	// Force writes the new value in the ring buffer. May overwrite an earlier value and move
+	// the read index forward (if buffer is full).
+	void force_write(const value_type& value)
+	{
+		mBuffer[mWriteIdx] = value;
+		mWriteIdx++;
+		mWriteIdx %= buffer_size;
+
+		if (mCount == buffer_size) {
+			mReadIdx = mWriteIdx;
+		} else {
+			mCount++;
+		}
+	}
+
+	// Reads one element from the head of ring buffer and removes it. If buffer
+	// is empty returns false, otherwise returns true.
+	bool read(value_type& value)
+	{
+		value_type *f = front();
+
+		if (f) {
+			value = *f;
+		}
+
+		return remove();
+	}
+
+	// Removes the element from front of the ring buffer. If buffer is empty
+	// and the remove operation fails, returns false, otherwise returns true.
+	bool remove()
+	{
+		if (mCount == 0) {
+			return false;
+		}
+
+		mReadIdx++;
+		mReadIdx %= buffer_size;
+		mCount--;
+
+		return true;
+	}
+
+	// Clears the ring buffer.
+	void clear()
+	{
+		mReadIdx = mWriteIdx = 0;
+		mCount = 0;
+	}
+
+private:
+	class IteratorBase
+	{
+	protected:
+		IteratorBase(const value_type *ptr, const RingBuffer *ring_buffer_ptr)
+		{
+			mIterPtr = ptr;
+			mRingBufferPtr = ring_buffer_ptr;
+		}
+
+		IteratorBase(const IteratorBase &it)
+		{
+			mIterPtr = it.mIterPtr;
+			mRingBufferPtr = it.mRingBufferPtr;
+		}
+
+	public:
+		bool operator==(const IteratorBase &lhs)
+		{
+			return (mIterPtr == lhs.mIterPtr) && (mRingBufferPtr == lhs.mRingBufferPtr);
+		}
+
+		bool operator!=(const IteratorBase &lhs)
+		{
+			return !((*this) == lhs);
+		}
+
+		const value_type *get_ptr()     { return mIterPtr;  }
+
+		const value_type& operator* ()  { return *mIterPtr; }
+		const value_type* operator-> () { return mIterPtr;  }
+
+	protected:
+		const value_type *mIterPtr;
+		const RingBuffer *mRingBufferPtr;
+	};
+
+public:
+	// An iterator for going through the elements in the ring buffer from front to back.
+	class Iterator : public IteratorBase
+	{
+	public:
+		Iterator() : IteratorBase(NULL, NULL) { }
+		Iterator(const Iterator &it) : IteratorBase(it) { }
+
+		Iterator& operator++() {
+			advance();
+			return *this;
+		}
+
+		Iterator operator++(int val) {
+			(void)val;
+			Iterator it(*this);
+			advance();
+			return it;
+		}
+
+		void advance(void)
+		{
+			const RingBuffer *rb = this->mRingBufferPtr;
+			if (rb)
+			{
+				this->mIterPtr++;
+				if (this->mIterPtr == &rb->mBuffer[rb->buffer_size]) {
+					this->mIterPtr = &rb->mBuffer[0];
+				}
+
+				if (this->mIterPtr == &rb->mBuffer[rb->mWriteIdx]) {
+					this->mIterPtr = NULL;
+				}
+			}
+		}
+
+	private:
+		Iterator(const value_type *ptr, const RingBuffer *ring_buffer_ptr) :
+			IteratorBase(ptr, ring_buffer_ptr) { }
+
+		friend Iterator RingBuffer::begin() const;
+		friend Iterator RingBuffer::end() const;
+	};
+
+	// A reverse iterator for going through the elements in the ring buffer from back to front.
+	class ReverseIterator : public IteratorBase
+	{
+	public:
+		ReverseIterator() : IteratorBase(NULL, NULL) { }
+		ReverseIterator(const Iterator &it) : IteratorBase(it) { }
+
+		ReverseIterator& operator++() {
+			advance();
+			return *this;
+		}
+
+		ReverseIterator operator++(int val) {
+			(void)val;
+			ReverseIterator it(*this);
+			advance();
+			return it;
+		}
+
+		void advance(void)
+		{
+			const RingBuffer *rb = this->mRingBufferPtr;
+			if (rb)
+			{
+				if (this->mIterPtr == &rb->mBuffer[rb->mReadIdx]) {
+					this->mIterPtr = NULL;
+				} else {
+					if (this->mIterPtr == &rb->mBuffer[0]) {
+						this->mIterPtr = &rb->mBuffer[rb->buffer_size - 1];
+					} else {
+						this->mIterPtr--;
+					}
+				}
+			}
+		}
+
+	private:
+		ReverseIterator(const value_type *ptr, const RingBuffer *ring_buffer_ptr) :
+			IteratorBase(ptr, ring_buffer_ptr) { }
+
+		friend ReverseIterator RingBuffer::rbegin() const;
+		friend ReverseIterator RingBuffer::rend() const;
+	};
+
+	// Returns a RingBuffer::Iterator to the beginning of the buffer
+	Iterator begin() const
+	{
+		return Iterator(front(), this);
+	}
+
+	// Returns a RingBuffer::Iterator marking the end/tail of the buffer
+	Iterator end() const
+	{
+		return Iterator(NULL, this);
+	}
+
+	// Returns a RingBuffer::ReverseIterator pointing to back/tail of the buffer.
+	ReverseIterator rbegin() const
+	{
+		return ReverseIterator(back(), this);
+	}
+
+	// Returns a RingBuffer::ReverseIterator pointing to end/head of the buffer.
+	ReverseIterator rend() const
+	{
+		return ReverseIterator(NULL, this);
+	}
+
+private:
+	size_type mReadIdx, mWriteIdx;
+	size_type mCount;
+	value_type mBuffer[buffer_size];
+};
+
+}; // namespace nl
+
+#endif
diff --git a/src/util/SocketAdapter.cpp b/src/util/SocketAdapter.cpp
new file mode 100644
index 0000000..b0b8d62
--- /dev/null
+++ b/src/util/SocketAdapter.cpp
@@ -0,0 +1,129 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      This file contains the implementation of the SocketAdapter class,
+ *      which is a base class for implementing "soft" sockets that
+ *      use other sockets (for things like reliability layers, etc).
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "SocketAdapter.h"
+#include <errno.h>
+
+using namespace nl;
+
+const boost::shared_ptr<SocketWrapper>&
+SocketAdapter::set_parent(boost::shared_ptr<SocketWrapper> parent)
+{
+	return mParent = parent;
+}
+
+const boost::shared_ptr<SocketWrapper>&
+SocketAdapter::get_parent()
+{
+	return mParent;
+}
+
+SocketAdapter::SocketAdapter(boost::shared_ptr<SocketWrapper> parent)
+	:mParent(parent)
+{
+}
+
+int
+SocketAdapter::hibernate(void)
+{
+	return mParent ? mParent->hibernate() : -EINVAL;
+}
+
+ssize_t
+SocketAdapter::write(const void* data, size_t len)
+{
+	return mParent ? mParent->write(data, len) : -EINVAL;
+}
+
+ssize_t
+SocketAdapter::read(void* data, size_t len)
+{
+	return mParent ? mParent->read(data, len) : -EINVAL;
+}
+
+bool
+SocketAdapter::can_read(void)const
+{
+	return mParent ? mParent->can_read() : false;
+}
+
+bool
+SocketAdapter::can_write(void)const
+{
+	return mParent ? mParent->can_write() : false;
+}
+
+int
+SocketAdapter::get_read_fd(void)const
+{
+	return mParent ? mParent->get_read_fd() : -EINVAL;
+}
+
+int
+SocketAdapter::get_write_fd(void)const
+{
+	return mParent ? mParent->get_write_fd() : -EINVAL;
+}
+
+int
+SocketAdapter::process(void)
+{
+	return mParent ? mParent->process() : 0;
+}
+
+void
+SocketAdapter::reset()
+{
+	if(mParent)
+		mParent->reset();
+}
+
+void
+SocketAdapter::send_break()
+{
+	if(mParent)
+		mParent->send_break();
+};
+
+bool
+SocketAdapter::did_reset()
+{
+	return mParent ? mParent->did_reset() : false;
+}
+
+cms_t
+SocketAdapter::get_ms_to_next_event(void)const
+{
+	return mParent ? mParent->get_ms_to_next_event() : CMS_DISTANT_FUTURE;
+}
+
+int
+SocketAdapter::update_fd_set(fd_set *read_fd_set, fd_set *write_fd_set, fd_set *error_fd_set, int *max_fd, cms_t *timeout)
+{
+	return mParent ? mParent->update_fd_set(read_fd_set, write_fd_set, error_fd_set, max_fd, timeout) : 0;
+}
diff --git a/src/util/SocketAdapter.h b/src/util/SocketAdapter.h
new file mode 100644
index 0000000..3e896af
--- /dev/null
+++ b/src/util/SocketAdapter.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      This file contains the declaration of the SocketAdapter class,
+ *      which is a base class for implementing "soft" sockets that
+ *      use other sockets (for things like reliability layers, etc).
+ *
+ */
+
+#ifndef __wpantund__SocketAdapter__
+#define __wpantund__SocketAdapter__
+
+#include "SocketWrapper.h"
+
+namespace nl {
+class SocketAdapter : public SocketWrapper {
+public:
+	virtual ~SocketAdapter() {}
+	virtual const boost::shared_ptr<SocketWrapper>& set_parent(boost::shared_ptr<SocketWrapper> parent);
+	const boost::shared_ptr<SocketWrapper>& get_parent();
+
+	virtual ssize_t write(const void* data, size_t len);
+	virtual ssize_t read(void* data, size_t len);
+	virtual bool can_read(void)const;
+	virtual bool can_write(void)const;
+	virtual int get_read_fd(void)const;
+	virtual int get_write_fd(void)const;
+	virtual int process(void);
+	virtual cms_t get_ms_to_next_event(void)const;
+	virtual void send_break();
+
+	virtual void reset();
+	virtual bool did_reset();
+	virtual int update_fd_set(fd_set *read_fd_set, fd_set *write_fd_set, fd_set *error_fd_set, int *max_fd, cms_t *timeout);
+
+	virtual int hibernate(void);
+
+protected:
+	SocketAdapter(boost::shared_ptr<SocketWrapper> parent);
+	boost::shared_ptr<SocketWrapper> mParent;
+}; // class SocketAdapter
+
+
+}; // namespace nl
+
+#endif /* defined(__wpantund__SocketAdapter__) */
diff --git a/src/util/SocketAsyncOp.h b/src/util/SocketAsyncOp.h
new file mode 100644
index 0000000..cffbefb
--- /dev/null
+++ b/src/util/SocketAsyncOp.h
@@ -0,0 +1,167 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __wpantund__SocketAsyncOp__
+#define __wpantund__SocketAsyncOp__
+
+#include <stdint.h>
+#include "SocketWrapper.h"
+#include "nlpt.h"
+#include <errno.h>
+
+namespace nl {
+
+static inline int
+read_stream_pt(struct nlpt *pt, nl::SocketWrapper* socket, void* data, size_t len)
+{
+	const int fd = socket->get_read_fd();
+
+	PT_BEGIN(&pt->sub_pt);
+	pt->byte_count = 0;
+	pt->last_errno = 0;
+
+	while (pt->byte_count < len) {
+		ssize_t bytes_read;
+
+		// Wait for the socket to become readable...
+		_nlpt_setup_read_fd_source(pt, fd);
+		PT_WAIT_UNTIL(&pt->sub_pt, nlpt_hook_check_read_fd_source(pt, fd) || socket->can_read());
+		_nlpt_cleanup_read_fd_source(pt, fd);
+
+		// Read what is left of the packet on the socket.
+		bytes_read = socket->read(
+			static_cast<void*>(static_cast<uint8_t*>(data) + pt->byte_count),
+			len - pt->byte_count
+		);
+
+		if (0 > bytes_read) {
+			pt->last_errno = errno;
+			break;
+		}
+
+		pt->byte_count += bytes_read;
+	}
+
+	PT_END(&pt->sub_pt);
+}
+
+static inline int
+write_stream_pt(struct nlpt *pt, nl::SocketWrapper* socket, const void* data, size_t len)
+{
+	const int fd = socket->get_write_fd();
+
+	PT_BEGIN(&pt->sub_pt);
+	pt->byte_count = 0;
+	pt->last_errno = 0;
+
+	while (pt->byte_count < len) {
+		ssize_t bytes_written;
+
+		// Wait for the socket to become writable...
+		_nlpt_setup_write_fd_source(pt, fd);
+		PT_WAIT_UNTIL(&pt->sub_pt, nlpt_hook_check_write_fd_source(pt, fd) || socket->can_write());
+		_nlpt_cleanup_write_fd_source(pt, fd);
+
+		// Attempt to write out what is left of the packet to the socket.
+		bytes_written = socket->write(
+			static_cast<const void*>(static_cast<const uint8_t*>(data) + pt->byte_count),
+			len - pt->byte_count
+		);
+
+		if (0 > bytes_written) {
+			pt->last_errno = errno;
+			break;
+		}
+
+		pt->byte_count += bytes_written;
+	}
+
+	PT_END(&pt->sub_pt);
+}
+
+static inline int
+write_packet_pt(struct nlpt *pt, nl::SocketWrapper* socket, const void* data, size_t len)
+{
+	const int fd = socket->get_write_fd();
+
+	PT_BEGIN(&pt->sub_pt);
+	ssize_t bytes_written;
+	pt->byte_count = 0;
+	pt->last_errno = 0;
+
+	// Wait for the socket to become writable...
+	_nlpt_setup_write_fd_source(pt, fd);
+	PT_WAIT_UNTIL(&pt->sub_pt, nlpt_hook_check_write_fd_source(pt, fd) || socket->can_write());
+	_nlpt_cleanup_write_fd_source(pt, fd);
+
+	// Write out the packet
+	bytes_written = socket->write(
+		static_cast<const void*>(static_cast<const uint8_t*>(data) + pt->byte_count),
+		len - pt->byte_count
+	);
+
+	if (0 > bytes_written) {
+		pt->last_errno = errno;
+	} else {
+		pt->byte_count += bytes_written;
+	}
+
+	PT_END(&pt->sub_pt);
+}
+
+#define NLPT_ASYNC_READ_STREAM(pt, sock, data, len) \
+		PT_SPAWN( \
+			&(pt)->pt, \
+			&(pt)->sub_pt, \
+			::nl::read_stream_pt( \
+				(pt), \
+				(sock), \
+				static_cast<void*>(data), \
+				(len) \
+			) \
+		)
+
+#define NLPT_ASYNC_WRITE_STREAM(pt, sock, data, len) \
+		PT_SPAWN( \
+			&(pt)->pt, \
+			&(pt)->sub_pt, \
+			::nl::write_stream_pt( \
+				(pt), \
+				(sock), \
+				static_cast<const void*>(data), \
+				(len) \
+			) \
+		)
+
+#define NLPT_ASYNC_WRITE_PACKET(pt, sock, data, len) \
+		PT_SPAWN( \
+			&(pt)->pt, \
+			&(pt)->sub_pt, \
+			::nl::write_packet_pt( \
+				(pt), \
+				(sock), \
+				static_cast<const void*>(data), \
+				(len) \
+			) \
+		)
+
+}; // namespace nl
+
+
+#endif /* defined(__wpantund__SocketAsyncOp__) */
diff --git a/src/util/SocketWrapper.cpp b/src/util/SocketWrapper.cpp
new file mode 100644
index 0000000..ec42008
--- /dev/null
+++ b/src/util/SocketWrapper.cpp
@@ -0,0 +1,111 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      This file contains the default implementations for certain members
+ *      of the SocketWrapper class, which is the virtual base class for using
+ *      things like TCP sockets, serial file descriptors, or even other
+ *      processes.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "SocketWrapper.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <termios.h>
+#include <poll.h>
+#include <errno.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+using namespace nl;
+
+SocketWrapper::~SocketWrapper()
+{
+}
+
+bool
+SocketWrapper::can_read(void)const
+{
+	return false;
+}
+
+bool
+SocketWrapper::can_write(void)const
+{
+	return false;
+}
+
+int
+SocketWrapper::set_log_level(int log_level)
+{
+	return -ENOTSUP;
+}
+
+int
+SocketWrapper::get_read_fd(void)const
+{
+	return -1;
+}
+
+int
+SocketWrapper::get_write_fd(void)const
+{
+	return -1;
+}
+
+cms_t
+SocketWrapper::get_ms_to_next_event(void)const
+{
+	return CMS_DISTANT_FUTURE;
+}
+
+void
+SocketWrapper::send_break(void)
+{
+}
+
+void
+SocketWrapper::reset(void)
+{
+}
+
+int
+SocketWrapper::hibernate(void)
+{
+	return -1;
+}
+
+bool
+SocketWrapper::did_reset(void)
+{
+	return false;
+}
+
+int
+SocketWrapper::update_fd_set(fd_set *read_fd_set, fd_set *write_fd_set, fd_set *error_fd_set, int *max_fd, cms_t *timeout)
+{
+	if (timeout != NULL) {
+		*timeout = std::min(*timeout, get_ms_to_next_event());
+	}
+	return 0;
+}
diff --git a/src/util/SocketWrapper.h b/src/util/SocketWrapper.h
new file mode 100644
index 0000000..449f934
--- /dev/null
+++ b/src/util/SocketWrapper.h
@@ -0,0 +1,70 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      This file declares the SocketWrapper class, which is the virtual
+ *      base class for using things like TCP sockets, serial file descriptors,
+ *      or even other processes.
+ *
+ */
+
+#ifndef __wpantund__SocketWrapper__
+#define __wpantund__SocketWrapper__
+
+#include <stdint.h>
+#include <cstring>
+#include <boost/shared_ptr.hpp>
+#include <climits>
+#include <string>
+#include <sys/select.h>
+#include "time-utils.h"
+
+namespace nl {
+
+class SocketWrapper {
+public:
+	virtual ~SocketWrapper();
+	virtual ssize_t write(const void* data, size_t len) = 0;
+	virtual ssize_t read(void* data, size_t len) = 0;
+	virtual bool can_read(void)const;
+	virtual bool can_write(void)const;
+	virtual int process(void) = 0;
+	virtual int set_log_level(int log_level);
+
+	virtual int get_read_fd(void)const;
+	virtual int get_write_fd(void)const;
+	virtual cms_t get_ms_to_next_event(void)const;
+
+	virtual void send_break(void);
+
+	virtual void reset(void);
+	virtual bool did_reset(void);
+
+	//! Any ancilary file descriptors to update. Only really needed for adapters.
+	virtual int update_fd_set(fd_set *read_fd_set, fd_set *write_fd_set, fd_set *error_fd_set, int *max_fd, cms_t *timeout);
+
+public:
+	//! Special function which closes the file descriptors. Not supported on all sockets. Call `reset()` to undo.
+	virtual int hibernate(void);
+
+}; // class SocketWrapper
+
+
+}; // namespace nl
+
+
+#endif /* defined(__wpantund__SocketWrapper__) */
diff --git a/src/util/SuperSocket.cpp b/src/util/SuperSocket.cpp
new file mode 100644
index 0000000..402c89e
--- /dev/null
+++ b/src/util/SuperSocket.cpp
@@ -0,0 +1,125 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      This file defindes the SuperSocket class, which provides an
+ *      easy way to create various types of sockets using a convenient
+ *      path syntax.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "assert-macros.h"
+
+#include "SuperSocket.h"
+#include "socket-utils.h"
+#include "time-utils.h"
+#include <stdexcept>
+#include <syslog.h>
+#include <errno.h>
+#include <unistd.h>
+#include <termios.h>
+#include <sys/file.h>
+
+using namespace nl;
+
+SuperSocket::SuperSocket(const std::string& path)
+	:UnixSocket(-1, false), mPath(path)
+{
+	int fd = open_serial_socket(path.c_str());
+
+	mFDRead = mFDWrite = fd;
+
+	if (0 > fd) {
+		syslog(LOG_ERR, "Unable to open socket with path <%s>, errno=%d (%s)", path.c_str(), errno, strerror(errno));
+		throw std::runtime_error("Unable to open socket");
+	}
+
+	// Lock the file descriptor. It does not make sense to allow someone else
+	// to use this file descriptor at the same time, or to use the device
+	// while someone else is using it.
+	if (flock(fd, LOCK_EX|LOCK_NB) < 0) {
+		// The only error we care about is EWOULDBLOCK. EINVAL is fine,
+		// it just means this file descriptor doesn't support locking.
+		if (EWOULDBLOCK == errno) {
+			syslog(LOG_ERR, "Socket is locked by another process");
+			throw std::runtime_error("Socket is locked by another process");
+		}
+	}
+}
+
+boost::shared_ptr<SocketWrapper>
+SuperSocket::create(const std::string& path)
+{
+	return boost::shared_ptr<SocketWrapper>(new SuperSocket(path));
+}
+
+int
+SuperSocket::hibernate(void)
+{
+	syslog(LOG_DEBUG, "SuperSocket::hibernate()");
+
+	if (mFDRead >= 0) {
+		// Unlock the FD.
+		IGNORE_RETURN_VALUE(flock(mFDRead, LOCK_UN));
+
+		// Close the existing FD.
+		IGNORE_RETURN_VALUE(close(mFDRead));
+	}
+
+	mFDRead = -1;
+	mFDWrite = -1;
+
+	return 0;
+}
+
+void
+SuperSocket::reset()
+{
+	syslog(LOG_DEBUG, "SuperSocket::reset()");
+
+	// Unlock the FD.
+	IGNORE_RETURN_VALUE(flock(mFDRead, LOCK_UN));
+
+	// Close the existing FD.
+	IGNORE_RETURN_VALUE(close(mFDRead));
+
+	// Sleep for 200ms to wait for things to settle down.
+	usleep(MSEC_PER_SEC * 200);
+
+	mFDRead = mFDWrite = open_serial_socket(mPath.c_str());
+	if (mFDRead < 0) {
+		// Unable to reopen socket...!
+		syslog(LOG_ERR, "SuperSocket::Reset: Unable to reopen socket <%s>, errno=%d (%s)", mPath.c_str(), errno, strerror(errno));
+		throw std::runtime_error("Unable to reopen socket");
+	}
+
+	// Lock the file descriptor. It does not make sense to allow someone else
+	// to use this file descriptor at the same time, or to use the device
+	// while someone else is using it.
+	if (flock(mFDRead, LOCK_EX|LOCK_NB) < 0) {
+		// The only error we care about is EWOULDBLOCK. EINVAL is fine,
+		// it just means this file descriptor doesn't support locking.
+		if (EWOULDBLOCK == errno) {
+			syslog(LOG_ERR, "Socket is locked by another process");
+			throw std::runtime_error("Socket is locked by another process");
+		}
+	}
+}
diff --git a/src/util/SuperSocket.h b/src/util/SuperSocket.h
new file mode 100644
index 0000000..e3308d7
--- /dev/null
+++ b/src/util/SuperSocket.h
@@ -0,0 +1,48 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      This file declares the SuperSocket class, which provides an
+ *      easy way to create various types of sockets using a convenient
+ *      path syntax.
+ *
+ */
+
+#ifndef __wpantund__SuperSocket__
+#define __wpantund__SuperSocket__
+
+#include "UnixSocket.h"
+
+namespace nl {
+class SuperSocket : public UnixSocket {
+protected:
+	SuperSocket(const std::string& path);
+
+public:
+	static boost::shared_ptr<SocketWrapper> create(const std::string& path);
+
+	virtual void reset();
+
+	virtual int hibernate(void);
+
+protected:
+	std::string mPath;
+}; // class SuperSocket
+
+}; // namespace nl
+
+#endif /* defined(__wpantund__SuperSocket__) */
diff --git a/src/util/Timer.cpp b/src/util/Timer.cpp
new file mode 100644
index 0000000..c3cad79
--- /dev/null
+++ b/src/util/Timer.cpp
@@ -0,0 +1,224 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      Implementation of callback timer.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "assert-macros.h"
+#include "Timer.h"
+
+using namespace nl;
+
+const Timer::Interval Timer::kOneMilliSecond = 1;
+const Timer::Interval Timer::kOneSecond      = Timer::kOneMilliSecond * 1000;
+const Timer::Interval Timer::kOneMinute      = Timer::kOneSecond * 60;
+const Timer::Interval Timer::kOneHour        = Timer::kOneMinute * 60;
+const Timer::Interval Timer::kOneDay         = Timer::kOneHour * 24;
+
+Timer *const Timer::kListEndMarker = (Timer *)(&Timer::mListHead);
+Timer *Timer::mListHead = kListEndMarker;
+
+static void
+null_timer_callback(Timer *timer)
+{
+	return;
+}
+
+Timer::Timer() :
+	mFireTime()
+{
+	mNext = NULL;
+	mCallback = &null_timer_callback;
+}
+
+Timer::~Timer()
+{
+	remove(this);
+}
+
+void
+Timer::schedule(Interval interval, const Timer::Callback& callback, Type type)
+{
+	remove(this);
+
+	require_string(interval > 0, bail, "Timer::schedule() - Input interval is invalid (is 0 or negative).");
+
+	mInterval = interval;
+	mCallback = callback;
+	mType = type;
+
+	mFireTime.set(interval + time_ms());
+	add(this);
+
+bail:
+	return;
+}
+
+void
+Timer::cancel(void)
+{
+	remove(this);
+}
+
+bool
+Timer::is_expired(void) const
+{
+	return mFireTime.is_now_or_in_past();
+}
+
+Timer::Interval
+Timer::get_interval(void) const
+{
+	return mInterval;
+}
+
+Timer::Type
+Timer::get_type(void) const
+{
+	return mType;
+}
+
+void
+Timer::add(Timer *timer)
+{
+	Timer *cur;
+
+	// If the list is empty, insert the new timer as the new head of the list.
+	if (mListHead == kListEndMarker) {
+		timer->mNext = kListEndMarker;
+		mListHead = timer;
+		return;
+	}
+
+	// If the new timer fire-time is before the head timer's fire-time, insert it as the new head.
+	if (timer->mFireTime < mListHead->mFireTime) {
+		timer->mNext = mListHead;
+		mListHead = timer;
+		return;
+	}
+
+	// Go through the list and find the proper place to insert the new timer.
+	for (cur = mListHead; cur->mNext != kListEndMarker; cur = cur->mNext) {
+		if (timer->mFireTime < cur->mNext->mFireTime) {
+			// Inset the new timer in between cur and cur->next
+			timer->mNext = cur->mNext;
+			cur->mNext = timer;
+			return;
+		}
+	}
+
+	// New timer's fire-time is after all other timers in the list, so insert it at the end.
+	timer->mNext = kListEndMarker;
+	cur->mNext = timer;
+}
+
+void
+Timer::remove(Timer *timer)
+{
+	Timer *cur;
+
+	// If timer is not on the list, there is nothing to do.
+	if (timer->mNext == NULL) {
+		goto bail;
+	}
+
+	// If timer is the head of the list, remove it and update the head.
+	if (mListHead == timer) {
+		mListHead = timer->mNext;
+		timer->mNext = NULL;
+		goto bail;
+	}
+
+	// Go through the list and find the timer.
+	for (cur = mListHead; cur != kListEndMarker; cur = cur->mNext) {
+		if (cur->mNext == timer) {
+			cur->mNext = timer->mNext;
+			timer->mNext = NULL;
+			goto bail;
+		}
+	}
+
+	// We should never get here, as the timer with a non-null mNext should be on the list
+	// If we get here, assert and bail.
+	require_string(false, bail, "Timer::remove() - Timer was not found on the list.");
+
+bail:
+	return;
+}
+
+int
+Timer::process(void)
+{
+	Timer *timer;
+
+	// Process all expired timers
+	for (timer = mListHead; (timer != kListEndMarker) && timer->is_expired(); timer = mListHead) {
+		// Remove the timer from the list and update the head.
+		mListHead = timer->mNext;
+		timer->mNext = NULL;
+
+		// Restart the timer if it is periodic.
+		if (timer->mType == kPeriodicFixedRate) {
+			// For fixed-rate, restart the timer from last fire time.
+			timer->mFireTime.set(timer->mInterval + timer->mFireTime.get());
+			add(timer);
+		} else if (timer->mType == kPeriodicFixedDelay) {
+			// For fixed-delay, restart the timer from now.
+			timer->mFireTime.set(timer->mInterval + time_ms());
+			add(timer);
+		}
+
+		// Invoke the callback.
+		timer->mCallback(timer);
+	}
+
+	return 0;
+}
+
+int
+Timer::update_timeout(cms_t *timeout)
+{
+	if (timeout != NULL) {
+		cms_t cms = get_ms_to_next_event();
+
+		if (cms < *timeout) {
+			*timeout = cms;
+		}
+	}
+
+	return 0;
+}
+
+cms_t
+Timer::get_ms_to_next_event(void)
+{
+	cms_t cms = CMS_DISTANT_FUTURE;
+
+	if (mListHead != kListEndMarker) {
+		cms = mListHead->mFireTime.get_ms_till_time();
+		if (cms < 0) {
+			cms = 0;
+		}
+	}
+	return cms;
+}
diff --git a/src/util/Timer.h b/src/util/Timer.h
new file mode 100644
index 0000000..7c33096
--- /dev/null
+++ b/src/util/Timer.h
@@ -0,0 +1,128 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      This file declares a callback timer module for use in wpantund.
+ *
+ */
+
+#ifndef __wpantund__Timer__
+#define __wpantund__Timer__
+
+#include <list>
+#include <boost/function.hpp>
+
+#include "time-utils.h"
+
+namespace nl {
+
+// A callback timer class for use in wpantund.
+class Timer {
+public:
+	// Timer interval in ms - defined as int32_t --> can go up to 24.8 days
+	typedef cms_t Interval;
+
+	// A timer callback
+	typedef boost::function<void (Timer *)> Callback;
+
+	// Timer type
+	enum Type {
+		kOneShot,              // One-shot timer (runs once and expires)
+		kPeriodicFixedRate,    // Restart the timer from last fire time
+		kPeriodicFixedDelay    // Restart the timer after processing the timer
+	};
+
+	// Time interval constants
+	static const Interval kOneMilliSecond;
+	static const Interval kOneSecond;
+	static const Interval kOneMinute;
+	static const Interval kOneHour;
+	static const Interval kOneDay;
+
+public:
+	Timer();
+	virtual ~Timer();
+
+	// Schedules the timer with given interval/period. At fire-time the given callback is invoked.
+	//    Three types of timers are supported: One-shot, fixed-rate periodic, fixed-delay periodic.
+	//    Timer gets started at the time of the call to schedule(). For periodic timer, the first
+	//    invocation of the callback happens after the first interval/period expires.
+	//    A subsequent call to schedule() will stop an already running timer and overwrite all the
+	//    timer parameters (interval, callback, and its type), basically starting a new timer.
+	void schedule(Interval interval, const Callback &callback, Type type = kOneShot);
+
+	// Cancels/Stops the timer (callback will not be invoked).
+	//    If timer is already expired or stopped, calling cancel() does nothing (and is ok).
+	void cancel(void);
+
+	// Returns true if the timer is expired or not running, otherwise returns false.
+	bool is_expired(void) const;
+
+	// Returns the current interval/period of the timer
+	Interval get_interval(void) const;
+
+	// Returns the type of the timer.
+	Type get_type(void) const;
+
+
+public:
+	static int process(void);
+	static int update_timeout(cms_t *timeout);
+
+private:
+	struct ClockTime {
+	public:
+		ClockTime()           { mTime = 0;    }
+		ClockTime(cms_t time) { mTime = time; }
+
+		void set(cms_t time)  { mTime = time; }
+		cms_t get(void) 	  { return mTime; }
+
+		bool operator<(const ClockTime& lhs)  const  { return (mTime - lhs.mTime) < 0;  }
+		bool operator<=(const ClockTime& lhs) const  { return (mTime - lhs.mTime) <= 0; }
+		bool operator==(const ClockTime &lhs) const  { return (mTime == lhs.mTime);     }
+
+		bool is_now_or_in_past(void)  const    { return (*this) <= now(); }
+		bool is_in_future(void) const          { return now() < (*this); }
+
+		cms_t get_ms_till_time(void) const     { return mTime - time_ms(); }
+
+		static ClockTime now(void)  { return ClockTime(time_ms()); }
+
+	private:
+		cms_t mTime;
+	};
+
+	ClockTime mFireTime;
+	cms_t mInterval;
+	Callback mCallback;
+	Type mType;
+	Timer *mNext;       // for linked-list
+
+private:
+	static void remove(Timer *timer);       // Removes timer from linked-list
+	static void add(Timer *timer);          // Adds timer to list (list sorted based on fire-time)
+
+	static cms_t get_ms_to_next_event(void);
+
+	static Timer * mListHead;   			// Head of the LinkedList
+	static Timer * const kListEndMarker;	// Marker for end of list
+};
+
+}; // namespace nl
+
+#endif  // ifndef __wpantund__Timer__
diff --git a/src/util/TunnelIPv6Interface.cpp b/src/util/TunnelIPv6Interface.cpp
new file mode 100644
index 0000000..a9497b4
--- /dev/null
+++ b/src/util/TunnelIPv6Interface.cpp
@@ -0,0 +1,479 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      This file contains the implementation for a C++ wrapper around the
+ *      `tunnel.c`/`tunnel.h` interface.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "assert-macros.h"
+#include "TunnelIPv6Interface.h"
+#include <syslog.h>
+#include "IPv6Helpers.h"
+
+#if __linux__
+#include <asm/types.h>
+#include <linux/if_link.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <fcntl.h>
+#endif
+
+#include <sys/select.h>
+
+TunnelIPv6Interface::TunnelIPv6Interface(const std::string& interface_name, int mtu):
+	UnixSocket(tunnel_open(interface_name.c_str()), true),
+	mInterfaceName(interface_name),
+	mLastError(0),
+	mNetlinkFD(-1),
+	mHardwareAddress(),
+	mRealmLocalAddress(),
+	mRealmLocalPrefixSize(64)
+{
+	if (0 > mFDRead) {
+		throw std::invalid_argument("Unable to open tunnel interface");
+	}
+
+	{
+		char ActualInterfaceName[TUNNEL_MAX_INTERFACE_NAME_LEN] = "";
+		int ret = 0;
+		ret = tunnel_get_name(mFDRead, ActualInterfaceName, sizeof(ActualInterfaceName));
+		if (ret) {
+			syslog(LOG_WARNING,
+				   "TunnelIPv6Interface: Couldn't get tunnel name! errno=%d, %s",
+				   errno,
+				   strerror(errno));
+		} else if (mInterfaceName != ActualInterfaceName) {
+			syslog(LOG_WARNING,
+				   "TunnelIPv6Interface: Couldn't create tunnel named \"%s\", got \"%s\" instead!",
+				   mInterfaceName.c_str(),
+				   ActualInterfaceName);
+			mInterfaceName = ActualInterfaceName;
+		}
+	}
+
+	tunnel_set_mtu(mFDRead, mtu);
+
+	setup_signals();
+}
+
+TunnelIPv6Interface::~TunnelIPv6Interface()
+{
+	close(mNetlinkFD);
+}
+
+#if __linux__ // --------------------------------------------------------------
+
+#define LCG32(x)		((uint32_t)(x)*1664525+1013904223)
+
+void
+TunnelIPv6Interface::setup_signals()
+{
+	int status;
+	int fd;
+	struct sockaddr_nl la;
+
+	fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+
+	require(fd != -1, bail);
+
+	memset(&la, 0, sizeof(la));
+	la.nl_family = AF_NETLINK;
+	la.nl_pad = 0;
+	la.nl_groups = RTMGRP_LINK | RTMGRP_IPV6_IFADDR;
+
+	// We calculate the PID in a pseudo-random way based on the
+	// address pointer and the actual process ID.
+	la.nl_pid = LCG32(getpid()) ^ LCG32((uint32_t)reinterpret_cast<uintptr_t>(this));
+
+	status = bind(fd, (struct sockaddr*) &la, sizeof(la));
+
+	require(status != -1, bail);
+
+	// Success!
+	IGNORE_RETURN_VALUE(fcntl(fd, F_SETFL, FNDELAY));
+
+	mNetlinkFD = fd;
+	fd = -1;
+
+bail:
+
+	// Cleanup (If necessary)
+	if (fd > 0) {
+		close(fd);
+	}
+
+	return;
+}
+
+int
+TunnelIPv6Interface::process(void)
+{
+	uint8_t buffer[1024];
+	ssize_t buffer_len(-1);
+
+	if (mNetlinkFD >= 0) {
+		buffer_len = recv(mNetlinkFD, buffer, sizeof(buffer), 0);
+	}
+
+	if (buffer_len > 0) {
+		struct nlmsghdr *nlp;
+		struct rtmsg *rtp;
+		int rta_len;
+		struct rtattr *rta;
+
+		nlp = (struct nlmsghdr *)buffer;
+		for (;NLMSG_OK(nlp, buffer_len); nlp=NLMSG_NEXT(nlp, buffer_len))
+		{
+			if (nlp->nlmsg_type == RTM_NEWADDR || nlp->nlmsg_type == RTM_DELADDR) {
+				struct ifaddrmsg *ifaddr = (struct ifaddrmsg *)NLMSG_DATA(nlp);
+				char ifnamebuf[IF_NAMESIZE];
+				const char *ifname = if_indextoname(ifaddr->ifa_index, ifnamebuf);
+				struct in6_addr addr;
+
+				if ((ifname == NULL) || (get_interface_name() != ifname)) {
+					continue;
+				}
+
+				// get RTNETLINK message header
+				// get start of attributes
+				rta = (struct rtattr *) IFA_RTA(ifaddr);
+
+				// get length of attributes
+				rta_len = IFA_PAYLOAD(nlp);
+
+				for(;RTA_OK(rta, rta_len); rta = RTA_NEXT(rta, rta_len)) {
+					switch(rta->rta_type) {
+					case IFA_ADDRESS:
+					case IFA_LOCAL:
+					case IFA_BROADCAST:
+					case IFA_ANYCAST:
+						memcpy(addr.s6_addr, RTA_DATA(rta), sizeof(addr));
+
+						if (nlp->nlmsg_type == RTM_NEWADDR) {
+							mAddressWasAdded(addr, ifaddr->ifa_prefixlen);
+						} else if (nlp->nlmsg_type == RTM_DELADDR) {
+							mAddressWasRemoved(addr, ifaddr->ifa_prefixlen);
+						}
+						break;
+					default:
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	return nl::UnixSocket::process();
+}
+
+#else // ----------------------------------------------------------------------
+
+void
+TunnelIPv6Interface::setup_signals()
+{
+	// Unknown platform.
+}
+
+int
+TunnelIPv6Interface::process(void)
+{
+	return nl::UnixSocket::process();
+}
+
+#endif // ---------------------------------------------------------------------
+
+int
+TunnelIPv6Interface::update_fd_set(fd_set *read_fd_set, fd_set *write_fd_set, fd_set *error_fd_set, int *max_fd, cms_t *timeout)
+{
+	if (read_fd_set && (mNetlinkFD >= 0)) {
+		FD_SET(mNetlinkFD, read_fd_set);
+
+		if ((max_fd != NULL)) {
+			*max_fd = std::max(*max_fd, mNetlinkFD);
+		}
+	}
+
+	return nl::UnixSocket::update_fd_set(read_fd_set, write_fd_set, error_fd_set, max_fd, timeout);
+}
+
+const std::string&
+TunnelIPv6Interface::get_interface_name(void)
+{
+	return mInterfaceName;
+}
+
+int
+TunnelIPv6Interface::get_last_error(void)
+{
+	return mLastError;
+}
+
+bool
+TunnelIPv6Interface::is_online(void)
+{
+	return tunnel_is_online(mFDRead);
+}
+
+int
+TunnelIPv6Interface::set_online(bool online)
+{
+	int ret = 0;
+
+	if (online) {
+		syslog(LOG_INFO, "Bringing interface %s online. . .", mInterfaceName.c_str());
+
+		require_action((ret = tunnel_bring_online(mFDRead)) == 0, bail, mLastError = errno);
+		require_action(tunnel_is_online(mFDRead), bail, mLastError = errno);
+
+		(void)tunnel_set_hw_address(mFDRead, mHardwareAddress);
+
+		require_action_string(tunnel_is_online(mFDRead), bail, mLastError = errno, "Tunnel went offline unexpectedly!");
+
+		if (!IN6_IS_ADDR_UNSPECIFIED(&mRealmLocalAddress)) {
+			(void)tunnel_add_address(mFDRead, mRealmLocalAddress.s6_addr, mRealmLocalPrefixSize);
+		}
+
+		std::set<struct in6_addr>::const_iterator iter;
+		for (iter = mAddresses.begin(); iter != mAddresses.end(); ++iter) {
+			(void)tunnel_add_address(mFDRead, iter->s6_addr, 64);
+		}
+	} else {
+		syslog(LOG_INFO, "Taking interface %s offline. . .", mInterfaceName.c_str());
+
+		require_action((ret = tunnel_bring_offline(mFDRead)) == 0, bail, mLastError = errno);
+	}
+
+bail:
+	return ret;
+}
+
+void
+TunnelIPv6Interface::reset(void)
+{
+	syslog(LOG_INFO, "Resetting interface %s. . .", mInterfaceName.c_str());
+
+	if (!IN6_IS_ADDR_UNSPECIFIED(&mRealmLocalAddress)) {
+		remove_address(&mRealmLocalAddress, mRealmLocalPrefixSize);
+		memset((void*)&mRealmLocalAddress, 0, sizeof(mRealmLocalAddress));
+		mRealmLocalPrefixSize = 0;
+	}
+
+	mAddresses.clear();
+
+	set_online(false);
+	set_hardware_address(mHardwareAddress);
+}
+
+bool
+TunnelIPv6Interface::set_hardware_address(const uint8_t addr[8])
+{
+	bool ret = false;
+
+	if (memcmp(mHardwareAddress, addr, sizeof(mHardwareAddress)) != 0) {
+		require_noerr(tunnel_set_hw_address(mFDRead, addr), bail);
+
+		memcpy(mHardwareAddress, addr, sizeof(mHardwareAddress));
+	}
+
+	ret = true;
+bail:
+	return ret;
+}
+
+const uint8_t*
+TunnelIPv6Interface::get_hardware_address(void)const
+{
+	return mHardwareAddress;
+}
+
+bool
+TunnelIPv6Interface::add_address(const struct in6_addr *addr, int prefixlen)
+{
+	bool ret = false;
+
+	syslog(
+		LOG_INFO,
+	   "TunnelIPv6Interface: Adding address \"%s\" to interface \"%s\".",
+	   in6_addr_to_string(*addr).c_str(),
+	   mInterfaceName.c_str()
+	);
+
+	if (is_online()) {
+		require_noerr_action(tunnel_add_address(mFDRead, addr->s6_addr, prefixlen), bail, mLastError = errno);
+	}
+
+	mAddresses.insert(*addr);
+
+	ret = true;
+
+bail:
+	return ret;
+
+}
+
+bool
+TunnelIPv6Interface::set_realm_local_address(const struct in6_addr *addr, int prefixlen)
+{
+	bool ret = false;
+
+	if (is_online() && (mRealmLocalAddress != *addr)) {
+		require(ret = add_address(addr, prefixlen), bail);
+
+		if (!IN6_IS_ADDR_UNSPECIFIED(&mRealmLocalAddress)) {
+			remove_address(&mRealmLocalAddress, mRealmLocalPrefixSize);
+		}
+	}
+
+	mRealmLocalAddress = *addr;
+	mRealmLocalPrefixSize = prefixlen;
+
+	ret = true;
+
+bail:
+	return ret;
+}
+
+const struct in6_addr&
+TunnelIPv6Interface::get_realm_local_address()const
+{
+	return mRealmLocalAddress;
+}
+
+bool
+TunnelIPv6Interface::remove_address(const struct in6_addr *addr, int prefixlen)
+{
+	bool ret = false;
+
+	syslog(
+		LOG_INFO,
+	   "TunnelIPv6Interface: Removing address \"%s\" from interface \"%s\".",
+	   in6_addr_to_string(*addr).c_str(),
+	   mInterfaceName.c_str()
+	);
+
+	mAddresses.erase(*addr);
+
+	require_action(!IN6_IS_ADDR_UNSPECIFIED(addr), bail, mLastError = EINVAL);
+
+	require_action(!IN6_IS_ADDR_LINKLOCAL(addr), bail, mLastError = EINVAL);
+
+	if (tunnel_remove_address(mFDRead, addr->s6_addr) != 0) {
+		mLastError = errno;
+		goto bail;
+	}
+
+	if (mRealmLocalAddress == *addr) {
+		memset(mRealmLocalAddress.s6_addr, 0, sizeof(mRealmLocalAddress));
+	}
+
+	ret = true;
+
+bail:
+	return ret;
+
+}
+
+bool
+TunnelIPv6Interface::add_route(const struct in6_addr *route, int prefixlen)
+{
+	bool ret = false;
+
+	syslog(
+		LOG_INFO,
+		"TunnelIPv6Interface: Adding route prefix \"%s/%d\" -> \"%s\".",
+		in6_addr_to_string(*route).c_str(),
+		prefixlen,
+		mInterfaceName.c_str()
+	);
+
+	if (is_online()) {
+		require_noerr_action(tunnel_add_route(mFDRead, route->s6_addr, prefixlen), bail, mLastError = errno);
+	}
+
+	ret = true;
+
+bail:
+	return ret;
+}
+
+bool
+TunnelIPv6Interface::remove_route(const struct in6_addr *route, int prefixlen)
+{
+	bool ret = false;
+
+	syslog(
+		LOG_INFO,
+		"TunnelIPv6Interface: Removing route prefix \"%s/%d\" -> \"%s\".",
+		in6_addr_to_string(*route).c_str(),
+		prefixlen,
+		mInterfaceName.c_str()
+	);
+
+	if (is_online()) {
+		require_noerr_action(tunnel_remove_route(mFDRead, route->s6_addr, prefixlen), bail, mLastError = errno);
+	}
+
+	ret = true;
+
+bail:
+	return ret;
+}
+
+ssize_t
+TunnelIPv6Interface::read(void* data, size_t len)
+{
+	ssize_t ret = nl::UnixSocket::read(data, len);
+	uint8_t *data_bytes = static_cast<uint8_t*>(data);
+
+	// Remove any subheader, if present.
+	if ((ret >= 4) && (data_bytes[0] == 0) && (data_bytes[1] == 0)) {
+		ret -= 4;
+		memmove(data, static_cast<const void*>(data_bytes + 4), ret);
+	}
+
+	return ret;
+}
+
+ssize_t
+TunnelIPv6Interface::write(const void* data, size_t len)
+{
+#ifdef __APPLE__
+	const uint8_t* const data_bytes = static_cast<const uint8_t*>(data);
+
+	if ((data_bytes[0] != 0) || (data_bytes[0] != 0)) {
+		// The utun interface on OS X needs this header.
+		// Linux seems to be able to infer the type of the packet
+		// with no problems.
+		uint8_t packet[len + 4];
+		packet[0] = 0;
+		packet[1] = 0;
+		packet[2] = (PF_INET6 << 8) & 0xFF;
+		packet[3] = (PF_INET6 << 0) & 0xFF;
+		memcpy(static_cast<void*>(packet + 4), data, len);
+		ssize_t ret = nl::UnixSocket::write(packet, len + 4);
+		return (ret >= 4)?(ret - 4):(-1);
+	}
+#endif
+	return nl::UnixSocket::write(data, len);
+}
diff --git a/src/util/TunnelIPv6Interface.h b/src/util/TunnelIPv6Interface.h
new file mode 100644
index 0000000..566e5eb
--- /dev/null
+++ b/src/util/TunnelIPv6Interface.h
@@ -0,0 +1,93 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      This file contains the interface for a C++ wrapper around the
+ *      `tunnel.c`/`tunnel.h` interface.
+ *
+ */
+
+#ifndef __wpantund__TunnelInterface__
+#define __wpantund__TunnelInterface__
+
+#include "tunnel.h"
+#include <cstdio>
+#include <string>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include "UnixSocket.h"
+#include <set>
+#include "IPv6Helpers.h"
+#include <boost/signals2/signal.hpp>
+
+class TunnelIPv6Interface : public nl::UnixSocket
+{
+
+public:
+	TunnelIPv6Interface(const std::string& interface_name = "", int mtu = 1280);
+
+	virtual ~TunnelIPv6Interface();
+
+	const std::string& get_interface_name(void);
+
+	int get_last_error(void);
+
+	bool is_online(void);
+	int set_online(bool isOnline);
+
+	bool set_hardware_address(const uint8_t addr[8]);
+
+	const uint8_t* get_hardware_address(void)const;
+
+	bool set_realm_local_address(const struct in6_addr *addr, int prefixlen = 64);
+
+	const struct in6_addr& get_realm_local_address()const;
+
+	bool add_address(const struct in6_addr *addr, int prefixlen = 64);
+	bool remove_address(const struct in6_addr *addr, int prefixlen = 64);
+
+	bool add_route(const struct in6_addr *route, int prefixlen = 64);
+	bool remove_route(const struct in6_addr *route, int prefixlen = 64);
+
+	virtual void reset();
+	virtual ssize_t write(const void* data, size_t len);
+	virtual ssize_t read(void* data, size_t len);
+
+	virtual int process(void);
+	virtual int update_fd_set(fd_set *read_fd_set, fd_set *write_fd_set, fd_set *error_fd_set, int *max_fd, cms_t *timeout);
+
+public: // Signals
+
+	boost::signals2::signal<void(const struct in6_addr&, int)> mAddressWasAdded;
+	boost::signals2::signal<void(const struct in6_addr&, int)> mAddressWasRemoved;
+
+private:
+	void setup_signals();
+
+private:
+	std::string mInterfaceName;
+	int mLastError;
+
+	int mNetlinkFD;
+
+	uint8_t mHardwareAddress[8];
+	struct in6_addr mRealmLocalAddress;
+	int mRealmLocalPrefixSize;
+	std::set<struct in6_addr> mAddresses;
+};
+#endif /* defined(__wpantund__TunnelInterface__) */
diff --git a/src/util/UnixSocket.cpp b/src/util/UnixSocket.cpp
new file mode 100644
index 0000000..bfd27ea
--- /dev/null
+++ b/src/util/UnixSocket.cpp
@@ -0,0 +1,187 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "UnixSocket.h"
+#include <errno.h>
+#include "socket-utils.h"
+#include <termios.h>
+#include <sys/file.h>
+#include <syslog.h>
+#include <poll.h>
+
+using namespace nl;
+
+#define SOCKET_DEBUG_BYTES_PER_LINE			16
+
+UnixSocket::UnixSocket(int rfd, int wfd, bool should_close)
+	:mShouldClose(should_close), mFDRead(rfd), mFDWrite(wfd), mLogLevel(-1)
+{
+}
+
+UnixSocket::UnixSocket(int fd, bool should_close)
+	:mShouldClose(should_close), mFDRead(fd), mFDWrite(fd), mLogLevel(-1)
+{
+}
+
+UnixSocket::~UnixSocket()
+{
+	if (mShouldClose) {
+		close(mFDRead);
+
+		if(mFDWrite != mFDRead) {
+			close(mFDWrite);
+		}
+	}
+}
+
+boost::shared_ptr<SocketWrapper>
+UnixSocket::create(int rfd, int wfd, bool should_close)
+{
+	return boost::shared_ptr<SocketWrapper>(new UnixSocket(rfd,wfd,should_close));
+}
+
+boost::shared_ptr<SocketWrapper>
+UnixSocket::create(int fd, bool should_close)
+{
+	return UnixSocket::create(fd, fd, should_close);
+}
+
+ssize_t
+UnixSocket::write(const void* data, size_t len)
+{
+	ssize_t ret = ::write(mFDWrite, data, len);
+	if(ret<0) {
+		ret = -errno;
+	} else if(ret == 0) {
+		ret = fd_has_error(mFDWrite);
+#if DEBUG
+	} else if (mLogLevel != -1) {
+		const uint8_t* byte = (uint8_t*)data;
+		ssize_t i = 0;
+
+		while(i<ret) {
+			ssize_t j = 0;
+			char dump_string[SOCKET_DEBUG_BYTES_PER_LINE*3+1];
+
+			for (j = 0;i<ret && j<SOCKET_DEBUG_BYTES_PER_LINE;i++,j++) {
+				sprintf(dump_string+j*3, "%02X ", byte[i]);
+			}
+
+			syslog(mLogLevel, "UnixSocket: %3d Byte(s) sent to FD%d:   %s%s", (int)j, mFDWrite, dump_string, (i<ret)?" ...":"");
+		}
+#endif
+	}
+	return ret;
+}
+
+ssize_t
+UnixSocket::read(void* data, size_t len)
+{
+	ssize_t ret = ::read(mFDRead, data, len);
+	if(ret<0) {
+		if(EAGAIN == errno) {
+			ret = 0;
+		} else {
+			ret = -errno;
+		}
+#if DEBUG
+	} else if ((ret > 0) && (mLogLevel != -1)) {
+		const uint8_t* byte = (uint8_t*)data;
+		ssize_t i = 0;
+
+		while(i<ret) {
+			ssize_t j = 0;
+			char dump_string[SOCKET_DEBUG_BYTES_PER_LINE*3+1];
+
+			for (j = 0;i<ret && j<SOCKET_DEBUG_BYTES_PER_LINE;i++,j++) {
+				sprintf(dump_string+j*3, "%02X ", byte[i]);
+			}
+
+			syslog(mLogLevel, "UnixSocket: %3d Byte(s) read from FD%d: %s%s", (int)j, mFDWrite, dump_string, (i<ret)?" ...":"");
+		}
+#endif
+	}
+
+	if(ret==0) {
+		ret = fd_has_error(mFDRead);
+	}
+
+	return ret;
+}
+
+bool
+UnixSocket::can_read(void)const
+{
+	bool ret = false;
+	const int flags = POLLRDNORM|POLLERR|POLLNVAL|POLLHUP;
+	struct pollfd pollfd = { mFDRead, flags, 0 };
+	int count = poll(&pollfd, 1, 0);
+	ret = (count>0) && ((pollfd.revents & flags) != 0);
+	return ret;
+}
+
+bool
+UnixSocket::can_write(void)const
+{
+	const int flags = POLLOUT|POLLERR|POLLNVAL|POLLHUP;
+	struct pollfd pollfd = { mFDWrite, flags, 0 };
+
+	return (poll(&pollfd, 1, 0)>0) && ((pollfd.revents & flags) != 0);
+}
+
+void
+UnixSocket::send_break()
+{
+	if (isatty(mFDWrite)) {
+#if DEBUG
+		syslog(mLogLevel, "UnixSocket: Sending BREAK");
+#endif
+		tcsendbreak(mFDWrite, 0);
+	}
+};
+
+int
+UnixSocket::get_read_fd(void)const
+{
+	return mFDRead;
+}
+
+int
+UnixSocket::get_write_fd(void)const
+{
+	return mFDWrite;
+}
+
+int
+UnixSocket::process(void)
+{
+	return 0;
+}
+
+int
+UnixSocket::set_log_level(int log_level)
+{
+	mLogLevel = log_level;
+
+	return 0;
+}
diff --git a/src/util/UnixSocket.h b/src/util/UnixSocket.h
new file mode 100644
index 0000000..9ab990e
--- /dev/null
+++ b/src/util/UnixSocket.h
@@ -0,0 +1,56 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __wpantund__UnixSocket__
+#define __wpantund__UnixSocket__
+
+#include "SocketWrapper.h"
+#include <stdio.h>
+#include <unistd.h>
+
+namespace nl {
+class UnixSocket : public SocketWrapper {
+protected:
+	UnixSocket(int rfd, int wfd, bool should_close);
+	UnixSocket(int fd, bool should_close);
+
+public:
+	static boost::shared_ptr<SocketWrapper> create(int fd, bool should_close = false);
+	static boost::shared_ptr<SocketWrapper> create(int rfd, int wfd, bool should_close = false);
+	virtual ~UnixSocket();
+	virtual ssize_t write(const void* data, size_t len);
+	virtual ssize_t read(void* data, size_t len);
+	virtual bool can_read(void)const;
+	virtual bool can_write(void)const;
+	virtual int get_read_fd(void)const;
+	virtual int get_write_fd(void)const;
+	virtual int process(void);
+	virtual void send_break();
+	virtual int set_log_level(int log_level);
+
+protected:
+	bool mShouldClose;
+	int mFDRead;
+	int mFDWrite;
+	int mLogLevel;
+}; // class UnixSocket
+
+}; // namespace nl
+
+#endif /* defined(__wpantund__UnixSocket__) */
diff --git a/src/util/ValueMap.cpp b/src/util/ValueMap.cpp
new file mode 100644
index 0000000..923ffe9
--- /dev/null
+++ b/src/util/ValueMap.cpp
@@ -0,0 +1,42 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      ValueMap is a dictionary-like key-value store
+ *
+ */
+
+#include "ValueMap.h"
+
+nl::ValueMap
+nl::ValueMapWithKeysAndValues(const char* key, ...)
+{
+	ValueMap ret;
+	va_list args;
+	va_start(args, key);
+	while (key != NULL) {
+		boost::any* value = va_arg(args, boost::any*);
+		if (value == NULL) {
+			ret[key] = boost::any();
+		} else {
+			ret[key] = *value;
+		}
+		key = va_arg(args, const char*);
+	}
+	va_end(args);
+	return ret;
+}
diff --git a/src/util/ValueMap.h b/src/util/ValueMap.h
new file mode 100644
index 0000000..135ede5
--- /dev/null
+++ b/src/util/ValueMap.h
@@ -0,0 +1,39 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      ValueMap is a dictionary-like key-value store
+ *
+ */
+
+#ifndef wpantund_ValueMap_h
+#define wpantund_ValueMap_h
+
+#include <boost/any.hpp>
+#include <cstdarg>
+#include <map>
+#include <string>
+
+namespace nl {
+
+typedef std::map<std::string, boost::any> ValueMap;
+
+extern ValueMap ValueMapWithKeysAndValues(const char* key, ...);
+
+}; // namespace nl
+
+#endif
diff --git a/src/util/any-to.cpp b/src/util/any-to.cpp
new file mode 100644
index 0000000..92acb37
--- /dev/null
+++ b/src/util/any-to.cpp
@@ -0,0 +1,278 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      Implementation of utility functions related to boost::any.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include "any-to.h"
+#include <exception>
+#include <stdexcept>
+#include <ctype.h>
+#include <list>
+#include "string-utils.h"
+
+using namespace nl;
+
+nl::Data any_to_data(const boost::any& value)
+{
+	nl::Data net_key;
+
+	if (value.type() == typeid(std::string)) {
+		std::string key_string = boost::any_cast<std::string>(value);
+		net_key = nl::Data(
+		    (const uint8_t*)key_string.c_str(), key_string.length());
+	} else if (value.type() == typeid(nl::Data)) {
+		net_key = boost::any_cast<nl::Data>(value);
+	} else {
+		net_key = nl::Data(boost::any_cast<std::vector<uint8_t> >(value));
+	}
+	return net_key;
+}
+
+int any_to_int(const boost::any& value)
+{
+	int32_t ret = 0;
+
+	if (value.type() == typeid(std::string)) {
+		std::string key_string = boost::any_cast<std::string>(value);
+		ret = (int)strtol(key_string.c_str(), NULL, 0);
+	} else if (value.type() == typeid(uint8_t)) {
+		ret = boost::any_cast<uint8_t>(value);
+	} else if (value.type() == typeid(int8_t)) {
+		ret = boost::any_cast<int8_t>(value);
+	} else if (value.type() == typeid(uint16_t)) {
+		ret = boost::any_cast<uint16_t>(value);
+	} else if (value.type() == typeid(int16_t)) {
+		ret = boost::any_cast<int16_t>(value);
+	} else if (value.type() == typeid(uint32_t)) {
+		ret = boost::any_cast<uint32_t>(value);
+	} else if (value.type() == typeid(int32_t)) {
+		ret = boost::any_cast<int32_t>(value);
+	} else if (value.type() == typeid(bool)) {
+		ret = boost::any_cast<bool>(value);
+	} else if (value.type() == typeid(unsigned int)) {
+		ret = boost::any_cast<unsigned int>(value);
+	} else {
+		ret = boost::any_cast<int>(value);
+	}
+	return ret;
+}
+
+struct in6_addr
+any_to_ipv6(const boost::any& value)
+{
+	struct in6_addr ret = {};
+
+	if (value.type() == typeid(std::string)) {
+		std::string str(boost::any_cast<std::string>(value));
+		size_t lastchar(str.find_first_not_of("0123456789abcdefABCDEF:."));
+		int bytes;
+
+		if (lastchar != std::string::npos) {
+			str.erase(str.begin() + lastchar, str.end());
+		}
+
+		bytes = inet_pton(
+			AF_INET6,
+			str.c_str(),
+			ret.s6_addr
+		);
+		if (bytes <= 0) {
+			throw std::invalid_argument("String not IPv6 address");
+		}
+	} else if (value.type() == typeid(nl::Data)) {
+		nl::Data data(boost::any_cast<nl::Data>(value));
+		if (data.size() <= sizeof(ret)) {
+			memcpy(ret.s6_addr, data.data(), data.size());
+		}
+	} else {
+		ret = boost::any_cast<struct in6_addr>(value);
+	}
+
+	return ret;
+}
+
+uint64_t
+any_to_uint64(const boost::any& value)
+{
+	uint64_t ret(0);
+
+	if (value.type() == typeid(std::string)) {
+		const std::string& key_string = boost::any_cast<std::string>(value);
+
+		if (key_string.size() != 16) {
+			throw std::invalid_argument("String not 16 characters long");
+		}
+
+		ret = static_cast<uint64_t>(strtoull(key_string.c_str(), NULL, 16));
+
+	} else if (value.type() == typeid(nl::Data)) {
+		const nl::Data& data = boost::any_cast<nl::Data>(value);
+		union {
+			uint64_t val;
+			uint8_t data[sizeof(uint64_t)];
+		} x;
+
+		if (data.size() != sizeof(uint64_t)) {
+			throw std::invalid_argument("Data not 8 bytes long");
+		}
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+		memcpyrev(x.data, data.data(), sizeof(uint64_t));
+#else
+		memcpy(x.data, data.data(), sizeof(uint64_t));
+#endif
+
+		ret = x.val;
+
+	} else {
+		ret = boost::any_cast<uint64_t>(value);
+	}
+
+
+	return ret;
+}
+
+bool any_to_bool(const boost::any& value)
+{
+	bool ret = 0;
+
+	if (value.type() == typeid(std::string)) {
+		std::string key_string = boost::any_cast<std::string>(value);
+		if (key_string=="true" || key_string=="yes" || key_string=="1" || key_string == "TRUE" || key_string == "YES")
+			ret = true;
+		else if (key_string=="false" || key_string=="no" || key_string=="0" || key_string == "FALSE" || key_string == "NO")
+			ret = false;
+		else
+			ret = (bool)strtol(key_string.c_str(), NULL, 0);
+	} else if (value.type() == typeid(bool)) {
+		ret = boost::any_cast<bool>(value);
+	} else {
+		ret = any_to_int(value) != 0;
+	}
+	return ret;
+}
+
+std::string any_to_string(const boost::any& value)
+{
+	std::string ret;
+
+	if (value.type() == typeid(uint8_t)) {
+		char tmp[10];
+		snprintf(tmp, sizeof(tmp), "%u", boost::any_cast<uint8_t>(value));
+		ret = tmp;
+	} else if (value.type() == typeid(int8_t)) {
+		char tmp[10];
+		snprintf(tmp, sizeof(tmp), "%d", boost::any_cast<int8_t>(value));
+		ret = tmp;
+	} else if (value.type() == typeid(uint16_t)) {
+		char tmp[10];
+		snprintf(tmp, sizeof(tmp), "%u", boost::any_cast<uint16_t>(value));
+		ret = tmp;
+	} else if (value.type() == typeid(int16_t)) {
+		char tmp[10];
+		snprintf(tmp, sizeof(tmp), "%d", boost::any_cast<int16_t>(value));
+		ret = tmp;
+	} else if (value.type() == typeid(uint32_t)) {
+		char tmp[10];
+		snprintf(tmp, sizeof(tmp), "%u", (unsigned int)boost::any_cast<uint32_t>(value));
+		ret = tmp;
+	} else if (value.type() == typeid(int32_t)) {
+		char tmp[10];
+		snprintf(tmp, sizeof(tmp), "%d", (int)boost::any_cast<int32_t>(value));
+		ret = tmp;
+	} else if (value.type() == typeid(uint64_t)) {
+		char tmp[20];
+		uint64_t u64_val = boost::any_cast<uint64_t>(value);
+		snprintf(tmp,
+		         sizeof(tmp),
+		         "%08x%08x",
+		         static_cast<uint32_t>(u64_val >> 32),
+		         static_cast<uint32_t>(u64_val & 0xFFFFFFFF));
+		ret = tmp;
+	} else if (value.type() == typeid(unsigned int)) {
+		char tmp[10];
+		snprintf(tmp, sizeof(tmp), "%u", boost::any_cast<unsigned int>(value));
+		ret = tmp;
+	} else if (value.type() == typeid(int)) {
+		char tmp[10];
+		snprintf(tmp, sizeof(tmp), "%d", boost::any_cast<int>(value));
+		ret = tmp;
+	} else if (value.type() == typeid(bool)) {
+		ret = (boost::any_cast<bool>(value))? "true" : "false";
+	} else if (value.type() == typeid(nl::Data)) {
+		ret = "<nl::Data>";
+	} else {
+		ret = boost::any_cast<std::string>(value);
+	}
+	return ret;
+}
+
+std::set<int>
+any_to_int_set(const boost::any& value)
+{
+	std::set<int> ret;
+
+	if (value.type() == typeid(std::string)) {
+		std::string key_string = boost::any_cast<std::string>(value);
+		if (key_string.empty()) {
+			// Empty set. Do nothing.
+		} else if (key_string.find(',') != std::string::npos) {
+			// List of values. Not yet supported.
+			throw std::invalid_argument("integer mask string format not yet implemented");
+		} else if (isdigit(key_string[0])) {
+			// Special case, only one value.
+			ret.insert((int)strtol(key_string.c_str(), NULL, 0));
+		} else {
+			throw std::invalid_argument(key_string);
+		}
+	} else if (value.type() == typeid(uint8_t)) {
+		ret.insert(boost::any_cast<uint8_t>(value));
+	} else if (value.type() == typeid(int8_t)) {
+		ret.insert(boost::any_cast<int8_t>(value));
+	} else if (value.type() == typeid(uint16_t)) {
+		ret.insert(boost::any_cast<uint16_t>(value));
+	} else if (value.type() == typeid(int16_t)) {
+		ret.insert(boost::any_cast<int16_t>(value));
+	} else if (value.type() == typeid(uint32_t)) {
+		ret.insert(boost::any_cast<uint32_t>(value));
+	} else if (value.type() == typeid(int32_t)) {
+		ret.insert(boost::any_cast<int32_t>(value));
+	} else if (value.type() == typeid(bool)) {
+		ret.insert(boost::any_cast<bool>(value));
+	} else if (value.type() == typeid(std::list<int>)) {
+		std::list<int> number_list(boost::any_cast< std::list<int> >(value));
+		ret.insert(number_list.begin(), number_list.end());
+	} else if (value.type() == typeid(std::list<boost::any>)) {
+		std::list<boost::any> any_list(boost::any_cast< std::list<boost::any> >(value));
+		std::list<boost::any>::const_iterator iter;
+		for(iter = any_list.begin(); iter != any_list.end(); ++iter) {
+			ret.insert(any_to_int(*iter));
+		}
+	} else {
+		ret = boost::any_cast< std::set<int> >(value);
+	}
+	return ret;
+}
diff --git a/src/util/any-to.h b/src/util/any-to.h
new file mode 100644
index 0000000..19e7936
--- /dev/null
+++ b/src/util/any-to.h
@@ -0,0 +1,39 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      Declaration of utility functions related to boost::any.
+ *
+ */
+
+#ifndef wpantund_any_to_h
+#define wpantund_any_to_h
+
+#include <string>
+#include <boost/any.hpp>
+#include "Data.h"
+#include <set>
+#include <arpa/inet.h>
+
+extern nl::Data any_to_data(const boost::any& value);
+extern int any_to_int(const boost::any& value);
+extern uint64_t any_to_uint64(const boost::any& value);
+extern struct in6_addr any_to_ipv6(const boost::any& value);
+extern bool any_to_bool(const boost::any& value);
+extern std::string any_to_string(const boost::any& value);
+extern std::set<int> any_to_int_set(const boost::any& value);
+#endif
diff --git a/src/util/args.h b/src/util/args.h
new file mode 100644
index 0000000..1c67efc
--- /dev/null
+++ b/src/util/args.h
@@ -0,0 +1,69 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef UTIL_ARGS_HEADER_INCLUDED
+#define UTIL_ARGS_HEADER_INCLUDED 1
+
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+
+typedef struct {
+	char shortarg;
+	const char* longarg;
+	const char* param;
+	const char* desc;
+} arg_list_item_t;
+
+static inline void
+print_arg_list_help(
+    const arg_list_item_t arg_list[],
+    const char*                             command_name,
+    const char*                             syntax
+    )
+{
+	int i;
+
+	printf("Syntax:\n");
+	printf("   %s %s\n", command_name, syntax);
+	printf("Options:\n");
+	for (i = 0; arg_list[i].desc; ++i) {
+		if (arg_list[i].shortarg)
+			printf("   -%c", arg_list[i].shortarg);
+		else
+			printf("     ");
+
+		if (arg_list[i].longarg) {
+			if (arg_list[i].shortarg)
+				printf("/");
+			else
+				printf(" ");
+
+			printf("--%s%s",
+			       arg_list[i].longarg,
+			       &"                    "[strlen(arg_list[i].longarg)]);
+		} else {
+			printf("                       ");
+		}
+
+		printf(" %s\n", arg_list[i].desc);
+	}
+}
+
+#endif // UTIL_ARGS_HEADER_INCLUDED
diff --git a/src/util/config-file.c b/src/util/config-file.c
new file mode 100644
index 0000000..3cd4685
--- /dev/null
+++ b/src/util/config-file.c
@@ -0,0 +1,136 @@
+/*
+ *
+ *    Copyright (c) 2010-2012 Nest Labs, Inc.
+ *    All rights reserved.
+ *
+ *    This document is the property of Nest. It is considered
+ *    confidential and proprietary information.
+ *
+ *    This document may not be reproduced or transmitted in any form,
+ *    in whole or in part, without the express written permission of
+ *    Nest.
+ *
+ *    Description:
+ *		This file implements a configuration file parser.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef ASSERT_MACROS_USE_SYSLOG
+#define ASSERT_MACROS_USE_SYSLOG 1
+#endif
+
+#include "assert-macros.h"
+
+#include "config-file.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <ctype.h>
+#include <poll.h>
+#include <sys/select.h>
+#include <libgen.h>
+#include <syslog.h>
+
+#include "fgetln.h"
+#include "string-utils.h"
+
+#define strcaseequal(x, y)   (strcasecmp(x, y) == 0)
+
+char*
+get_next_arg(
+    char *buf, char **rest
+    )
+{
+	char* ret = NULL;
+
+	while (*buf && isspace(*buf)) buf++;
+
+	if (!*buf || *buf == '#') {
+		goto bail;
+	}
+
+	ret = buf;
+
+	char quote_type = 0;
+	char* write_iter = ret;
+
+	while (isspace(*buf)) { buf++; };
+
+	while (*buf) {
+		if (quote_type != 0) {
+			if (*buf == quote_type) {
+				quote_type = 0;
+				buf++;
+				continue;
+			}
+		} else {
+			if (*buf == '"' || *buf == '\'') {
+				quote_type = *buf++;
+				continue;
+			}
+
+			if (isspace(*buf)) {
+				buf++;
+				break;
+			}
+		}
+
+		if (buf[0] == '\\' && buf[1]) buf++;
+
+		*write_iter++ = *buf++;
+	}
+
+	*write_iter = 0;
+
+bail:
+	if (rest)
+		*rest = buf;
+	return ret;
+}
+
+int
+read_config(
+    const char* filename,
+	config_param_set_func setter,
+	void* context
+) {
+	int ret = 0;
+
+	FILE* file = fopen(filename, "r");
+	char* line = NULL;
+	size_t line_len = 0;
+	int line_number = 0;
+
+	if (file == NULL) {
+		ret = -1;
+		goto bail;
+	}
+
+	syslog(LOG_INFO, "Reading configuration from \"%s\" . . .", filename);
+
+	while (!feof(file) && (line = fgetln(file, &line_len)) && !ret) {
+		char *key = get_next_arg(line, &line);
+		char *value = get_next_arg(line, &line);
+		line_number++;
+		if (!key) {
+			continue;
+		}
+		ret = setter(context, key, value);
+	}
+
+bail:
+	if (file) {
+		fclose(file);
+	}
+	return ret;
+}
diff --git a/src/util/config-file.h b/src/util/config-file.h
new file mode 100644
index 0000000..aac5f65
--- /dev/null
+++ b/src/util/config-file.h
@@ -0,0 +1,29 @@
+/*
+ *
+ *    Copyright (c) 2010-2012 Nest Labs, Inc.
+ *    All rights reserved.
+ *
+ *    This document is the property of Nest. It is considered
+ *    confidential and proprietary information.
+ *
+ *    This document may not be reproduced or transmitted in any form,
+ *    in whole or in part, without the express written permission of
+ *    Nest.
+ *
+ *    Description:
+ *		This is the header for the configuration file parser.
+ *
+ */
+
+#ifndef __WPAN_CONFIG_FILE_H__
+#define __WPAN_CONFIG_FILE_H__ 1
+
+__BEGIN_DECLS
+extern char* get_next_arg(char *buf, char **rest);
+
+typedef int (*config_param_set_func)(void* context, const char* key, const char* value);
+
+extern int read_config(const char* filename, config_param_set_func setter, void* context);
+__END_DECLS
+
+#endif // __WPAN_CONFIG_FILE_H__
diff --git a/src/util/nlpt-select.c b/src/util/nlpt-select.c
new file mode 100644
index 0000000..f53281d
--- /dev/null
+++ b/src/util/nlpt-select.c
@@ -0,0 +1,141 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      Flavor of protothreads for handling asynchronous I/O, via select()
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _XOPEN_SOURCE 1 // For the "fds_bits" member of "fd_set"
+
+#include <stdio.h>
+#include <stdint.h>
+#include "nlpt.h"
+#include "nlpt-select.h"
+#include "assert-macros.h"
+
+static void
+fd_set_merge(const fd_set *src, fd_set *dest, int fd_count)
+{
+	int i;
+	const int32_t* src_data = (const int32_t*)src->fds_bits;
+	int32_t* dest_data = (int32_t*)dest->fds_bits;
+
+	for (i = (fd_count+31)/32; i > 0 ; --i) {
+		*dest_data++ |= *src_data++;
+	}
+}
+
+void
+nlpt_select_update_fd_set(
+	const struct nlpt* nlpt,
+	fd_set *read_fd_set,
+	fd_set *write_fd_set,
+	fd_set *error_fd_set,
+	int *max_fd
+) {
+	if ((max_fd != NULL) && (*max_fd < nlpt->max_fd)) {
+		*max_fd = nlpt->max_fd;
+	}
+
+	if (read_fd_set != NULL) {
+		fd_set_merge(&nlpt->read_fds, read_fd_set, nlpt->max_fd + 1);
+	}
+
+	if (write_fd_set != NULL) {
+		fd_set_merge(&nlpt->write_fds, write_fd_set, nlpt->max_fd + 1);
+	}
+
+	if (error_fd_set != NULL) {
+		fd_set_merge(&nlpt->error_fds, error_fd_set, nlpt->max_fd + 1);
+	}
+}
+
+bool
+_nlpt_checkpoll(int fd, short poll_flags)
+{
+	bool ret = false;
+
+	if (fd >= 0) {
+		struct pollfd pollfd = { fd, poll_flags, 0 };
+		IGNORE_RETURN_VALUE( poll(&pollfd, 1, 0) );
+		ret = ((pollfd.revents & poll_flags) != 0);
+	}
+
+	return ret;
+}
+
+void
+_nlpt_cleanup_all(struct nlpt* nlpt)
+{
+	nlpt->max_fd = -1;
+	FD_ZERO(&nlpt->read_fds);
+	FD_ZERO(&nlpt->write_fds);
+	FD_ZERO(&nlpt->error_fds);
+}
+
+void
+_nlpt_cleanup_read_fd_source(struct nlpt* nlpt, int fd)
+{
+	if (fd >= 0) {
+		FD_CLR(fd, &nlpt->read_fds);
+		FD_CLR(fd, &nlpt->error_fds);
+	}
+}
+
+void
+_nlpt_cleanup_write_fd_source(struct nlpt* nlpt, int fd)
+{
+	if (fd >= 0) {
+		FD_CLR(fd, &nlpt->write_fds);
+		FD_CLR(fd, &nlpt->error_fds);
+	}
+}
+
+void
+_nlpt_setup_read_fd_source(struct nlpt* nlpt, int fd)
+{
+	if (fd >= 0) {
+		if (fd > nlpt->max_fd) {
+			nlpt->max_fd = fd;
+		}
+		FD_SET(fd, &nlpt->read_fds);
+		FD_SET(fd, &nlpt->error_fds);
+	}
+}
+
+void
+_nlpt_setup_write_fd_source(struct nlpt* nlpt, int fd)
+{
+	if (fd >= 0) {
+		if (fd > nlpt->max_fd) {
+			nlpt->max_fd = fd;
+		}
+		FD_SET(fd, &nlpt->write_fds);
+		FD_SET(fd, &nlpt->error_fds);
+	}
+}
+
+void
+_nlpt_init(struct nlpt* nlpt)
+{
+	_nlpt_cleanup_all(nlpt);
+}
diff --git a/src/util/nlpt-select.h b/src/util/nlpt-select.h
new file mode 100644
index 0000000..0508f2e
--- /dev/null
+++ b/src/util/nlpt-select.h
@@ -0,0 +1,75 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      Flavor of protothreads for handling asynchronous I/O, via select()
+ *
+ */
+
+#ifndef wpantund_nlpt_select_h
+#define wpantund_nlpt_select_h
+
+#include <stdbool.h>
+#include <poll.h>
+#include <sys/select.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+struct nlpt {
+	struct pt pt;
+	struct pt sub_pt;
+	size_t byte_count;
+	int last_errno;
+
+	fd_set read_fds;
+	fd_set write_fds;
+	fd_set error_fds;
+	int max_fd;
+};
+
+/* ========================================================================= */
+/* Private, Back-end-specific functions */
+
+extern void _nlpt_init(struct nlpt* nlpt);
+extern void _nlpt_cleanup_all(struct nlpt* nlpt);
+extern void _nlpt_cleanup_read_fd_source(struct nlpt* nlpt, int fd);
+extern void _nlpt_cleanup_write_fd_source(struct nlpt* nlpt, int fd);
+extern void _nlpt_setup_read_fd_source(struct nlpt* nlpt, int fd);
+extern void _nlpt_setup_write_fd_source(struct nlpt* nlpt, int fd);
+
+/* ========================================================================= */
+/* Protected, Back-end-specific hooks (for async I/O) */
+
+extern bool nlpt_hook_check_read_fd_source(struct nlpt* nlpt, int fd);
+extern bool nlpt_hook_check_write_fd_source(struct nlpt* nlpt, int fd);
+
+/* ========================================================================= */
+/* Public, Back-end-specific functions (for async I/O) */
+
+extern void nlpt_select_update_fd_set(
+	const struct nlpt* nlpt,
+	fd_set *read_fd_set,
+	fd_set *write_fd_set,
+	fd_set *error_fd_set,
+	int *max_fd
+);
+extern bool _nlpt_checkpoll(int fd, short poll_flags);
+
+__END_DECLS
+
+#endif
diff --git a/src/util/nlpt.h b/src/util/nlpt.h
new file mode 100644
index 0000000..2b438a6
--- /dev/null
+++ b/src/util/nlpt.h
@@ -0,0 +1,129 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      Flavor of protothreads for handling asynchronous I/O.
+ *
+ */
+
+#ifndef wpantund_nlpt_h
+#define wpantund_nlpt_h
+
+#include "pt.h"
+
+#if USING_GLIB
+#include "nlpt-glib.h"
+#else
+#include "nlpt-select.h"
+#endif
+
+#define NLPT_INIT(nlpt) do { memset(nlpt, 0, sizeof(*nlpt)); PT_INIT(&(nlpt)->pt); _nlpt_init(nlpt); } while (0)
+
+#define NLPT_THREAD(name_args)          PT_THREAD(name_args)
+#define NLPT_BEGIN(nlpt)                PT_BEGIN(&(nlpt)->pt)
+#define NLPT_END(nlpt)                  PT_END(&(nlpt)->pt)
+#define NLPT_SPAWN(nlpt, child, thread) PT_SPAWN(&(nlpt)->pt, &(child)->pt, thread)
+#define NLPT_WAIT_UNTIL(nlpt, cond)     PT_WAIT_UNTIL(&(nlpt)->pt, cond)
+#define NLPT_WAIT_WHILE(nlpt, cond)     PT_WAIT_WHILE(&(nlpt)->pt, cond)
+#define NLPT_RESTART(nlpt)              PT_RESTART(&(nlpt)->pt)
+#define NLPT_EXIT(nlpt)                 PT_EXIT(&(nlpt)->pt)
+#define NLPT_YIELD(nlpt)                PT_YIELD(&(nlpt)->pt)
+#define NLPT_YIELD_UNTIL(nlpt, cond)    PT_YIELD_UNTIL(&(nlpt)->pt, cond)
+
+//! Waits until one of the two given file descriptors are readable or the condition is satisfied.
+#define NLPT_WAIT_UNTIL_READABLE2_OR_COND(nlpt, fd_, fd2_, c) \
+	do {                                                     \
+		_nlpt_setup_read_fd_source(nlpt, fd_); \
+		_nlpt_setup_read_fd_source(nlpt, fd2_); \
+		NLPT_WAIT_UNTIL(nlpt, \
+						 nlpt_hook_check_read_fd_source(nlpt, fd_) \
+						 || nlpt_hook_check_read_fd_source(nlpt, fd2_) \
+						 || (c)); \
+		_nlpt_cleanup_read_fd_source(nlpt, fd2_); \
+		_nlpt_cleanup_read_fd_source(nlpt, fd_); \
+	} while (0)
+
+#define NLPT_WAIT_UNTIL_READABLE_OR_COND(nlpt, fd_, c) \
+	do {                                                     \
+		_nlpt_setup_read_fd_source(nlpt, fd_); \
+		NLPT_WAIT_UNTIL(nlpt, \
+						 nlpt_hook_check_read_fd_source(nlpt, fd_) || (c)); \
+		_nlpt_cleanup_read_fd_source(nlpt, fd_); \
+	} while (0)
+
+#define NLPT_WAIT_UNTIL_WRITABLE_OR_COND(nlpt, fd_, c) \
+	do {                                                     \
+		_nlpt_setup_write_fd_source(nlpt, fd_); \
+		NLPT_WAIT_UNTIL(nlpt, \
+						 nlpt_hook_check_write_fd_source(nlpt, fd_) || (c)); \
+		_nlpt_cleanup_write_fd_source(nlpt, fd_); \
+	} while (0)
+
+#define NLPT_YIELD_UNTIL_READABLE2_OR_COND(nlpt, fd_, fd2_, c) \
+	do {                                                     \
+		_nlpt_setup_read_fd_source(nlpt, fd_); \
+		_nlpt_setup_read_fd_source(nlpt, fd2_); \
+		NLPT_YIELD_UNTIL(nlpt, \
+						 nlpt_hook_check_read_fd_source(nlpt, fd_) \
+						 || nlpt_hook_check_read_fd_source(nlpt, fd2_) \
+						 || (c)); \
+		_nlpt_cleanup_read_fd_source(nlpt, fd2_); \
+		_nlpt_cleanup_read_fd_source(nlpt, fd_); \
+	} while (0)
+
+#define NLPT_YIELD_UNTIL_READABLE_OR_COND(nlpt, fd_, c) \
+	do {                                                     \
+		_nlpt_setup_read_fd_source(nlpt, fd_); \
+		NLPT_YIELD_UNTIL(nlpt, \
+						 nlpt_hook_check_read_fd_source(nlpt, fd_) || (c)); \
+		_nlpt_cleanup_read_fd_source(nlpt, fd_); \
+	} while (0)
+
+#define NLPT_YIELD_UNTIL_WRITABLE_OR_COND(nlpt, fd_, c) \
+	do {                                                     \
+		_nlpt_setup_write_fd_source(nlpt, fd_); \
+		NLPT_YIELD_UNTIL(nlpt, \
+						 nlpt_hook_check_write_fd_source(nlpt, fd_) || (c)); \
+		_nlpt_cleanup_write_fd_source(nlpt, fd_); \
+	} while (0)
+
+#define NLPT_WAIT_UNTIL_READABLE(nlpt, fd) \
+	NLPT_WAIT_UNTIL_READABLE_OR_COND( \
+	    nlpt, \
+	    fd, \
+	    0)
+
+#define NLPT_WAIT_UNTIL_WRITABLE(nlpt, fd) \
+	NLPT_WAIT_UNTIL_WRITABLE_OR_COND( \
+	    nlpt, \
+	    fd, \
+	    0)
+
+#define NLPT_YIELD_UNTIL_READABLE(nlpt, fd) \
+	NLPT_YIELD_UNTIL_READABLE_OR_COND( \
+	    nlpt, \
+	    fd, \
+	    0)
+
+#define NLPT_YIELD_UNTIL_WRITABLE(nlpt, fd) \
+	NLPT_YIELD_UNTIL_WRITABLE_OR_COND( \
+	    nlpt, \
+	    fd, \
+	    0)
+
+
+#endif
diff --git a/src/util/socket-utils.c b/src/util/socket-utils.c
new file mode 100644
index 0000000..1dbdef1
--- /dev/null
+++ b/src/util/socket-utils.c
@@ -0,0 +1,759 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE 1
+
+#include "assert-macros.h"
+
+#include <stdio.h>
+#include "socket-utils.h"
+#include <ctype.h>
+#include <syslog.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <ctype.h>
+
+#if HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#if HAVE_PTY_H
+#include <pty.h>
+#endif
+
+#if HAVE_UTIL_H
+#include <util.h>
+#endif
+
+#if !defined(HAVE_PTSNAME) && __APPLE__
+#define HAVE_PTSNAME 1
+#endif
+
+#if !HAVE_GETDTABLESIZE
+#define getdtablesize()     (int)sysconf(_SC_OPEN_MAX)
+#endif
+
+#ifndef O_NONBLOCK
+#define O_NONBLOCK          O_NDELAY
+#endif
+
+#include <poll.h>
+
+int
+fd_has_error(int fd)
+{
+	const int flags = (POLLPRI|POLLRDBAND|POLLERR|POLLHUP|POLLNVAL);
+	struct pollfd pollfd = { fd, flags, 0 };
+	int count = poll(&pollfd, 1, 0);
+
+	if (count<0) {
+		return -errno;
+	} else if (count > 0) {
+		if (pollfd.revents&POLLHUP) {
+			return -EPIPE;
+		}
+
+		if (pollfd.revents&(POLLRDBAND|POLLPRI)) {
+			return -EPIPE;
+		}
+
+		if (pollfd.revents&POLLNVAL) {
+			return -EINVAL;
+		}
+
+		if (pollfd.revents&POLLERR) {
+			return -EIO;
+		}
+	}
+	return 0;
+}
+
+int gSocketWrapperBaud = 115200;
+
+bool
+socket_name_is_system_command(const char* socket_name)
+{
+	return strncmp(socket_name,SOCKET_SYSTEM_COMMAND_PREFIX,strlen(SOCKET_SYSTEM_COMMAND_PREFIX)) == 0
+	    || strncmp(socket_name,SOCKET_SYSTEM_FORKPTY_COMMAND_PREFIX,strlen(SOCKET_SYSTEM_FORKPTY_COMMAND_PREFIX)) == 0
+	    || strncmp(socket_name,SOCKET_SYSTEM_SOCKETPAIR_COMMAND_PREFIX,strlen(SOCKET_SYSTEM_SOCKETPAIR_COMMAND_PREFIX)) == 0
+	;
+}
+
+bool
+socket_name_is_port(const char* socket_name)
+{
+	// It's a port if the string is just a number.
+	while (*socket_name) {
+		if (!isdigit(*socket_name++))
+			return false;
+	}
+
+	return true;
+}
+
+bool
+socket_name_is_inet(const char* socket_name)
+{
+	// It's an inet address if it Contains no slashes
+	do {
+		if (*socket_name == '/') {
+			return false;
+		}
+	} while (*++socket_name);
+	return !socket_name_is_port(socket_name) && !socket_name_is_system_command(socket_name);
+}
+
+bool
+socket_name_is_device(const char* socket_name)
+{
+	return !socket_name_is_system_command(socket_name) && !socket_name_is_inet(socket_name);
+}
+
+int
+lookup_sockaddr_from_host_and_port(
+    struct sockaddr_in6* outaddr, const char* host, const char* port
+    )
+{
+	int ret = 0;
+	struct addrinfo hint = {
+	};
+
+	hint.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED | AI_ALL;
+	hint.ai_family = AF_INET6;
+
+	struct addrinfo *results = NULL;
+	struct addrinfo *iter = NULL;
+
+	if (!port)
+		port = "4951";
+
+	if (!host)
+		host = "::1";
+
+	syslog(LOG_INFO, "Looking up [%s]:%s", host, port);
+
+	if (isdigit(port[0]) && strcmp(host, "::1") == 0) {
+		// Special case to avoid calling getaddrinfo() when
+		// we really don't need to.
+		memset(outaddr, 0, sizeof(struct sockaddr_in6));
+		outaddr->sin6_family = AF_INET6;
+		outaddr->sin6_addr.s6_addr[15] = 1;
+		outaddr->sin6_port = htons(atoi(port));
+	} else if (isdigit(port[0]) && inet_addr(host) != 0) {
+		in_addr_t v4addr = inet_addr(host);
+		memset(outaddr, 0, sizeof(struct sockaddr_in6));
+		outaddr->sin6_family = AF_INET6;
+		outaddr->sin6_addr.s6_addr[10] = 0xFF;
+		outaddr->sin6_addr.s6_addr[11] = 0xFF;
+		outaddr->sin6_port = htons(atoi(port));
+		memcpy(outaddr->sin6_addr.s6_addr + 12, &v4addr, sizeof(v4addr));
+		outaddr->sin6_port = htons(atoi(port));
+	} else {
+		int error = getaddrinfo(host, port, &hint, &results);
+
+		require_action_string(
+		    !error,
+		    bail,
+		    ret = -1,
+		    gai_strerror(error)
+		    );
+
+		for (iter = results;
+		     iter && (iter->ai_family != AF_INET6);
+		     iter = iter->ai_next) ;
+
+		require_action(NULL != iter, bail, ret = -1);
+
+		memcpy(outaddr, iter->ai_addr, iter->ai_addrlen);
+	}
+
+bail:
+	if (results)
+		freeaddrinfo(results);
+
+	return ret;
+}
+
+#if HAVE_FORKPTY
+static int
+diagnose_forkpty_problem()
+{
+	int ret = -1;
+	// COM-S-7530: Do some more diagnostics on the situation, because
+	// sort of failure is weird. We step through some of the same sorts of
+	// things that forkpty would do so that we can see where we are failing.
+	int pty_master_fd = -1;
+	int pty_slave_fd = -1;
+	do {
+		if( access( "/dev/ptmx", F_OK ) < 0 ) {
+			syslog(LOG_WARNING, "Call to access(\"/dev/ptmx\",F_OK) failed: %s (%d)", strerror(errno), errno);
+			perror("access(\"/dev/ptmx\",F_OK)");
+		}
+
+		if( access( "/dev/ptmx", R_OK|W_OK ) < 0 ) {
+			syslog(LOG_WARNING, "Call to access(\"/dev/ptmx\",R_OK|W_OK) failed: %s (%d)", strerror(errno), errno);
+			perror("access(\"/dev/ptmx\",R_OK|W_OK)");
+		}
+
+		pty_master_fd = posix_openpt(O_NOCTTY | O_RDWR);
+
+		if (pty_master_fd < 0) {
+			syslog(LOG_CRIT, "Call to posix_openpt() failed: %s (%d)", strerror(errno), errno);
+			perror("posix_openpt(O_NOCTTY | O_RDWR)");
+			break;
+		}
+
+		if (grantpt(pty_master_fd) < 0) {
+			syslog(LOG_CRIT, "Call to grantpt() failed: %s (%d)", strerror(errno), errno);
+			perror("grantpt");
+		}
+
+		if (unlockpt(pty_master_fd) < 0) {
+			syslog(LOG_CRIT, "Call to unlockpt() failed: %s (%d)", strerror(errno), errno);
+			perror("unlockpt");
+		}
+
+#if HAVE_PTSNAME
+		if (NULL == ptsname(pty_master_fd)) {
+			syslog(LOG_CRIT, "Call to ptsname() failed: %s (%d)", strerror(errno), errno);
+			perror("ptsname");
+			break;
+		}
+
+		pty_slave_fd = open(ptsname(pty_master_fd), O_RDWR | O_NOCTTY);
+
+		if (pty_slave_fd < 0) {
+			syslog(LOG_CRIT, "Call to open(\"%s\",O_RDWR|O_NOCTTY) failed: %s (%d)", ptsname(pty_master_fd), strerror(errno), errno);
+			perror("open(ptsname(pty_master_fd),O_RDWR|O_NOCTTY)");
+			break;
+		}
+#endif
+
+		ret = 0;
+	} while(0);
+	close(pty_master_fd);
+	close(pty_slave_fd);
+	return ret;
+}
+
+static int
+open_system_socket_forkpty(const char* command)
+{
+	int ret_fd = -1;
+	int stderr_copy_fd;
+	pid_t pid;
+	struct termios tios = { .c_cflag = CS8|HUPCL|CREAD|CLOCAL };
+
+	cfmakeraw(&tios);
+
+	// Duplicate stderr so that we can hook it back up in the forked process.
+	stderr_copy_fd = dup(STDERR_FILENO);
+	if (stderr_copy_fd < 0) {
+		syslog(LOG_ERR, "Call to dup() failed: %s (%d)", strerror(errno), errno);
+		goto cleanup_and_fail;
+	}
+
+	pid = forkpty(&ret_fd, NULL, &tios, NULL);
+	if (pid < 0) {
+		syslog(LOG_ERR, "Call to forkpty() failed: %s (%d)", strerror(errno), errno);
+
+		if (0 == diagnose_forkpty_problem()) {
+			syslog(LOG_CRIT, "FORKPTY() FAILED BUT NOTHING WAS OBVIOUSLY WRONG!!!");
+		}
+
+		goto cleanup_and_fail;
+	}
+
+	// Check to see if we are the forked process or not.
+	if (0 == pid) {
+		// We are the forked process.
+		const int dtablesize = getdtablesize();
+		int i;
+
+		// Re-instate our original stderr.
+		dup2(stderr_copy_fd, STDERR_FILENO);
+
+		syslog(LOG_DEBUG, "Forked!");
+
+		// Re-instate our original stderr (clobbered by login_tty)
+		dup2(stderr_copy_fd, STDERR_FILENO);
+
+		// Set the shell environment variable if it isn't set already.
+		setenv("SHELL", "/bin/sh", 0);
+
+		// Close all file descriptors larger than STDERR_FILENO.
+		for (i = (STDERR_FILENO + 1); i < dtablesize; i++) {
+			close(i);
+		}
+
+		syslog(LOG_NOTICE,"About to exec \"%s\"",command);
+
+		execl(getenv("SHELL"),getenv("SHELL"),"-c",command,NULL);
+
+		syslog(LOG_ERR,"Failed for fork and exec of \"%s\": %s (%d)", command, strerror(errno), errno);
+
+		_exit(errno);
+	}
+
+	// Clean up our copy of stderr
+	close(stderr_copy_fd);
+
+#if HAVE_PTSNAME
+	// See http://stackoverflow.com/questions/3486491/
+	close(open(ptsname(ret_fd), O_RDWR | O_NOCTTY));
+#endif
+
+	return ret_fd;
+
+cleanup_and_fail:
+	{
+		int prevErrno = errno;
+
+		close(ret_fd);
+		close(stderr_copy_fd);
+
+		errno = prevErrno;
+	}
+	return -1;
+}
+#endif // HAVE_FORKPTY
+
+#ifdef PF_UNIX
+
+int
+fork_unixdomain_socket(int* fd_pointer)
+{
+	int fd[2] = { -1, -1 };
+	int i;
+	pid_t pid = -1;
+
+	if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd) < 0) {
+		syslog(LOG_ERR, "Call to socketpair() failed: %s (%d)", strerror(errno), errno);
+		goto bail;
+	}
+
+	pid = fork();
+	if (pid < 0) {
+		syslog(LOG_ERR, "Call to fork() failed: %s (%d)", strerror(errno), errno);
+		goto bail;
+	}
+
+	// Check to see if we are the forked process or not.
+	if (0 == pid) {
+		const int dtablesize = getdtablesize();
+		// We are the forked process.
+
+		close(fd[0]);
+
+		dup2(fd[1], STDIN_FILENO);
+		dup2(fd[1], STDOUT_FILENO);
+
+		syslog(LOG_DEBUG, "Forked!");
+
+        // Close all file descriptors larger than STDERR_FILENO.
+        for (i = (STDERR_FILENO + 1); i < dtablesize; i++) {
+            close(i);
+		}
+
+		*fd_pointer = STDIN_FILENO;
+	} else {
+		close(fd[1]);
+		*fd_pointer = fd[0];
+	}
+
+	fd[0] = -1;
+	fd[1] = -1;
+
+bail:
+
+	{
+		int prevErrno = errno;
+		if (fd[0] < 0) {
+			close(fd[0]);
+		}
+		if (fd[1] < 0) {
+			close(fd[1]);
+		}
+		errno = prevErrno;
+	}
+
+	return pid;
+}
+
+static int
+open_system_socket_unix_domain(const char* command)
+{
+	int fd = -1;
+	int status = 0;
+	pid_t pid = -1;
+
+	pid = fork_unixdomain_socket(&fd);
+
+	if (pid < 0) {
+		syslog(LOG_ERR, "Call to fork() failed: %s (%d)", strerror(errno), errno);
+		goto cleanup_and_fail;
+	}
+
+	// Check to see if we are the forked process or not.
+	if (0 == pid) {
+		// Double fork to avoid leaking zombie processes.
+		pid = fork();
+		if (pid < 0) {
+			syslog(LOG_ERR, "Call to fork() failed: %s (%d)", strerror(errno), errno);
+
+			_exit(errno);
+		}
+
+		if (0 == pid)
+		{
+			// Set the shell environment variable if it isn't set already.
+			setenv("SHELL","/bin/sh",0);
+
+			syslog(LOG_NOTICE, "About to exec \"%s\"", command);
+
+			execl(getenv("SHELL"), getenv("SHELL"),"-c", command, NULL);
+
+			syslog(LOG_ERR, "Failed for fork and exec of \"%s\": %s (%d)", command, strerror(errno), errno);
+
+			_exit(EXIT_FAILURE);
+		}
+
+		_exit(EXIT_SUCCESS);
+	}
+
+	// Wait for the first fork to return, and place the return value in errno
+	if (waitpid(pid, &status, 0) < 0) {
+		syslog(LOG_ERR, "Call to waitpid() failed: %s (%d)", strerror(errno), errno);
+	}
+
+	if (0 != WEXITSTATUS(status)) {
+		// If this has happened then the double fork failed. Clean up
+		// and pass this status along to the caller as errno.
+
+		syslog(LOG_ERR, "Child process failed: %s (%d)", strerror(WEXITSTATUS(status)), WEXITSTATUS(status));
+
+		close(fd);
+		fd = -1;
+
+		errno = WEXITSTATUS(status);
+	}
+
+	return fd;
+
+cleanup_and_fail:
+
+	if (fd >= 0) {
+		int prevErrno = errno;
+
+		close(fd);
+
+		errno = prevErrno;
+	}
+
+	return -1;
+}
+#endif // PF_UNIX
+
+static int
+open_system_socket(const char* command)
+{
+	int ret_fd = -1;
+
+#if HAVE_FORKPTY
+	ret_fd = open_system_socket_forkpty(command);
+#endif
+
+#if defined(PF_UNIX)
+	// Fall back to unix-domain socket-based mechanism:
+	if (ret_fd < 0) {
+		ret_fd = open_system_socket_unix_domain(command);
+	}
+#endif
+
+#if !HAVE_FORKPTY && !defined(PF_UNIX)
+	assert_printf("%s","Process pipe sockets are not supported in current configuration");
+	errno = ENOTSUP;
+#endif
+
+	return ret_fd;
+}
+
+int
+open_serial_socket(const char* socket_name)
+{
+	int fd = -1;
+	char* host = NULL; // Needs to be freed if set
+	const char* port = NULL;
+	char* options = strchr(socket_name, ',');
+	char* filename = strchr(socket_name, ':');
+	bool socket_name_is_well_formed = true; // True if socket has type name and options
+	enum {
+		SOCKET_TYPE_UNKNOWN,
+		SOCKET_TYPE_SYSTEM,
+		SOCKET_TYPE_SYSTEM_FORKPTY,
+		SOCKET_TYPE_SYSTEM_SOCKETPAIR,
+		SOCKET_TYPE_FD,
+		SOCKET_TYPE_TCP,
+		SOCKET_TYPE_FILE
+	} socket_type = SOCKET_TYPE_UNKNOWN;
+
+	// Move past the colon, if there was one.
+	if (NULL != filename) {
+		filename++;
+	} else {
+		filename = "";
+	}
+
+	if (strncasecmp(socket_name, SOCKET_SYSTEM_COMMAND_PREFIX, sizeof(SOCKET_SYSTEM_COMMAND_PREFIX)-1) == 0) {
+		socket_type = SOCKET_TYPE_SYSTEM;
+	} else if (strncasecmp(socket_name, SOCKET_SYSTEM_FORKPTY_COMMAND_PREFIX, sizeof(SOCKET_SYSTEM_FORKPTY_COMMAND_PREFIX)-1) == 0) {
+		socket_type = SOCKET_TYPE_SYSTEM_FORKPTY;
+	} else if (strncasecmp(socket_name, SOCKET_SYSTEM_SOCKETPAIR_COMMAND_PREFIX, sizeof(SOCKET_SYSTEM_SOCKETPAIR_COMMAND_PREFIX)-1) == 0) {
+		socket_type = SOCKET_TYPE_SYSTEM_SOCKETPAIR;
+	} else if (strncasecmp(socket_name, SOCKET_FD_COMMAND_PREFIX, sizeof(SOCKET_FD_COMMAND_PREFIX)-1) == 0) {
+		socket_type = SOCKET_TYPE_FD;
+	} else if (strncasecmp(socket_name, SOCKET_FILE_COMMAND_PREFIX, sizeof(SOCKET_FILE_COMMAND_PREFIX)-1) == 0) {
+		socket_type = SOCKET_TYPE_FILE;
+	} else if (strncasecmp(socket_name, SOCKET_SERIAL_COMMAND_PREFIX, sizeof(SOCKET_SERIAL_COMMAND_PREFIX)-1) == 0) {
+		socket_type = SOCKET_TYPE_FILE;
+	} else if (socket_name_is_inet(socket_name) || socket_name_is_port(socket_name)) {
+		socket_type = SOCKET_TYPE_TCP;
+		filename = (char*)socket_name;
+		options = NULL;
+		socket_name_is_well_formed = false;
+	} else if (socket_name_is_device(socket_name)) {
+		socket_type = SOCKET_TYPE_FILE;
+		filename = (char*)socket_name;
+		options = ",default";
+		socket_name_is_well_formed = false;
+	}
+
+	filename = strdup(filename);
+
+	if (NULL == filename) {
+		perror("strdup");
+		syslog(LOG_ERR, "strdup failed. \"%s\" (%d)", strerror(errno), errno);
+		goto bail;
+	}
+
+	// Make sure we zero terminate the file name before
+	// any options. (this is why we needed to strdup)
+	if (socket_name_is_well_formed && (NULL != options)) {
+		const char* ptr = strchr(socket_name, ':');
+		if (NULL == ptr) {
+			ptr = socket_name;
+		} else {
+			ptr++;
+		}
+		filename[options-ptr] = 0;
+	}
+
+	if (SOCKET_TYPE_SYSTEM == socket_type) {
+		fd = open_system_socket(filename);
+#if HAVE_FORKPTY
+	} else if (SOCKET_TYPE_SYSTEM_FORKPTY == socket_type) {
+		fd = open_system_socket_forkpty(filename);
+#endif
+	} else if (SOCKET_TYPE_SYSTEM_SOCKETPAIR == socket_type) {
+		fd = open_system_socket_unix_domain(filename);
+	} else if (SOCKET_TYPE_FD == socket_type) {
+		fd = dup((int)strtol(filename, NULL, 0));
+	} else if (SOCKET_TYPE_FILE == socket_type) {
+		fd = open(filename, O_RDWR | O_NOCTTY | O_NONBLOCK);
+
+		if (fd >= 0) {
+			fcntl(fd, F_SETFL, O_NONBLOCK);
+			tcflush(fd, TCIOFLUSH);
+		}
+	} else if (SOCKET_TYPE_TCP == socket_type) {
+		struct sockaddr_in6 addr;
+
+		if (socket_name_is_port(socket_name)) {
+			port = socket_name;
+		} else {
+			ssize_t i;
+			if (socket_name[0] == '[') {
+				host = strdup(socket_name + 1);
+			} else {
+				host = strdup(socket_name);
+			}
+			for (i = strlen(host) - 1; i >= 0; --i) {
+				if (host[i] == ':' && port == NULL) {
+					host[i] = 0;
+					port = host + i + 1;
+					continue;
+				}
+				if (host[i] == ']') {
+					host[i] = 0;
+					break;
+				}
+				if (!isdigit(host[i]))
+					break;
+			}
+		}
+
+		if (0 != lookup_sockaddr_from_host_and_port(&addr, host, port)) {
+			syslog(LOG_ERR, "Unable to lookup \"%s\"", socket_name);
+			goto bail;
+		}
+
+		fd = socket(AF_INET6, SOCK_STREAM, 0);
+
+		if (fd < 0) {
+			syslog(LOG_ERR, "Unable to open socket. \"%s\" (%d)", strerror(errno), errno);
+			goto bail;
+		}
+
+		if (0 != connect(fd, (struct sockaddr*)&addr, sizeof(addr))) {
+			syslog(LOG_ERR, "Call to connect() failed. \"%s\" (%d)", strerror(errno), errno);
+			close(fd);
+			fd = -1;
+			goto bail;
+		}
+	} else {
+		syslog(LOG_ERR, "I don't know how to open \"%s\" (socket type %d)", socket_name, (int)socket_type);
+	}
+
+	if (fd < 0) {
+		syslog(LOG_ERR, "Unable to open socket. \"%s\" (%d) (filename = %s, type = %d)", strerror(errno), errno, filename, (int)socket_type);
+		goto bail;
+	}
+
+	// Configure the socket.
+	{
+		int flags = fcntl(fd, F_GETFL);
+		int i;
+		struct termios tios;
+		fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+
+#ifdef SO_NOSIGPIPE
+		int set = 0;
+		setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int));
+#endif
+
+#define FETCH_TERMIOS() do { if(0 != tcgetattr(fd, &tios)) { \
+			/* perror("tcgetattr"); */ \
+			syslog(LOG_DEBUG, "tcgetattr() failed. \"%s\" (%d)", strerror(errno), errno); \
+		} } while(0)
+
+#define COMMIT_TERMIOS() do { if(0 != tcsetattr(fd, TCSANOW, &tios)) { \
+			/* perror("tcsetattr"); */ \
+			syslog(LOG_DEBUG, "tcsetattr() failed. \"%s\" (%d)", strerror(errno), errno); \
+		} } while(0)
+
+		// Parse the options, if any
+		for (; NULL != options; options = strchr(options+1, ',')) {
+			if (strncasecmp(options, ",b", 2) == 0 && isdigit(options[2])) {
+				// Change Baud rate
+				FETCH_TERMIOS();
+				cfsetspeed(&tios, strtol(options+2,NULL,10));
+				COMMIT_TERMIOS();
+			} else if (strncasecmp(options, ",default", strlen(",default")) == 0) {
+				FETCH_TERMIOS();
+				for (i=0; i < NCCS; i++) {
+					tios.c_cc[i] = _POSIX_VDISABLE;
+				}
+
+				tios.c_cflag = (CS8|HUPCL|CREAD|CLOCAL);
+				tios.c_iflag = 0;
+				tios.c_oflag = 0;
+				tios.c_lflag = 0;
+
+				cfmakeraw(&tios);
+				cfsetspeed(&tios, gSocketWrapperBaud);
+				COMMIT_TERMIOS();
+			} else if (strncasecmp(options, ",raw", 4) == 0) {
+				// Raw mode
+				FETCH_TERMIOS();
+				cfmakeraw(&tios);
+				COMMIT_TERMIOS();
+			} else if (strncasecmp(options, ",clocal=", 8) == 0) {
+				FETCH_TERMIOS();
+				options = strchr(options,'=');
+				if (options[1] == '1') {
+					tios.c_cflag |= CLOCAL;
+				} else if (options[1] == '0') {
+					tios.c_cflag &= ~CLOCAL;
+				}
+				COMMIT_TERMIOS();
+			} else if (strncasecmp(options, ",ixoff=", 6) == 0) {
+				FETCH_TERMIOS();
+				options = strchr(options,'=');
+				if (options[1] == '1') {
+					tios.c_iflag |= IXOFF;
+				} else if (options[1] == '0') {
+					tios.c_iflag &= ~IXOFF;
+				}
+				COMMIT_TERMIOS();
+			} else if (strncasecmp(options, ",ixon=", 6) == 0) {
+				FETCH_TERMIOS();
+				options = strchr(options,'=');
+				if (options[1] == '1') {
+					tios.c_iflag |= IXON;
+				} else if (options[1] == '0') {
+					tios.c_iflag &= ~IXON;
+				}
+				COMMIT_TERMIOS();
+			} else if (strncasecmp(options, ",ixany=", 6) == 0) {
+				FETCH_TERMIOS();
+				options = strchr(options,'=');
+				if (options[1] == '1') {
+					tios.c_iflag |= IXANY;
+				} else if (options[1] == '0') {
+					tios.c_iflag &= ~IXANY;
+				}
+				COMMIT_TERMIOS();
+			} else if (strncasecmp(options, ",crtscts=", 9) == 0) {
+				FETCH_TERMIOS();
+				options = strchr(options,'=');
+				// Hardware flow control
+				if (options[1] == '1') {
+					syslog(LOG_DEBUG, "Using hardware flow control for serial socket.");
+					tios.c_cflag |= CRTSCTS;
+				} else if (options[1] == '0') {
+					tios.c_cflag &= ~CRTSCTS;
+				}
+				COMMIT_TERMIOS();
+			} else {
+				syslog(LOG_ERR, "Unknown option (%s)", options);
+			}
+		}
+	}
+bail:
+	free(filename);
+	free(host);
+	return fd;
+}
diff --git a/src/util/socket-utils.h b/src/util/socket-utils.h
new file mode 100644
index 0000000..b71df03
--- /dev/null
+++ b/src/util/socket-utils.h
@@ -0,0 +1,51 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef wpantund_socket_utils_h
+#define wpantund_socket_utils_h
+
+#include <stdbool.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#define SOCKET_SYSTEM_COMMAND_PREFIX	"system:"
+#define SOCKET_FD_COMMAND_PREFIX	"fd:"
+#define SOCKET_FILE_COMMAND_PREFIX	"file:"
+#define SOCKET_SERIAL_COMMAND_PREFIX	"serial:"
+#define SOCKET_SYSTEM_FORKPTY_COMMAND_PREFIX	"system-forkpty:"
+#define SOCKET_SYSTEM_SOCKETPAIR_COMMAND_PREFIX	"system-socketpair:"
+
+__BEGIN_DECLS
+extern int gSocketWrapperBaud;
+bool socket_name_is_system_command(const char* socket_name);
+bool socket_name_is_port(const char* socket_name);
+bool socket_name_is_inet(const char* socket_name);
+bool socket_name_is_device(const char* socket_name);
+int lookup_sockaddr_from_host_and_port( struct sockaddr_in6* outaddr, const char* host, const char* port);
+int open_serial_socket(const char* socket_name);
+int fd_has_error(int fd);
+
+int fork_unixdomain_socket(int* fd_pointer);
+
+__END_DECLS
+
+
+#endif
diff --git a/src/util/string-utils.c b/src/util/string-utils.c
new file mode 100644
index 0000000..b1a452c
--- /dev/null
+++ b/src/util/string-utils.c
@@ -0,0 +1,303 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      This file defines utility functions for manipulating and comparing
+ *      C-strings or other buffers.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include "string-utils.h"
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdlib.h>
+
+#define __USE_GNU // Needed for `strcasestr`
+#include <string.h>
+
+void
+memcpyrev(void *dest_, const void *src_, size_t len)
+{
+	uint8_t* const dest = dest_;
+	const uint8_t* const src = src_;
+	int i;
+
+	for (i = 0; i < len; i++) {
+		dest[i] = src[len - 1 - i];
+	}
+}
+
+int
+memcmprev(const void *dest_, const void *src_, size_t len)
+{
+	const uint8_t* const dest = dest_;
+	const uint8_t* const src = src_;
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < len && !ret; i++) {
+		ret = dest[i] - src[len - 1 - i];
+	}
+	return ret;
+}
+
+void
+reverse_bytes(uint8_t *bytes, size_t count)
+{
+	int i;
+
+	for (i = 0; i < count / 2; i++) {
+		uint8_t x = bytes[i];
+		bytes[i] = bytes[count - i - 1];
+		bytes[count - i - 1] = x;
+	}
+}
+
+int
+parse_string_into_data(uint8_t* buffer, size_t len, const char* c_str)
+{
+	int ret = 0;
+
+	if (NULL == buffer) {
+		len = 0;
+	}
+
+	while (*c_str != 0) {
+		char c = tolower(*c_str++);
+		if (!(isdigit(c) || (c >= 'a' && c <= 'f'))) {
+			continue;
+		}
+		c = isdigit(c) ? (c - '0') : (c - 'a' + 10);
+		if (len > 0) {
+			*buffer = (c << 4);
+			len--;
+		}
+		ret++;
+		if (!*c_str) {
+			break;
+		}
+		c = tolower(*c_str++);
+		if (!(isdigit(c) || (c >= 'a' && c <= 'f'))) {
+			continue;
+		}
+		c = isdigit(c) ? (c - '0') : (c - 'a' + 10);
+		*buffer++ |= c;
+	}
+
+	return ret;
+}
+
+int
+encode_data_into_string(
+    const uint8_t*  buffer,
+    size_t len,
+    char*                   c_str,
+    size_t c_str_max_len,
+    int pad_to
+    ) {
+	int ret = 0;
+
+	*c_str++ = '{';
+	ret++;
+	c_str_max_len--;
+	while (len && (c_str_max_len > 4)) {
+		uint8_t byte = *buffer++;
+		len--;
+		pad_to--;
+		*c_str++ = int_to_hex_digit(byte >> 4);
+		*c_str++ = int_to_hex_digit(byte & 0xF);
+		c_str_max_len -= 2;
+		ret += 2;
+	}
+
+	while (pad_to > 0 && (c_str_max_len > 4)) {
+		pad_to--;
+		*c_str++ = '0';
+		*c_str++ = '0';
+		c_str_max_len -= 2;
+		ret += 2;
+	}
+
+	*c_str++ = '}';
+	ret++;
+	c_str_max_len--;
+	*c_str++ = 0;
+	return ret;
+}
+
+bool
+strtobool(const char* string)
+{
+	bool ret = false;
+	switch(string[0]) {
+	case 'y':
+	case 'Y':
+	case 't':
+	case 'T':
+		ret = true;
+		break;
+
+	case 'n':
+	case 'N':
+	case 'f':
+	case 'F':
+		ret = false;
+		break;
+
+	default:
+		ret = (strtol(string, NULL, 0) != 0);
+		break;
+	}
+	return ret;
+}
+
+uint32_t
+strtomask_uint32(const char* in_string)
+{
+	char *tmp_string = strdup(in_string);
+	char *chan_ranges;      // points to a channel num or a range of channels
+	char *dash_location;    // points to location of the dash in a range of channels
+	uint8_t channel_start = 0;
+	uint8_t channel_stop = 0;
+	uint32_t mask = 0;
+
+	chan_ranges = strtok(tmp_string, ",");
+	while(chan_ranges != NULL) {
+		// loop to parse channels by comma (,)
+		// each fragment may include a range (-) of channels
+
+		dash_location = strchr(chan_ranges, '-');
+		if (dash_location != NULL) {
+			// process a range of channels
+			*dash_location = '\0';
+			dash_location++;
+			if (atoi(chan_ranges) < atoi(dash_location)) {
+				channel_start = atoi(chan_ranges);
+				channel_stop = atoi(dash_location);
+			} else {
+				channel_stop = atoi(chan_ranges);
+				channel_start = atoi(dash_location);
+			}
+
+			while (channel_start <= channel_stop) {
+				mask |= (1 << channel_start);
+				channel_start++;
+			}
+		} else {
+			// no range, just add channel to the scan mask
+
+			mask |= (1 << strtol(chan_ranges, NULL, 0));
+		}
+		chan_ranges = strtok(NULL, ",");
+	}
+	free(tmp_string);
+	return mask;
+}
+
+#include <syslog.h>
+
+int
+strtologmask(const char* value, int prev_mask)
+{
+	int mask = (int)strtol(value, NULL, 0);
+
+	if (mask == 0) {
+		mask = prev_mask;
+
+		if(strcasestr(value, "all") != NULL) {
+			if (strcasestr(value, "-all") != NULL) {
+				mask &= 0;
+			} else {
+				mask |= ~0;
+			}
+		}
+		if (strcasestr(value, "emerg") != NULL) {
+			if (strcasestr(value, "-emerg") != NULL) {
+				mask &= ~LOG_MASK(LOG_EMERG);
+			} else {
+				mask |= LOG_MASK(LOG_EMERG);
+			}
+		}
+		if (strcasestr(value, "alert") != NULL) {
+			if (strcasestr(value, "-alert") != NULL) {
+				mask &= ~LOG_MASK(LOG_ALERT);
+			} else {
+				mask |= LOG_MASK(LOG_ALERT);
+			}
+		}
+		if (strcasestr(value, "crit") != NULL) {
+			if (strcasestr(value, "-crit") != NULL) {
+				mask &= ~LOG_MASK(LOG_CRIT);
+			} else {
+				mask |= LOG_MASK(LOG_CRIT);
+			}
+		}
+		if (strcasestr(value, "err") != NULL) {
+			if (strcasestr(value, "-err") != NULL) {
+				mask &= ~LOG_MASK(LOG_ERR);
+			} else {
+				mask |= LOG_MASK(LOG_ERR);
+			}
+		}
+		if (strcasestr(value, "warn") != NULL) {
+			if (strcasestr(value, "-warn") != NULL) {
+				mask &= ~LOG_MASK(LOG_WARNING);
+			} else {
+				mask |= LOG_MASK(LOG_WARNING);
+			}
+		}
+		if (strcasestr(value, "notice") != NULL) {
+			if (strcasestr(value, "-notice") != NULL) {
+				mask &= ~LOG_MASK(LOG_NOTICE);
+			} else {
+				mask |= LOG_MASK(LOG_NOTICE);
+			}
+		}
+		if (strcasestr(value, "info") != NULL) {
+			if (strcasestr(value, "-info") != NULL) {
+				mask &= ~LOG_MASK(LOG_INFO);
+			} else {
+				mask |= LOG_MASK(LOG_INFO);
+			}
+		}
+		if (strcasestr(value, "debug") != NULL) {
+			if (strcasestr(value, "-debug") != NULL) {
+				mask &= ~LOG_MASK(LOG_DEBUG);
+			} else {
+				mask |= LOG_MASK(LOG_DEBUG);
+			}
+		}
+	}
+
+	return mask;
+}
+
+bool
+buffer_is_nonzero(const uint8_t* buffer, size_t len)
+{
+	while (len--) {
+		if (*buffer++ != 0) {
+			return true;
+		}
+	}
+	return false;
+}
diff --git a/src/util/string-utils.h b/src/util/string-utils.h
new file mode 100644
index 0000000..528fbd2
--- /dev/null
+++ b/src/util/string-utils.h
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      This file declares utility functions for manipulating and comparing
+ *      C-strings or other buffers.
+ *
+ */
+
+#ifndef wpantund_string_utils_h
+#define wpantund_string_utils_h
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+#define strcaseequal(x, y)   (strcasecmp(x, y) == 0)
+#define strncaseequal(x, y, n)   (strncasecmp(x, y, n) == 0)
+#define strequal(x, y)   (strcmp(x, y) == 0)
+#define strnequal(x, y, n)   (strncmp(x, y, n) == 0)
+
+__BEGIN_DECLS
+extern void memcpyrev(void* dest, const void *src, size_t len);
+extern int memcmprev(const void* dest, const void *src, size_t len);
+extern void reverse_bytes(uint8_t *bytes, size_t count);
+extern int parse_string_into_data(uint8_t* buffer, size_t len, const char* c_str);
+extern int encode_data_into_string(const uint8_t*  buffer, size_t len, char* c_str, size_t c_str_max_len, int pad_to);
+extern int strtologmask(const char* value, int prev_mask);
+extern bool buffer_is_nonzero(const uint8_t* buffer, size_t len);
+
+static inline char
+int_to_hex_digit(uint8_t x)
+{
+	return "0123456789ABCDEF"[x & 0xF];
+}
+
+uint32_t strtomask_uint32(const char* in_string);
+
+extern bool strtobool(const char* string);
+
+__END_DECLS
+
+#endif
diff --git a/src/util/time-utils.c b/src/util/time-utils.c
new file mode 100644
index 0000000..40c4bd7
--- /dev/null
+++ b/src/util/time-utils.c
@@ -0,0 +1,70 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "time-utils.h"
+#include <sys/time.h>
+
+cms_t
+time_ms(void)
+{
+#if HAVE_CLOCK_GETTIME
+	struct timespec tv = { 0 };
+	int ret;
+
+	ret = clock_gettime(CLOCK_MONOTONIC, &tv);
+
+	return (cms_t)(tv.tv_sec * MSEC_PER_SEC) + (cms_t)(tv.tv_nsec / NSEC_PER_MSEC);
+#else
+	struct timeval tv = { 0 };
+	gettimeofday(&tv, NULL);
+	return (cms_t)(tv.tv_sec * MSEC_PER_SEC) + (cms_t)(tv.tv_usec / USEC_PER_MSEC);
+#endif
+}
+
+time_t
+time_get_monotonic(void)
+{
+#if HAVE_CLOCK_GETTIME
+	struct timespec ts;
+	int ret;
+
+	ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+
+	return ret == 0 ? ts.tv_sec : 0;
+#else
+	return time(NULL);
+#endif // !__linux__
+}
+
+cms_t
+cms_until_time(time_t time)
+{
+	time -= time_get_monotonic();
+
+	if (time > (TIME_DISTANT_FUTURE / MSEC_PER_SEC)) {
+		// Overflow.
+		return CMS_DISTANT_FUTURE;
+	}
+
+	return (cms_t)(time * MSEC_PER_SEC);
+}
diff --git a/src/util/time-utils.h b/src/util/time-utils.h
new file mode 100644
index 0000000..0dcae86
--- /dev/null
+++ b/src/util/time-utils.h
@@ -0,0 +1,66 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef wpantund_timeutils_h
+#define wpantund_timeutils_h
+
+#ifndef __STDC_CONSTANT_MACROS
+#define __STDC_CONSTANT_MACROS 1
+#endif
+
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/cdefs.h>
+#include <time.h>
+
+#ifndef MSEC_PER_SEC
+#define MSEC_PER_SEC	1000
+#endif
+
+#ifndef USEC_PER_MSEC
+#define USEC_PER_MSEC	1000
+#endif
+
+#ifndef NSEC_PER_MSEC
+#define NSEC_PER_MSEC	1000000
+#endif
+
+#ifndef CMS_DISTANT_FUTURE
+#define CMS_DISTANT_FUTURE			INT32_MAX
+#endif
+
+#ifndef TIME_DISTANT_FUTURE
+#define TIME_DISTANT_FUTURE			INTPTR_MAX
+#endif
+
+#define CMS_SINCE(x)	(time_ms() - (x))
+
+__BEGIN_DECLS
+typedef int32_t cms_t;
+
+extern cms_t time_ms(void);
+extern time_t time_get_monotonic(void);
+extern cms_t cms_until_time(time_t time);
+__END_DECLS
+
+#endif
diff --git a/src/util/tunnel.c b/src/util/tunnel.c
new file mode 100644
index 0000000..dc822a1
--- /dev/null
+++ b/src/util/tunnel.c
@@ -0,0 +1,667 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *		This file implements the code which managed the TUN interface.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef ASSERT_MACROS_USE_SYSLOG
+#define ASSERT_MACROS_USE_SYSLOG 1
+#endif
+
+#include "assert-macros.h"
+#include "pt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "tunnel.h"
+#include <syslog.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#ifndef __APPLE__
+#include <linux/if_tun.h>
+#endif
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <net/route.h> // AF_ROUTE things
+
+#ifdef __APPLE__
+#include <netinet6/in6_var.h>
+#include <netinet6/nd6.h>   // ND6_INFINITE_LIFETIME
+#include <net/if_dl.h>      // struct sockaddr_dl
+#include <net/if_utun.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/kern_control.h>
+#include <sys/ioctl.h>
+#include <sys/sys_domain.h>
+#include <sys/kern_event.h>
+#define IFEF_NOAUTOIPV6LL   0x2000  /* Interface IPv6 LinkLocal address not provided by kernel */
+#endif
+
+int
+tunnel_open(const char* tun_name)
+{
+	int fd = -1;
+	char *device = NULL;
+
+	if ((tun_name == NULL) || (tun_name[0] == 0)) {
+		tun_name = TUNNEL_DEFAULT_INTERFACE_NAME;
+	}
+
+	syslog(LOG_INFO, "Opening tun interface socket with name \"%s\"", tun_name);
+
+#if defined(UTUN_CONTROL_NAME)
+	int error = 0;
+	fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
+	struct sockaddr_ctl addr;
+
+	/* get/set the id */
+	struct ctl_info info;
+	memset(&info, 0, sizeof(info));
+	strncpy(info.ctl_name, UTUN_CONTROL_NAME, strlen(UTUN_CONTROL_NAME));
+	error = ioctl(fd, CTLIOCGINFO, &info);
+
+	if (error) {
+		syslog(LOG_ERR, "Failed to open utun interface: %s", strerror(errno));
+		close(fd);
+		fd = -1;
+		goto bail;
+	}
+
+	addr.sc_id = info.ctl_id;
+	addr.sc_len = sizeof(addr);
+	addr.sc_family = AF_SYSTEM;
+	addr.ss_sysaddr = AF_SYS_CONTROL;
+	addr.sc_unit = 0;  /* allocate dynamically */
+
+	if (strncmp(tun_name, "utun", 4) == 0)
+		addr.sc_unit = (int)strtol(tun_name + 4, NULL, 10) + 1;
+
+	error = connect(fd, (struct sockaddr*)&addr, sizeof(addr));
+
+	if (error && errno == EBUSY) {
+		addr.sc_unit = 0;  /* allocate dynamically */
+		error = connect(fd, (struct sockaddr*)&addr, sizeof(addr));
+	}
+
+	if (error) {
+		syslog(LOG_ERR, "Failed to open tun interface: %s", strerror(errno));
+		close(fd);
+		fd = -1;
+		goto bail;
+	}
+
+	tunnel_bring_offline(fd);
+
+	goto bail;
+
+#else
+
+#ifdef __APPLE__
+	if (strncmp(tun_name, "utun", 4) == 0)
+		tun_name = "tun0";
+	asprintf(&device, "/dev/%s", tun_name);
+#else
+	device = strdup("/dev/net/tun");
+#endif
+
+	require(NULL != device, bail);
+
+	fd = open(device, O_RDWR | O_NONBLOCK);
+
+	if (0 > fd) {
+		syslog(LOG_ERR, "Failed to open tun interface: %s", strerror(errno));
+		perror("open-tun");
+		goto bail;
+	}
+#endif
+
+#ifdef TUNSETIFF
+	struct ifreq ifr = { .ifr_flags = IFF_TUN | IFF_NO_PI };
+	strncpy(ifr.ifr_name, tun_name, IFNAMSIZ);
+
+	require(0 == ioctl(fd, TUNSETIFF, (void*)&ifr), bail);
+
+	// Verify that the name was set. If it wasn't
+	// we need to fail.
+	char name[20] = "";
+
+	if (tunnel_get_name(fd, name, sizeof(name)) != 0) {
+		syslog(LOG_ERR, "Unable to set name on tun interface: %s", strerror(errno));
+		perror("open-tun");
+		close(fd);
+		fd = -1;
+		goto bail;
+	}
+
+	if (name[0] == 0) {
+		syslog(LOG_ERR, "Unable to set name on tun interface");
+		close(fd);
+		fd = -1;
+		goto bail;
+	}
+
+#endif
+
+bail:
+	free(device);
+	return fd;
+}
+
+void
+tunnel_close(int fd)
+{
+	close(fd);
+}
+
+int
+tunnel_get_name(
+    int fd, char* name, int maxlen
+    )
+{
+	int ret = -1;
+
+	if (maxlen && name) name[0] = 0;
+#if defined(UTUN_CONTROL_NAME)
+	socklen_t len = maxlen;
+	if (0 == getsockopt(fd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, name, &len)) {
+		ret = 0;
+		goto bail;
+	}
+#elif defined(TUNGETIFF)
+	struct ifreq ifr = { };
+	require(0 == ioctl(fd, TUNGETIFF, (void*)&ifr), bail);
+	strncpy(name, ifr.ifr_name, maxlen);
+#else
+	struct stat st;
+	ret = fstat(fd, &st);
+	if (ret) {
+		perror("tunnel_get_name: fstat failed.");
+		goto bail;
+	}
+	devname_r(st.st_rdev, S_IFCHR, name, (int)maxlen);
+#endif
+	ret = 0;
+bail:
+	return ret;
+}
+
+static int
+_tunnel_get_iff(
+    int fd, struct ifreq *ifr
+    )
+{
+	int ret = -1;
+
+	ret = tunnel_get_name(fd, ifr->ifr_name, sizeof(ifr->ifr_name));
+
+	return ret;
+}
+
+
+bool
+tunnel_is_online(int fd)
+{
+	bool ret = false;
+	int status = -1;
+	int reqfd = -1;
+	struct ifreq ifr = { };
+
+	/* get interface name */
+	_tunnel_get_iff(fd, &ifr);
+
+	reqfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
+
+	status = ioctl(reqfd, SIOCGIFFLAGS, &ifr);
+	require_string(status == 0, bail, strerror(errno));
+
+	ret = ((ifr.ifr_flags & IFF_UP) == IFF_UP);
+
+bail:
+	close(reqfd);
+	return ret;
+}
+
+int
+tunnel_bring_online(int fd)
+{
+	int ret = -1;
+	int reqfd = -1;
+	struct ifreq ifr = { };
+
+	/* get interface name */
+	_tunnel_get_iff(fd, &ifr);
+
+	reqfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
+
+	ret = ioctl(reqfd, SIOCGIFFLAGS, &ifr);
+	require_string(ret == 0, bail, strerror(errno));
+
+	ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
+	ret = ioctl(reqfd, SIOCSIFFLAGS, &ifr);
+	require_string(ret == 0, bail, strerror(errno));
+
+bail:
+	close(reqfd);
+	return ret;
+}
+
+int
+tunnel_bring_offline(int fd)
+{
+	int ret = -1;
+	int reqfd = -1;
+	struct ifreq ifr = { };
+
+	/* get interface name */
+	_tunnel_get_iff(fd, &ifr);
+
+	reqfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
+
+	ret = ioctl(reqfd, SIOCGIFFLAGS, &ifr);
+	require_string(ret == 0, bail, strerror(errno));
+
+	ifr.ifr_flags &= ~(IFF_UP);
+	ret = ioctl(reqfd, SIOCSIFFLAGS, &ifr);
+	require_string(ret == 0, bail, strerror(errno));
+
+bail:
+	close(reqfd);
+	return ret;
+}
+
+int
+tunnel_set_mtu(
+    int fd, uint16_t mtu
+    )
+{
+	int ret = -1;
+	int reqfd = -1;
+	struct ifreq ifr = { };
+
+	/* get interface name */
+	_tunnel_get_iff(fd, &ifr);
+
+	reqfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
+
+	ifr.ifr_mtu = mtu;
+
+	ret = ioctl(reqfd, SIOCSIFMTU, &ifr);
+
+	if (ret)
+		perror("tapdev: Uable to set MTU, call to ioctl failed.");
+
+	close(reqfd);
+	return ret;
+}
+#ifndef SIOCSIFLLADDR
+#define SIOCSIFLLADDR SIOCSIFHWADDR
+#endif
+
+int
+tunnel_set_hw_address(
+    int fd, const uint8_t addr[8]
+    )
+{
+	int ret = -1;
+	bool was_online = tunnel_is_online(fd);
+
+	uint8_t ll_addr[16] = { 0xFE, 0x80 };
+
+	memcpy(ll_addr + 8, addr, 8);
+
+	ll_addr[8] = ll_addr[8] ^ 0x02;
+
+	tunnel_remove_address(fd, ll_addr);
+	ret = tunnel_add_address(fd, ll_addr, 10);
+
+	if (ret) {
+		if (errno == EALREADY)
+			ret = 0;
+	}
+
+	if (!was_online && tunnel_is_online(fd))
+		tunnel_bring_offline(fd);
+
+	return ret;
+}
+
+static inline void
+apply_mask(
+    struct in6_addr *address, uint8_t mask
+    )
+{
+	// Coverty might complain in this function, but don't believe it.
+	// This code has been reviewed carefully and should not misbehave.
+
+	if (mask > 128) {
+		mask = 128;
+	}
+
+	memset(
+		(void*)(address->s6_addr + ((mask + 7) / 8)),
+	    0,
+	    16 - ((mask + 7) / 8)
+	);
+
+	if (mask % 8) {
+		address->s6_addr[mask / 8] &= ~(0xFF >> (mask % 8));
+	}
+}
+
+int
+tunnel_add_address(
+    int fd, const uint8_t addr[16], int prefixlen
+    )
+{
+	int ret = -1;
+	int reqfd = -1;
+
+	reqfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
+
+#ifdef __APPLE__
+
+	/************* Add address *************/
+
+	struct in6_aliasreq addreq6 = { };
+
+	ret = tunnel_get_name(fd, addreq6.ifra_name, sizeof(addreq6.ifra_name));
+
+	if (ret) {
+		perror("tunnel_add_address: Uable to get interface name.");
+		goto bail;
+	}
+
+	addreq6.ifra_addr.sin6_family = AF_INET6;
+	addreq6.ifra_addr.sin6_len = sizeof(addreq6.ifra_addr);
+	memcpy((void*)&addreq6.ifra_addr.sin6_addr, addr, 16);
+
+	addreq6.ifra_prefixmask.sin6_family = AF_INET6;
+	addreq6.ifra_prefixmask.sin6_len = sizeof(addreq6.ifra_prefixmask);
+	memset((void*)&addreq6.ifra_prefixmask.sin6_addr, 0xFF, 16);
+	apply_mask(&addreq6.ifra_prefixmask.sin6_addr, prefixlen);
+
+	addreq6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+	addreq6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
+	addreq6.ifra_lifetime.ia6t_expire = ND6_INFINITE_LIFETIME;
+	addreq6.ifra_lifetime.ia6t_preferred = ND6_INFINITE_LIFETIME;
+
+	addreq6.ifra_flags |= IN6_IFF_NODAD;
+
+	ret = ioctl(reqfd, SIOCAIFADDR_IN6, &addreq6);
+	if (ret && errno != EALREADY)
+		goto bail;
+
+#else
+
+	/* Linux */
+
+	// In linux, we need to remove the address first.
+	tunnel_remove_address(fd, addr);
+
+#define ifreq_offsetof(x)  offsetof(struct ifreq, x)
+
+	struct in6_ifreq {
+		struct in6_addr ifr6_addr;
+		__u32 ifr6_prefixlen;
+		unsigned int ifr6_ifindex;
+	};
+
+	struct ifreq ifr = { };
+	struct sockaddr_in6 sai;
+	int sockfd;
+	struct in6_ifreq ifr6;
+
+	/* get interface name */
+	_tunnel_get_iff(fd, &ifr);
+
+	memset(&sai, 0, sizeof(struct sockaddr));
+	sai.sin6_family = AF_INET6;
+	sai.sin6_port = 0;
+
+	memcpy((void*)&sai.sin6_addr, addr, 16);
+
+	memcpy((char*)&ifr6.ifr6_addr, (char*)&sai.sin6_addr,
+	       sizeof(struct in6_addr));
+
+	if (ioctl(reqfd, SIOGIFINDEX, &ifr) < 0)
+		perror("SIOGIFINDEX");
+	ifr6.ifr6_ifindex = ifr.ifr_ifindex;
+	ifr6.ifr6_prefixlen = 64;
+	ret = ioctl(reqfd, SIOCSIFADDR, &ifr6);
+	if (ret && errno != EALREADY)
+		goto bail;
+	ret = 0;
+
+#endif
+	ret = 0;
+bail:
+	if (ret) {
+		syslog(LOG_INFO, "tunnel_add_address: errno=%d (%s)", errno, strerror(errno));
+	}
+	if (reqfd >= 0)
+		close(reqfd);
+
+	return ret;
+}
+
+int
+tunnel_remove_address(
+    int fd, const uint8_t addr[16]
+    )
+{
+	int ret = -1;
+
+	int reqfd = -1;
+
+	reqfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
+
+	struct sockaddr_in6 sai = { };
+
+	memset(&sai, 0, sizeof(struct sockaddr));
+	sai.sin6_family = AF_INET6;
+	sai.sin6_port = 0;
+	memcpy((void*)&sai.sin6_addr, addr, 16);
+
+	/************* Remove address *************/
+
+#ifdef __APPLE__
+	sai.sin6_len = sizeof(sai);
+
+
+	struct in6_ifreq ifreq6 = { };
+	ret = tunnel_get_name(fd, ifreq6.ifr_name, sizeof(ifreq6.ifr_name));
+
+	if (ret)
+		goto bail;
+
+	ifreq6.ifr_addr = sai;
+
+	if (-1 == ioctl(reqfd, SIOCDIFADDR_IN6, &ifreq6)) {
+		ret = -errno;
+		goto bail;
+	}
+
+#else
+	int ifindex = 0;
+	{
+		struct ifreq ifr = { };
+
+		/* get interface name */
+		_tunnel_get_iff(fd, &ifr);
+
+		if (ioctl(reqfd, SIOGIFINDEX, &ifr) < 0)
+			perror("SIOGIFINDEX");
+
+		ifindex = ifr.ifr_ifindex;
+	}
+
+	struct in6_ifreq {
+		struct in6_addr ifr6_addr;
+		__u32 ifr6_prefixlen;
+		unsigned int ifr6_ifindex;
+	};
+
+	struct in6_ifreq ifr6;
+
+	ifr6.ifr6_addr = sai.sin6_addr;
+	ifr6.ifr6_ifindex = ifindex;
+	ifr6.ifr6_prefixlen = 64;
+
+	if (ioctl(reqfd, SIOCDIFADDR, &ifr6) < 0) {
+		ret = -errno;
+		goto bail;
+	}
+#endif
+	ret = 0;
+
+bail:
+	if (reqfd >= 0)
+		close(reqfd);
+
+	return ret;
+}
+
+
+int
+tunnel_add_route(
+    int fd, const uint8_t route[16], int prefixlen
+    )
+{
+	int ret = -1;
+	int reqfd = -1;
+
+	reqfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
+
+#ifdef __APPLE__
+
+	/************* Add ROUTE TODO *************/
+
+#else
+	/* Linux */
+
+	struct ifreq ifr;
+	struct in6_rtmsg rt;
+
+	memset(&ifr, 0, sizeof(struct ifreq));
+
+	/* get interface name */
+	_tunnel_get_iff(fd, &ifr);
+
+	memset(&rt, 0, sizeof(struct in6_rtmsg));
+	memcpy(rt.rtmsg_dst.s6_addr, route, sizeof(struct in6_addr));
+	rt.rtmsg_dst_len = prefixlen;
+	rt.rtmsg_flags = RTF_UP;
+	if (prefixlen == 128) {
+		rt.rtmsg_flags |= RTF_HOST;
+	}
+	rt.rtmsg_metric = 512;
+
+	if (ioctl(reqfd, SIOGIFINDEX, &ifr) < 0)
+		perror("SIOGIFINDEX");
+
+	rt.rtmsg_ifindex = ifr.ifr_ifindex;
+
+	ret = ioctl(reqfd, SIOCADDRT, &rt);
+	if (ret && errno != EALREADY && errno != EEXIST)
+		goto bail;
+
+#endif
+
+	ret = 0;
+bail:
+	if (ret) {
+		syslog(LOG_INFO, "tunnel_add_route: errno=%d (%s)", errno, strerror(errno));
+	}
+	if (reqfd >= 0)
+		close(reqfd);
+
+	return ret;
+}
+
+int
+tunnel_remove_route(
+    int fd, const uint8_t route[16], int prefixlen
+    )
+{
+    int ret = -1;
+    int reqfd = -1;
+
+    reqfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
+
+#ifdef __APPLE__
+
+    /************* Remove ROUTE TODO *************/
+
+#else
+    /* Linux */
+
+    struct ifreq ifr;
+    struct in6_rtmsg rt;
+
+    memset(&ifr, 0, sizeof(struct ifreq));
+
+    /* get interface name */
+    _tunnel_get_iff(fd, &ifr);
+
+    memset(&rt, 0, sizeof(struct in6_rtmsg));
+    memcpy(rt.rtmsg_dst.s6_addr, route, sizeof(struct in6_addr));
+    rt.rtmsg_dst_len = prefixlen;
+    rt.rtmsg_flags = RTF_UP;
+    if (prefixlen == 128) {
+        rt.rtmsg_flags |= RTF_HOST;
+    }
+    rt.rtmsg_metric = 512;
+
+    if (ioctl(reqfd, SIOGIFINDEX, &ifr) < 0)
+        perror("SIOGIFINDEX");
+
+    rt.rtmsg_ifindex = ifr.ifr_ifindex;
+
+    ret = ioctl(reqfd, SIOCDELRT, &rt);
+    if (ret && errno != EALREADY && errno != EEXIST)
+        goto bail;
+
+#endif
+
+    ret = 0;
+bail:
+    if (ret) {
+        syslog(LOG_INFO, "tunnel_remove_route: errno=%d (%s)", errno, strerror(errno));
+    }
+    if (reqfd >= 0)
+        close(reqfd);
+
+    return ret;
+}
diff --git a/src/util/tunnel.h b/src/util/tunnel.h
new file mode 100644
index 0000000..0b5abd6
--- /dev/null
+++ b/src/util/tunnel.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *		This file implements the code which managed the TUN interface.
+ *
+ */
+
+
+#ifndef wpantund_tunnel_h
+#define wpantund_tunnel_h
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __APPLE__
+#define TUNNEL_DEFAULT_INTERFACE_NAME   "utun2"
+#else
+#define TUNNEL_DEFAULT_INTERFACE_NAME   "wpan0"
+#endif
+
+#define TUNNEL_MAX_INTERFACE_NAME_LEN	60
+
+__BEGIN_DECLS
+extern int tunnel_open(const char* tun_name);
+extern int tunnel_get_name(
+    int fd, char* name, int maxlen);
+extern int tunnel_set_mtu(
+    int fd, uint16_t mtu);
+extern int tunnel_set_hw_address(
+    int fd, const uint8_t addr[8]);
+extern int tunnel_add_address(
+    int fd, const uint8_t addr[16], int prefixlen);
+extern int tunnel_remove_address(
+    int fd, const uint8_t addr[16]);
+extern void tunnel_close(int fd);
+extern int tunnel_bring_online(int fd);
+extern int tunnel_bring_offline(int fd);
+extern bool tunnel_is_online(int fd);
+extern int tunnel_add_route(
+    int fd, const uint8_t route[16], int prefixlen);
+extern int tunnel_remove_route(
+    int fd, const uint8_t route[16], int prefixlen);
+__END_DECLS
+
+
+#endif
diff --git a/src/version.c.in b/src/version.c.in
new file mode 100644
index 0000000..dcefa39
--- /dev/null
+++ b/src/version.c.in
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "version.h"
+
+const char *internal_build_source_version = SOURCE_VERSION;
+const char *internal_build_date = __DATE__ " " __TIME__;
diff --git a/src/version.h b/src/version.h
new file mode 100644
index 0000000..be8524c
--- /dev/null
+++ b/src/version.h
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef INTERNAL_VERSION_H__
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+extern const char *internal_build_source_version;
+extern const char *internal_build_date;
+__END_DECLS
+
+#endif // INTERNAL_VERSION_H__
diff --git a/src/wpanctl/Makefile.am b/src/wpanctl/Makefile.am
new file mode 100644
index 0000000..7e687fe
--- /dev/null
+++ b/src/wpanctl/Makefile.am
@@ -0,0 +1,96 @@
+#
+# Copyright (c) 2016 Nest Labs, Inc.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src \
+	-I$(top_srcdir)/src/ipc-dbus \
+	-I$(top_srcdir)/src/util \
+	-I$(top_srcdir)/src/wpantund \
+	-I$(top_srcdir)/third_party/fgetln \
+	-I$(top_srcdir)/third_party/assert-macros \
+	$(NULL)
+
+DISTCLEANFILES = .deps Makefile
+
+bin_PROGRAMS = wpanctl
+
+wpanctl_SOURCES = \
+	wpanctl.c \
+	wpanctl-cmds.h \
+	../util/config-file.c \
+	../util/string-utils.c \
+	wpanctl-utils.c \
+	tool-cmd-scan.c \
+	tool-cmd-join.c \
+	tool-cmd-form.c \
+	tool-cmd-leave.c \
+	tool-cmd-permit-join.c \
+	tool-cmd-list.c \
+	tool-cmd-status.c \
+	tool-cmd-resume.c \
+	tool-cmd-reset.c \
+	tool-cmd-getprop.c \
+	tool-cmd-setprop.c \
+	tool-cmd-begin-low-power.c \
+	tool-cmd-begin-net-wake.c \
+	tool-cmd-cd.c \
+	tool-cmd-poll.c \
+	tool-cmd-config-gateway.c \
+	tool-cmd-host-did-wake.c \
+	tool-cmd-add-route.c \
+	tool-cmd-remove-route.c \
+	wpanctl-utils.h \
+	tool-cmd-scan.h \
+	tool-cmd-join.h \
+	tool-cmd-form.h \
+	tool-cmd-leave.h \
+	tool-cmd-permit-join.h \
+	tool-cmd-list.h \
+	tool-cmd-status.h \
+	tool-cmd-resume.h \
+	tool-cmd-reset.h \
+	tool-cmd-getprop.h \
+	tool-cmd-setprop.h \
+	tool-cmd-begin-low-power.h \
+	tool-cmd-begin-net-wake.h \
+	tool-cmd-cd.h \
+	tool-cmd-poll.h \
+	tool-cmd-config-gateway.h \
+	tool-cmd-host-did-wake.h \
+	tool-cmd-add-route.h \
+	tool-cmd-remove-route.h \
+	$(NULL)
+
+wpanctl_LDADD = \
+	$(DBUS_LIBS) \
+	$(LIBREADLINE_LIBS) \
+	$(NULL)
+
+wpanctl_CPPFLAGS = \
+	$(AM_CPPFLAGS) \
+	$(DBUS_CFLAGS) \
+	$(LIBREADLINE_CPPFLAGS) \
+	$(NULL)
+
+
+SOURCE_VERSION=$(shell git describe --dirty --always --match "[0-9].*" 2> /dev/null)
+BUILT_SOURCES  = $(top_builddir)/$(subdir)/version.c
+CLEANFILES = $(top_builddir)/$(subdir)/version.c
+.INTERMEDIATE: $(top_builddir)/$(subdir)/version.c wpanctl-version.$(OBJEXT)
+$(top_builddir)/$(subdir)/version.c: ../version.c.in Makefile
+	sed 's/SOURCE_VERSION/"$(SOURCE_VERSION)"/' < $< > $@
+wpanctl_SOURCES += $(top_builddir)/$(subdir)/version.c
diff --git a/src/wpanctl/tool-cmd-add-route.c b/src/wpanctl/tool-cmd-add-route.c
new file mode 100644
index 0000000..c615c2f
--- /dev/null
+++ b/src/wpanctl/tool-cmd-add-route.c
@@ -0,0 +1,258 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      This file implements "add-route" command in wpanctl.
+ *
+ */
+
+#include <getopt.h>
+#include "wpanctl-utils.h"
+#include "tool-cmd-add-route.h"
+#include "assert-macros.h"
+#include "args.h"
+#include "assert-macros.h"
+#include "wpan-dbus-v1.h"
+#include "string-utils.h"
+
+#include <arpa/inet.h>
+#include <errno.h>
+
+const char add_route_cmd_syntax[] = "[args] <prefix>";
+
+static const arg_list_item_t add_route_option_list[] = {
+	{'h', "help", NULL, "Print Help"},
+	{'p', "priority", "(>0 for high, 0 for medium, <0 for low)", "Assign route priority"},
+	{'l', "length", "in bytes", "Specifies the route prefix length (default is 8)"},
+	{'d', "domain", NULL, "Domain id for the route (default is zero)"},
+	{0}
+};
+
+int tool_cmd_add_route(int argc, char* argv[])
+{
+	int ret = 0;
+	int c;
+	int timeout = DEFAULT_TIMEOUT_IN_SECONDS * 1000;
+
+	DBusConnection* connection = NULL;
+	DBusMessage *message = NULL;
+	DBusMessage *reply = NULL;
+	DBusError error;
+
+	const char *route_prefix = NULL;
+	int prefix_len = 8;
+	int16_t priority = 0;
+	uint16_t domain_id = 0;
+
+	dbus_error_init(&error);
+
+	while (1) {
+		static struct option long_options[] = {
+			{"help", no_argument, 0, 'h'},
+			{"priority", required_argument, 0, 'p'},
+			{"length", required_argument, 0, 'l'},
+			{"domain", required_argument, 0, 'd'},
+			{0, 0, 0, 0}
+		};
+
+		int option_index = 0;
+		c = getopt_long(argc, argv, "hp:l:d:", long_options,
+				&option_index);
+
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_arg_list_help(add_route_option_list, argv[0],
+					    add_route_cmd_syntax);
+			ret = ERRORCODE_HELP;
+			goto bail;
+
+		case 'p':
+			priority = (int16_t) strtol(optarg, NULL, 0);
+			break;
+
+		case 'l' :
+			prefix_len = (int) strtol(optarg, NULL, 0);
+			break;
+
+		case 'd':
+			domain_id = (uint16_t) strtol(optarg, NULL, 0);
+			break;
+		}
+	}
+
+	if (optind < argc) {
+		route_prefix = argv[optind];
+		optind++;
+	}
+
+	if (optind < argc) {
+		fprintf(stderr,
+		        "%s: error: Unexpected extra argument: \"%s\"\n",
+		        argv[0], argv[optind]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	if (gInterfaceName[0] == 0) {
+		fprintf(stderr,
+		        "%s: error: No WPAN interface set (use the `cd` command, or the `-I` argument for `wpanctl`).\n",
+		        argv[0]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
+
+	if (!connection) {
+		dbus_error_free(&error);
+		dbus_error_init(&error);
+		connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+	}
+
+	require_string(connection != NULL, bail, error.message);
+
+	{
+		DBusMessageIter iter;
+		DBusMessageIter list_iter;
+		char path[DBUS_MAXIMUM_NAME_LENGTH+1];
+		char interface_dbus_name[DBUS_MAXIMUM_NAME_LENGTH+1];
+		ret = lookup_dbus_name_from_interface(interface_dbus_name, gInterfaceName);
+		if (ret != 0) {
+			goto bail;
+		}
+		snprintf(path,
+		         sizeof(path),
+		         "%s/%s",
+		         WPANTUND_DBUS_PATH,
+		         gInterfaceName);
+
+		message = dbus_message_new_method_call(
+		    interface_dbus_name,
+		    path,
+		    WPANTUND_DBUS_APIv1_INTERFACE,
+		    WPANTUND_IF_CMD_ROUTE_ADD
+		);
+
+		if ((route_prefix != NULL) && (0 <= prefix_len) && (prefix_len <= 16)) {
+			uint8_t prefix_bytes[16];
+
+			memset(prefix_bytes, 0, sizeof(prefix_bytes));
+
+			// So the prefix could either be
+			// specified like an IPv6 address, or
+			// specified as a bunch of hex numbers.
+			// We use the presence of a colon (':')
+			// to differentiate.
+			if (strstr(route_prefix, ":")) {
+
+				// Address-style
+				int bits = inet_pton(AF_INET6, route_prefix, prefix_bytes);
+				if (bits < 0) {
+					fprintf(stderr,
+					        "Bad prefix \"%s\", errno=%d (%s)\n",
+					        route_prefix,
+					        errno,
+					        strerror(errno));
+					goto bail;
+				} else if (bits == 0) {
+					fprintf(stderr, "Bad prefix \"%s\"\n", route_prefix);
+					goto bail;
+				}
+			} else {
+				// DATA-style
+				int length = parse_string_into_data(prefix_bytes,
+				                                    16,
+				                                    route_prefix);
+				if(length<=0) {
+					fprintf(stderr, "Bad prefix \"%s\"\n", route_prefix);
+					goto bail;
+				}
+			}
+
+			fprintf(stderr, "Adding route prefix \"%s\" with len %d, priority \"%s\", domain-id %d.\n",
+				route_prefix, prefix_len,
+				(priority > 0)? "high" : ((priority < 0)? "low" : "medium"),
+				domain_id
+			);
+
+			uint8_t *addr = prefix_bytes;
+			uint8_t len = (uint8_t)prefix_len;
+
+			dbus_message_append_args(
+			    message,
+			    DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &addr, len,
+			    DBUS_TYPE_INVALID
+			);
+		} else {
+			dbus_message_append_args(
+			    message,
+			    DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, "", 0,
+			    DBUS_TYPE_INVALID
+			);
+		}
+
+		dbus_message_append_args(
+		    message,
+		    DBUS_TYPE_UINT16, &domain_id,
+		    DBUS_TYPE_INT16, &priority,
+		    DBUS_TYPE_INVALID
+		);
+
+		reply = dbus_connection_send_with_reply_and_block(
+		    connection,
+		    message,
+		    timeout,
+		    &error
+		);
+
+		if (!reply) {
+			fprintf(stderr, "%s: error: %s\n", argv[0], error.message);
+			ret = ERRORCODE_TIMEOUT;
+			goto bail;
+		}
+
+		dbus_message_get_args(reply, &error,
+		                      DBUS_TYPE_INT32, &ret,
+		                      DBUS_TYPE_INVALID
+		                      );
+
+		if (!ret) {
+			fprintf(stderr, "Route prefix added.\n");
+		} else {
+			fprintf(stderr, "%s failed with error %d. %s\n", argv[0], ret, wpantund_status_to_cstr(ret));
+			print_error_diagnosis(ret);
+		}
+	}
+
+bail:
+
+	if (connection)
+		dbus_connection_unref(connection);
+
+	if (message)
+		dbus_message_unref(message);
+
+	if (reply)
+		dbus_message_unref(reply);
+
+	dbus_error_free(&error);
+
+	return ret;
+}
diff --git a/src/wpanctl/tool-cmd-add-route.h b/src/wpanctl/tool-cmd-add-route.h
new file mode 100644
index 0000000..be200ec
--- /dev/null
+++ b/src/wpanctl/tool-cmd-add-route.h
@@ -0,0 +1,31 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      This file declares function related to "add-route" command
+ *      in wpanctl.
+ *
+ */
+
+#ifndef WPANCTL_TOOL_CMD_ADD_ROUTE_H
+#define WPANCTL_TOOL_CMD_ADD_ROUTE_H
+
+#include "wpanctl-utils.h"
+
+int tool_cmd_add_route(int argc, char* argv[]);
+
+#endif
diff --git a/src/wpanctl/tool-cmd-begin-low-power.c b/src/wpanctl/tool-cmd-begin-low-power.c
new file mode 100644
index 0000000..7dc3250
--- /dev/null
+++ b/src/wpanctl/tool-cmd-begin-low-power.c
@@ -0,0 +1,168 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <getopt.h>
+#include "wpanctl-utils.h"
+#include "tool-cmd-begin-low-power.h"
+#include "assert-macros.h"
+#include "args.h"
+#include "assert-macros.h"
+#include "wpan-dbus-v1.h"
+
+const char begin_low_power_cmd_syntax[] = "[args]";
+
+static const arg_list_item_t begin_low_power_option_list[] = {
+	{'h', "help", NULL, "Print Help"},
+	{'t', "timeout", "ms", "Set timeout period"},
+	{0}
+};
+
+int tool_cmd_begin_low_power(int argc, char* argv[])
+{
+	int ret = 0;
+	int c;
+	int timeout = 30 * 1000;
+	DBusConnection* connection = NULL;
+	DBusMessage *message = NULL;
+	DBusMessage *reply = NULL;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	while (1) {
+		static struct option long_options[] = {
+			{"help", no_argument, 0, 'h'},
+			{"timeout", required_argument, 0, 't'},
+			{0, 0, 0, 0}
+		};
+
+		int option_index = 0;
+		c = getopt_long(argc, argv, "ht:", long_options, &option_index);
+
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_arg_list_help(begin_low_power_option_list, argv[0],
+					    begin_low_power_cmd_syntax);
+			ret = ERRORCODE_HELP;
+			goto bail;
+
+		case 't':
+			timeout = strtol(optarg, NULL, 0);
+			break;
+		}
+	}
+
+	if (optind < argc) {
+		fprintf(stderr,
+		        "%s: error: Unexpected extra argument: \"%s\"\n",
+			argv[0], argv[optind]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	if (gInterfaceName[0] == 0) {
+		fprintf(stderr,
+		        "%s: error: No WPAN interface set (use the `cd` command, or the `-I` argument for `wpanctl`).\n",
+		        argv[0]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
+
+	if (!connection) {
+		dbus_error_free(&error);
+		dbus_error_init(&error);
+		connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+	}
+
+	require_string(connection != NULL, bail, error.message);
+
+	{
+		DBusMessageIter iter;
+		DBusMessageIter list_iter;
+		char path[DBUS_MAXIMUM_NAME_LENGTH+1];
+		char interface_dbus_name[DBUS_MAXIMUM_NAME_LENGTH+1];
+		ret = lookup_dbus_name_from_interface(interface_dbus_name, gInterfaceName);
+		if (ret != 0) {
+			goto bail;
+		}
+		snprintf(path,
+		         sizeof(path),
+		         "%s/%s",
+		         WPANTUND_DBUS_PATH,
+		         gInterfaceName);
+
+		message = dbus_message_new_method_call(
+		    interface_dbus_name,
+		    path,
+		    WPANTUND_DBUS_APIv1_INTERFACE,
+		    WPANTUND_IF_CMD_BEGIN_LOW_POWER
+		    );
+
+		fprintf(stderr, "Sending %s command. . .\n", WPANTUND_IF_CMD_BEGIN_LOW_POWER);
+
+		reply = dbus_connection_send_with_reply_and_block(
+		    connection,
+		    message,
+		    timeout,
+		    &error
+		    );
+
+		if (!reply) {
+			fprintf(stderr, "%s: error: %s\n", argv[0], error.message);
+			ret = ERRORCODE_TIMEOUT;
+			goto bail;
+		}
+
+		dbus_message_get_args(reply, &error,
+		                      DBUS_TYPE_INT32, &ret,
+		                      DBUS_TYPE_INVALID
+		                      );
+		if (ret == 6)
+			ret = 0;
+
+		if (ret) {
+			fprintf(stderr, "%s failed with error %d. %s\n", argv[0], ret, wpantund_status_to_cstr(ret));
+			print_error_diagnosis(ret);
+		}
+	}
+
+bail:
+
+	if (connection)
+		dbus_connection_unref(connection);
+
+	if (message)
+		dbus_message_unref(message);
+
+	if (reply)
+		dbus_message_unref(reply);
+
+	dbus_error_free(&error);
+
+	return ret;
+}
diff --git a/src/wpanctl/tool-cmd-begin-low-power.h b/src/wpanctl/tool-cmd-begin-low-power.h
new file mode 100644
index 0000000..79bdc3f
--- /dev/null
+++ b/src/wpanctl/tool-cmd-begin-low-power.h
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef WPANCTL_TOOL_CMD_LURK_H
+#define WPANCTL_TOOL_CMD_LURK_H
+
+#include "wpanctl-utils.h"
+
+int tool_cmd_begin_low_power(int argc, char* argv[]);
+
+#endif
diff --git a/src/wpanctl/tool-cmd-begin-net-wake.c b/src/wpanctl/tool-cmd-begin-net-wake.c
new file mode 100644
index 0000000..405f11c
--- /dev/null
+++ b/src/wpanctl/tool-cmd-begin-net-wake.c
@@ -0,0 +1,186 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      This file...
+ *
+ */
+
+#include <getopt.h>
+#include "wpanctl-utils.h"
+#include "tool-cmd-begin-net-wake.h"
+#include "assert-macros.h"
+#include "args.h"
+#include "assert-macros.h"
+#include "wpan-dbus-v0.h"
+
+const char begin_net_wake_cmd_syntax[] = "[args] <data>";
+
+static const arg_list_item_t begin_net_wake_option_list[] = {
+	{'h', "help", NULL, "Print Help"},
+	{'t', "timeout", "ms", "Set timeout period"},
+	{0}
+};
+
+int tool_cmd_begin_net_wake(int argc, char *argv[])
+{
+	int ret = 0;
+	int c;
+	int timeout = DEFAULT_TIMEOUT_IN_SECONDS * 1000;
+	DBusConnection* connection = NULL;
+	DBusMessage *message = NULL;
+	DBusMessage *reply = NULL;
+	DBusError error;
+
+	bool has_net_wake_data = false;
+	uint8_t net_wake_data = 0;
+	uint32_t net_wake_flags = -1;
+
+	dbus_error_init(&error);
+
+	while (1) {
+		static struct option long_options[] = {
+			{"help", no_argument, 0, 'h'},
+			{"timeout", required_argument, 0, 't'},
+			{0, 0, 0, 0}
+		};
+
+		int option_index = 0;
+		c = getopt_long(argc, argv, "ht:", long_options, &option_index);
+
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_arg_list_help(begin_net_wake_option_list,
+					    argv[0], begin_net_wake_cmd_syntax);
+			ret = ERRORCODE_HELP;
+			goto bail;
+
+		case 't':
+			timeout = strtol(optarg, NULL, 0);
+			break;
+		}
+	}
+
+	if (optind < argc) {
+		if (!has_net_wake_data) {
+			net_wake_data = strtol(argv[optind], NULL, 0);
+			has_net_wake_data = true;
+			optind++;
+		}
+	}
+
+	if (optind < argc) {
+		fprintf(stderr,
+		        "%s: error: Unexpected extra argument: \"%s\"\n",
+		        argv[0], argv[optind]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	if (gInterfaceName[0] == 0) {
+		fprintf(stderr,
+		        "%s: error: No WPAN interface set (use the `cd` command, or the `-I` argument for `wpanctl`).\n",
+		        argv[0]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
+
+	if (!connection) {
+		dbus_error_free(&error);
+		dbus_error_init(&error);
+		connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+	}
+
+	require_string(connection != NULL, bail, error.message);
+
+	{
+		DBusMessageIter iter;
+		DBusMessageIter list_iter;
+		char path[DBUS_MAXIMUM_NAME_LENGTH+1];
+		char interface_dbus_name[DBUS_MAXIMUM_NAME_LENGTH+1];
+		ret = lookup_dbus_name_from_interface(interface_dbus_name, gInterfaceName);
+		if (ret != 0) {
+			goto bail;
+		}
+		snprintf(path,
+		         sizeof(path),
+		         "%s/%s",
+		         WPAN_TUNNEL_DBUS_PATH,
+		         gInterfaceName);
+
+		message = dbus_message_new_method_call(
+		    interface_dbus_name,
+		    path,
+		    WPAN_TUNNEL_DBUS_INTERFACE,
+		    WPAN_IFACE_CMD_BEGIN_NET_WAKE
+		    );
+
+		dbus_message_append_args(
+		    message,
+		    DBUS_TYPE_BYTE, &net_wake_data,
+		    DBUS_TYPE_UINT32, &net_wake_flags,
+		    DBUS_TYPE_INVALID
+		    );
+
+		fprintf(stderr,
+				"Begin Net Wake, data = 0x%02X\n",
+				net_wake_data);
+
+		reply = dbus_connection_send_with_reply_and_block(
+		    connection,
+		    message,
+		    timeout,
+		    &error
+		    );
+
+		if (!reply) {
+			fprintf(stderr, "%s: error: %s\n", argv[0], error.message);
+			ret = ERRORCODE_TIMEOUT;
+			goto bail;
+		}
+
+		dbus_message_get_args(reply, &error,
+		                      DBUS_TYPE_INT32, &ret,
+		                      DBUS_TYPE_INVALID
+		                      );
+
+		if (ret) {
+			fprintf(stderr, "%s failed with error %d. %s\n", argv[0], ret, (ret<0)?strerror(-ret):"");
+			print_error_diagnosis(ret);
+		}
+	}
+
+bail:
+
+	if (connection)
+		dbus_connection_unref(connection);
+
+	if (message)
+		dbus_message_unref(message);
+
+	if (reply)
+		dbus_message_unref(reply);
+
+	dbus_error_free(&error);
+
+	return ret;
+}
diff --git a/src/wpanctl/tool-cmd-begin-net-wake.h b/src/wpanctl/tool-cmd-begin-net-wake.h
new file mode 100644
index 0000000..0c09648
--- /dev/null
+++ b/src/wpanctl/tool-cmd-begin-net-wake.h
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef WPANCTL_TOOL_CMD_BEGIN_NET_WAKE_H
+#define WPANCTL_TOOL_CMD_BEGIN_NET_WAKE_H
+
+#include "wpanctl-utils.h"
+
+int tool_cmd_begin_net_wake(int argc, char* argv[]);
+
+#endif
diff --git a/src/wpanctl/tool-cmd-cd.c b/src/wpanctl/tool-cmd-cd.c
new file mode 100644
index 0000000..9c7d7b0
--- /dev/null
+++ b/src/wpanctl/tool-cmd-cd.c
@@ -0,0 +1,48 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "wpanctl-utils.h"
+#include "tool-cmd-cd.h"
+#include <string.h>
+
+int tool_cmd_cd(int argc, char* argv[])
+{
+	int ret = 0;
+
+	if ((2 == argc) && (0 == strcmp(argv[1], "--help"))) {
+		fprintf(stderr,
+		        "%s: Help not yet implemented for this command.\n",
+		        argv[0]);
+		ret = ERRORCODE_HELP;
+	}
+
+	if (argc == 1) {
+		printf("%s\n", gInterfaceName);
+		ret = ERRORCODE_HELP;
+	} else if (argc == 2) {
+		strncpy(gInterfaceName, argv[1], sizeof(gInterfaceName) - 1);
+	}
+
+bail:
+	return ret;
+}
diff --git a/src/wpanctl/tool-cmd-cd.h b/src/wpanctl/tool-cmd-cd.h
new file mode 100644
index 0000000..82959c2
--- /dev/null
+++ b/src/wpanctl/tool-cmd-cd.h
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef WPANCTL_TOOL_CMD_CD_H
+#define WPANCTL_TOOL_CMD_CD_H
+
+#include "wpanctl-utils.h"
+
+int tool_cmd_cd(int argc, char* argv[]);
+
+#endif
diff --git a/src/wpanctl/tool-cmd-config-gateway.c b/src/wpanctl/tool-cmd-config-gateway.c
new file mode 100644
index 0000000..1947da7
--- /dev/null
+++ b/src/wpanctl/tool-cmd-config-gateway.c
@@ -0,0 +1,268 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <getopt.h>
+#include "wpanctl-utils.h"
+#include "tool-cmd-config-gateway.h"
+#include "assert-macros.h"
+#include "args.h"
+#include "assert-macros.h"
+#include "wpan-dbus-v1.h"
+#include "string-utils.h"
+
+#include <arpa/inet.h>
+#include <errno.h>
+
+const char config_gateway_cmd_syntax[] = "[args] <prefix>";
+
+static const arg_list_item_t config_gateway_option_list[] = {
+	{'h', "help", NULL, "Print Help"},
+	{'t', "timeout", "ms", "Set timeout period"},
+
+	{'p', "preferred-lifetime", "seconds",
+	 "Set the preferred lifetime (Default: infinite)"},
+	{'v', "valid-lifetime", "seconds",
+	 "Set the valid lifetime (Default: infinite)"},
+	{'d', "default", NULL, "Indicates that we can be a default route"},
+	{0}
+};
+
+int tool_cmd_config_gateway(int argc, char* argv[])
+{
+	int ret = 0;
+	int c;
+	int timeout = DEFAULT_TIMEOUT_IN_SECONDS * 1000;
+	DBusConnection* connection = NULL;
+	DBusMessage *message = NULL;
+	DBusMessage *reply = NULL;
+	DBusError error;
+	dbus_bool_t defaultRoute = FALSE;
+	uint32_t preferredLifetime = 0xFFFFFFFF;
+	uint32_t validLifetime = 0xFFFFFFFF;
+	const char* prefix = NULL;
+
+	dbus_error_init(&error);
+
+	while (1) {
+		static struct option long_options[] = {
+			{"help", no_argument, 0, 'h'},
+			{"timeout", required_argument, 0, 't'},
+			{"preferred-lifetime", required_argument, 0, 'p'},
+			{"valid-lifetime", required_argument, 0, 'v'},
+			{"default", no_argument, 0, 'd'},
+			{0, 0, 0, 0}
+		};
+
+		int option_index = 0;
+		c = getopt_long(argc, argv, "ht:p:v:d", long_options,
+				&option_index);
+
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_arg_list_help(config_gateway_option_list, argv[0],
+					    config_gateway_cmd_syntax);
+			ret = ERRORCODE_HELP;
+			goto bail;
+
+		case 'd':
+			defaultRoute = TRUE;
+			break;
+
+		case 't':
+			timeout = strtol(optarg, NULL, 0);
+			break;
+
+		case 'p':
+			preferredLifetime = (uint32_t) strtol(optarg, NULL, 0);
+			break;
+
+		case 'v':
+			validLifetime = (uint32_t) strtol(optarg, NULL, 0);
+			break;
+
+		}
+	}
+
+	if (optind < argc) {
+		if (!prefix) {
+			prefix = argv[optind];
+			optind++;
+		}
+	}
+
+	if (optind < argc) {
+		fprintf(stderr,
+		        "%s: error: Unexpected extra argument: \"%s\"\n",
+		        argv[0], argv[optind]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	if (gInterfaceName[0] == 0) {
+		fprintf(stderr,
+		        "%s: error: No WPAN interface set (use the `cd` command, or the `-I` argument for `wpanctl`).\n",
+		        argv[0]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
+
+	if (!connection) {
+		dbus_error_free(&error);
+		dbus_error_init(&error);
+		connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+	}
+
+	require_string(connection != NULL, bail, error.message);
+
+	{
+		DBusMessageIter iter;
+		DBusMessageIter list_iter;
+		char path[DBUS_MAXIMUM_NAME_LENGTH+1];
+		char interface_dbus_name[DBUS_MAXIMUM_NAME_LENGTH+1];
+		ret = lookup_dbus_name_from_interface(interface_dbus_name, gInterfaceName);
+		if (ret != 0) {
+			goto bail;
+		}
+		snprintf(path,
+		         sizeof(path),
+		         "%s/%s",
+		         WPANTUND_DBUS_PATH,
+		         gInterfaceName);
+
+		message = dbus_message_new_method_call(
+		    interface_dbus_name,
+		    path,
+		    WPANTUND_DBUS_APIv1_INTERFACE,
+		    WPANTUND_IF_CMD_CONFIG_GATEWAY
+		    );
+
+		dbus_message_append_args(
+		    message,
+		    DBUS_TYPE_BOOLEAN, &defaultRoute,
+		    DBUS_TYPE_INVALID
+		);
+
+		if(prefix) {
+			uint8_t prefix_bytes[16] = {};
+
+			// So the prefix could either be
+			// specified like an IPv6 address, or
+			// specified as a bunch of hex numbers.
+			// We use the presence of a colon (':')
+			// to differentiate.
+			if(strstr(prefix,":")) {
+
+				// Address-style
+				int bits = inet_pton(AF_INET6,prefix,prefix_bytes);
+				if(bits<0) {
+					fprintf(stderr,
+					        "Bad Prefix \"%s\", errno=%d (%s)\n",
+					        prefix,
+					        errno,
+					        strerror(errno));
+					goto bail;
+				} else if(!bits) {
+					fprintf(stderr, "Bad prefix \"%s\"\n", prefix);
+					goto bail;
+				}
+			} else {
+				// DATA-style
+				int length = parse_string_into_data(prefix_bytes,
+				                                    8,
+				                                    prefix);
+				if(length<=0) {
+					fprintf(stderr, "Bad prefix \"%s\"\n", prefix);
+					goto bail;
+				}
+			}
+
+			fprintf(stderr, "Using prefix \"%s\"\n", prefix);
+
+			uint8_t *addr = prefix_bytes;
+			dbus_message_append_args(
+			    message,
+			    DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &addr, 8,
+			    DBUS_TYPE_INVALID
+			);
+		} else {
+			dbus_message_append_args(
+			    message,
+			    DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, "", 0,
+			    DBUS_TYPE_INVALID
+			);
+		}
+
+		dbus_message_append_args(
+		    message,
+		    DBUS_TYPE_UINT32, &preferredLifetime,
+		    DBUS_TYPE_UINT32, &validLifetime,
+		    DBUS_TYPE_INVALID
+		);
+
+
+		reply = dbus_connection_send_with_reply_and_block(
+		    connection,
+		    message,
+		    timeout,
+		    &error
+		    );
+
+		if (!reply) {
+			fprintf(stderr, "%s: error: %s\n", argv[0], error.message);
+			ret = ERRORCODE_TIMEOUT;
+			goto bail;
+		}
+
+		dbus_message_get_args(reply, &error,
+		                      DBUS_TYPE_INT32, &ret,
+		                      DBUS_TYPE_INVALID
+		                      );
+
+		if (!ret) {
+			fprintf(stderr, "Gateway configured.\n");
+		} else {
+			fprintf(stderr, "%s failed with error %d. %s\n", argv[0], ret, wpantund_status_to_cstr(ret));
+			print_error_diagnosis(ret);
+		}
+	}
+
+bail:
+
+	if (connection)
+		dbus_connection_unref(connection);
+
+	if (message)
+		dbus_message_unref(message);
+
+	if (reply)
+		dbus_message_unref(reply);
+
+	dbus_error_free(&error);
+
+	return ret;
+}
diff --git a/src/wpanctl/tool-cmd-config-gateway.h b/src/wpanctl/tool-cmd-config-gateway.h
new file mode 100644
index 0000000..748232a
--- /dev/null
+++ b/src/wpanctl/tool-cmd-config-gateway.h
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef WPANCTL_TOOL_CMD_CONFIG_GATEWAY_H
+#define WPANCTL_TOOL_CMD_CONFIG_GATEWAY_H
+
+#include "wpanctl-utils.h"
+
+int tool_cmd_config_gateway(int argc, char* argv[]);
+
+#endif
diff --git a/src/wpanctl/tool-cmd-form.c b/src/wpanctl/tool-cmd-form.c
new file mode 100644
index 0000000..81df49b
--- /dev/null
+++ b/src/wpanctl/tool-cmd-form.c
@@ -0,0 +1,275 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <getopt.h>
+#include "wpanctl-utils.h"
+#include "tool-cmd-form.h"
+#include "assert-macros.h"
+#include "wpan-dbus-v0.h"
+#include "args.h"
+#include "string-utils.h"
+
+#include <arpa/inet.h>
+#include <errno.h>
+
+const char form_cmd_syntax[] = "[args] [network-name]";
+
+static const arg_list_item_t form_option_list[] = {
+	{'h', "help", NULL, "Print Help"},
+	{'t', "timeout", "ms", "Set timeout period"},
+	{'c', "channel", "channel", "Set the desired channel"},
+	{'T', "type", "node-type", "Join as a specific node type"},
+//	{'u', "ula-prefix", "ULA Prefix", "Specify a specific *LEGACY* ULA prefix"},
+	{'M', "mesh-local-prefix", "Mesh-Local IPv6 Prefix", "Specify a non-default mesh-local IPv6 prefix"},
+	{'L', "legacy-prefix", "Legacy IPv6 Prefix", "Specify a specific *LEGACY* IPv6 prefix"},
+	{0}
+};
+
+int tool_cmd_form(int argc, char* argv[])
+{
+	int ret = 0;
+	int c;
+	int timeout = DEFAULT_TIMEOUT_IN_SECONDS * 1000;
+	DBusConnection* connection = NULL;
+	DBusMessage *message = NULL;
+	DBusMessage *reply = NULL;
+	DBusError error;
+	const char* network_name = NULL;
+	const char* ula_prefix = NULL;
+	uint16_t node_type = WPAN_IFACE_ROLE_ROUTER; // Default to router for form
+	uint32_t channel_mask = 0;
+
+	dbus_error_init(&error);
+
+	while (1) {
+		static struct option long_options[] = {
+			{"help", no_argument, 0, 'h'},
+			{"timeout", required_argument, 0, 't'},
+			{"channel", required_argument, 0, 'c'},
+			{"ula-prefix", required_argument, 0, 'u'},
+			{"mesh-local-prefix", required_argument, 0, 'M'},
+			{"legacy-prefix", required_argument, 0, 'L'},
+			{"type", required_argument, 0, 'T'},
+			{0, 0, 0, 0}
+		};
+
+		int option_index = 0;
+		c = getopt_long(argc, argv, "hc:t:T:u:", long_options,
+				&option_index);
+
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_arg_list_help(form_option_list, argv[0],
+					    form_cmd_syntax);
+			ret = ERRORCODE_HELP;
+			goto bail;
+
+		case 't':
+			timeout = strtol(optarg, NULL, 0);
+			break;
+
+		case 'c':
+			channel_mask = (1 << strtol(optarg, NULL, 0));
+			break;
+
+		case 'M':
+			fprintf(stderr,
+					"%s: error: Setting the mesh local address at the command line isn't yet implemented. Set it as a property instead.\n",
+					argv[0]);
+			ret = ERRORCODE_BADARG;
+			goto bail;
+			break;
+
+		case 'L':
+		case 'u':
+			ula_prefix = optarg;
+			break;
+
+		case 'T':
+			node_type = node_type_str2int(optarg);
+			break;
+		}
+	}
+
+	if (optind < argc) {
+		if (!network_name) {
+			network_name = argv[optind];
+			optind++;
+		}
+	}
+
+	if (optind < argc) {
+		fprintf(stderr,
+			"%s: error: Unexpected extra argument: \"%s\"\n",
+			argv[0], argv[optind]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	if (!network_name) {
+		fprintf(stderr, "%s: error: Missing network name.\n", argv[0]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	if (gInterfaceName[0] == 0) {
+		fprintf(stderr,
+		        "%s: error: No WPAN interface set (use the `cd` command, or the `-I` argument for `wpanctl`).\n",
+		        argv[0]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
+
+	if (!connection) {
+		dbus_error_free(&error);
+		dbus_error_init(&error);
+		connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+	}
+
+	require_string(connection != NULL, bail, error.message);
+
+	{
+		DBusMessageIter iter;
+		DBusMessageIter list_iter;
+		char path[DBUS_MAXIMUM_NAME_LENGTH+1];
+		char interface_dbus_name[DBUS_MAXIMUM_NAME_LENGTH+1];
+		ret = lookup_dbus_name_from_interface(interface_dbus_name, gInterfaceName);
+		if (ret != 0) {
+			goto bail;
+		}
+		snprintf(path,
+		         sizeof(path),
+		         "%s/%s",
+		         WPAN_TUNNEL_DBUS_PATH,
+		         gInterfaceName);
+
+		message = dbus_message_new_method_call(
+		    interface_dbus_name,
+		    path,
+		    WPAN_TUNNEL_DBUS_INTERFACE,
+		    WPAN_IFACE_CMD_FORM
+		    );
+
+		dbus_message_append_args(
+		    message,
+		    DBUS_TYPE_STRING, &network_name,
+		    DBUS_TYPE_INT16, &node_type,
+		    DBUS_TYPE_UINT32, &channel_mask,
+		    DBUS_TYPE_INVALID
+		    );
+
+		if(ula_prefix) {
+			uint8_t ula_bytes[16] = {};
+
+			// So the ULA prefix could either be
+			// specified like an IPv6 address, or
+			// specified as a bunch of hex numbers.
+			// We use the presence of a colon (':')
+			// to differentiate.
+			if(strstr(ula_prefix,":")) {
+
+				// Address-style
+				int bits = inet_pton(AF_INET6,ula_prefix,ula_bytes);
+				if(bits<0) {
+					fprintf(stderr,
+					        "Bad ULA \"%s\", errno=%d (%s)\n",
+					        ula_prefix,
+					        errno,
+					        strerror(errno));
+					goto bail;
+				} else if(!bits) {
+					fprintf(stderr, "Bad ULA \"%s\"\n", ula_prefix);
+					goto bail;
+				}
+			} else {
+				// DATA-style
+				int length = parse_string_into_data(ula_bytes,
+				                                    8,
+				                                    ula_prefix);
+				if(length<=0) {
+					fprintf(stderr, "Bad ULA \"%s\"\n", ula_prefix);
+					goto bail;
+				}
+			}
+
+			fprintf(stderr, "Using ULA prefix \"%s\"\n", ula_prefix);
+
+			uint8_t *addr = ula_bytes;
+			dbus_message_append_args(
+			    message,
+			    DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &addr, 8,
+			    DBUS_TYPE_INVALID
+			    );
+		}
+
+		fprintf(stderr,
+		        "Forming WPAN \"%s\" as node type %d\n",
+		        network_name,
+		        node_type);
+
+		reply = dbus_connection_send_with_reply_and_block(
+		    connection,
+		    message,
+		    timeout,
+		    &error
+		    );
+
+		if (!reply) {
+			fprintf(stderr, "%s: error: %s\n", argv[0], error.message);
+			ret = ERRORCODE_TIMEOUT;
+			goto bail;
+		}
+
+		dbus_message_get_args(reply, &error,
+		                      DBUS_TYPE_INT32, &ret,
+		                      DBUS_TYPE_INVALID
+		                      );
+
+		if (!ret) {
+			fprintf(stderr, "Successfully formed!\n");
+		} else {
+			fprintf(stderr, "%s failed with error %d. %s\n", argv[0], ret, wpantund_status_to_cstr(ret));
+			print_error_diagnosis(ret);
+		}
+	}
+
+bail:
+
+	if (connection)
+		dbus_connection_unref(connection);
+
+	if (message)
+		dbus_message_unref(message);
+
+	if (reply)
+		dbus_message_unref(reply);
+
+	dbus_error_free(&error);
+
+	return ret;
+}
diff --git a/src/wpanctl/tool-cmd-form.h b/src/wpanctl/tool-cmd-form.h
new file mode 100644
index 0000000..781f77f
--- /dev/null
+++ b/src/wpanctl/tool-cmd-form.h
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef WPANCTL_TOOL_CMD_FORM_H
+#define WPANCTL_TOOL_CMD_FORM_H
+
+#include "wpanctl-utils.h"
+
+int tool_cmd_form(int argc, char* argv[]);
+
+#endif
diff --git a/src/wpanctl/tool-cmd-getprop.c b/src/wpanctl/tool-cmd-getprop.c
new file mode 100644
index 0000000..6f3f41b
--- /dev/null
+++ b/src/wpanctl/tool-cmd-getprop.c
@@ -0,0 +1,238 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <getopt.h>
+#include "wpanctl-utils.h"
+#include "tool-cmd-getprop.h"
+#include "assert-macros.h"
+#include "args.h"
+#include "assert-macros.h"
+#include "wpan-dbus-v1.h"
+
+const char getprop_cmd_syntax[] = "[args] <property-name>";
+
+static const arg_list_item_t getprop_option_list[] = {
+	{'h', "help", NULL, "Print Help"},
+	{'t', "timeout", "ms", "Set timeout period"},
+	{'a', "all", NULL, "Print all supported properties"},
+	{'v', "value-only", NULL, "Print only the value of the property"},
+	{0}
+};
+
+int tool_cmd_getprop(int argc, char *argv[])
+{
+	int ret = 0;
+	int c;
+	int timeout = 10 * 1000;
+	DBusConnection *connection = NULL;
+	DBusMessage *message = NULL;
+	DBusMessage *reply = NULL;
+	DBusError error;
+	const char *property_name = NULL;
+	bool get_all = false;
+	bool value_only = false;
+
+	dbus_error_init(&error);
+
+	while (1) {
+		static struct option long_options[] = {
+			{"help", no_argument, 0, 'h'},
+			{"timeout", required_argument, 0, 't'},
+			{"all", no_argument, 0, 'a'},
+			{"value-only", no_argument, 0, 'v'},
+			{0, 0, 0, 0}
+		};
+
+		int option_index = 0;
+		c = getopt_long(argc, argv, "ht:av", long_options,
+				&option_index);
+
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_arg_list_help(getprop_option_list, argv[0],
+					    getprop_cmd_syntax);
+			ret = ERRORCODE_HELP;
+			goto bail;
+
+		case 't':
+			timeout = strtol(optarg, NULL, 0);
+			break;
+
+		case 'a':
+			get_all = true;
+			break;
+
+		case 'v':
+			value_only = true;
+			break;
+		}
+	}
+
+	if (optind < argc) {
+		property_name = argv[optind];
+	}
+
+	if (optind == argc) {
+		property_name = "";
+		get_all = true;
+	}
+
+	if ((optind < argc) && get_all) {
+		fprintf(stderr,
+		        "%s: error: Can't specify a specific property and request all properties at the same time.\n",
+		        argv[0]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	if (gInterfaceName[0] == 0) {
+		fprintf(stderr,
+		        "%s: error: No WPAN interface set (use the `cd` command, or the `-I` argument for `wpanctl`).\n",
+		        argv[0]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	if (optind + 1 < argc) {
+		int cmd_and_flags = optind;
+		int j;
+		for (j = optind; j < argc; j++) {
+			optind = 0;
+			argv[cmd_and_flags] = argv[j];
+			ret = tool_cmd_getprop(cmd_and_flags + 1, argv);
+		}
+		goto bail;
+	}
+
+
+	connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
+
+	if (!connection) {
+		dbus_error_free(&error);
+		dbus_error_init(&error);
+		connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+	}
+
+	require_string(connection != NULL, bail, error.message);
+
+	{
+		DBusMessageIter iter;
+		DBusMessageIter list_iter;
+		char path[DBUS_MAXIMUM_NAME_LENGTH+1];
+		char interface_dbus_name[DBUS_MAXIMUM_NAME_LENGTH+1];
+		ret = lookup_dbus_name_from_interface(interface_dbus_name, gInterfaceName);
+		if (ret != 0) {
+			goto bail;
+		}
+		snprintf(path,
+		         sizeof(path),
+		         "%s/%s",
+		         WPANTUND_DBUS_PATH,
+		         gInterfaceName);
+
+		message = dbus_message_new_method_call(
+		    interface_dbus_name,
+		    path,
+		    WPANTUND_DBUS_APIv1_INTERFACE,
+		    WPANTUND_IF_CMD_PROP_GET
+		    );
+
+		dbus_message_append_args(
+		    message,
+		    DBUS_TYPE_STRING, &property_name,
+		    DBUS_TYPE_INVALID
+		    );
+
+		reply = dbus_connection_send_with_reply_and_block(
+		    connection,
+		    message,
+		    timeout,
+		    &error
+		    );
+
+		if (!reply) {
+			fprintf(stderr, "%s: error: %s\n", argv[0], error.message);
+			ret = ERRORCODE_TIMEOUT;
+			goto bail;
+		}
+
+		dbus_message_iter_init(reply, &iter);
+
+		// Get return code
+		dbus_message_iter_get_basic(&iter, &ret);
+
+		if (ret) {
+			const char* error_cstr = NULL;
+
+			// Try to see if there is an error explanation we can extract
+			dbus_message_iter_next(&iter);
+			if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) {
+				dbus_message_iter_get_basic(&iter, &error_cstr);
+			}
+
+			if(!error_cstr || error_cstr[0] == 0) {
+				error_cstr = (ret<0)?strerror(-ret):"Get failed";
+			}
+
+			fprintf(stderr, "%s: %s (%d)\n", property_name, error_cstr, ret);
+			goto bail;
+		}
+
+		// Move to the property
+		dbus_message_iter_next(&iter);
+
+		if (get_all) {
+			dbus_message_iter_recurse(&iter, &list_iter);
+
+			for (;
+			     dbus_message_iter_get_arg_type(&list_iter) == DBUS_TYPE_STRING;
+			     dbus_message_iter_next(&list_iter)) {
+				char* args[3] = { argv[0] };
+				dbus_message_iter_get_basic(&list_iter, &args[1]);
+				ret = tool_cmd_getprop(2, args);
+			}
+		} else {
+			if(!value_only && property_name[0])
+				fprintf(stdout, "%s = ", property_name);
+			dump_info_from_iter(stdout, &iter, 0, false, false);
+		}
+	}
+
+bail:
+
+	if (connection)
+		dbus_connection_unref(connection);
+
+	if (message)
+		dbus_message_unref(message);
+
+	if (reply)
+		dbus_message_unref(reply);
+
+	dbus_error_free(&error);
+
+	return ret;
+}
diff --git a/src/wpanctl/tool-cmd-getprop.h b/src/wpanctl/tool-cmd-getprop.h
new file mode 100644
index 0000000..bb94223
--- /dev/null
+++ b/src/wpanctl/tool-cmd-getprop.h
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef WPANCTL_TOOL_CMD_GETPROP_H
+#define WPANCTL_TOOL_CMD_GETPROP_H
+
+#include "wpanctl-utils.h"
+
+int tool_cmd_getprop(int argc, char* argv[]);
+
+#endif
diff --git a/src/wpanctl/tool-cmd-host-did-wake.c b/src/wpanctl/tool-cmd-host-did-wake.c
new file mode 100644
index 0000000..27ea87b
--- /dev/null
+++ b/src/wpanctl/tool-cmd-host-did-wake.c
@@ -0,0 +1,166 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <getopt.h>
+#include "wpanctl-utils.h"
+#include "tool-cmd-host-did-wake.h"
+#include "assert-macros.h"
+#include "args.h"
+#include "assert-macros.h"
+#include "wpan-dbus-v1.h"
+
+const char host_did_wake_cmd_syntax[] = "[args]";
+
+static const arg_list_item_t host_did_wake_option_list[] = {
+	{'h', "help", NULL, "Print Help"},
+	{'t', "timeout", "ms", "Set timeout period"},
+	{0}
+};
+
+int tool_cmd_host_did_wake(int argc, char* argv[])
+{
+	int ret = 0;
+	int c;
+	int timeout = 10 * 1000;
+	DBusConnection* connection = NULL;
+	DBusMessage *message = NULL;
+	DBusMessage *reply = NULL;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	while (1) {
+		static struct option long_options[] = {
+			{"help", no_argument, 0, 'h'},
+			{"timeout", required_argument, 0, 't'},
+			{0, 0, 0, 0}
+		};
+
+		int option_index = 0;
+		c = getopt_long(argc, argv, "ht:", long_options, &option_index);
+
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_arg_list_help(host_did_wake_option_list, argv[0],
+					    host_did_wake_cmd_syntax);
+			ret = ERRORCODE_HELP;
+			goto bail;
+
+		case 't':
+			timeout = strtol(optarg, NULL, 0);
+			break;
+		}
+	}
+
+	if (optind < argc) {
+		fprintf(stderr,
+		        "%s: error: Unexpected extra argument: \"%s\"\n",
+			argv[0], argv[optind]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	if (gInterfaceName[0] == 0) {
+		fprintf(stderr,
+		        "%s: error: No WPAN interface set (use the `cd` command, or the `-I` argument for `wpanctl`).\n",
+		        argv[0]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
+
+	if (!connection) {
+		dbus_error_free(&error);
+		dbus_error_init(&error);
+		connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+	}
+
+	require_string(connection != NULL, bail, error.message);
+
+	{
+		DBusMessageIter iter;
+		DBusMessageIter list_iter;
+		char path[DBUS_MAXIMUM_NAME_LENGTH+1];
+		char interface_dbus_name[DBUS_MAXIMUM_NAME_LENGTH+1];
+		ret = lookup_dbus_name_from_interface(interface_dbus_name, gInterfaceName);
+		if (ret != 0) {
+			goto bail;
+		}
+		snprintf(path,
+		         sizeof(path),
+		         "%s/%s",
+		         WPANTUND_DBUS_PATH,
+		         gInterfaceName);
+
+		message = dbus_message_new_method_call(
+		    interface_dbus_name,
+		    path,
+		    WPANTUND_DBUS_APIv1_INTERFACE,
+		    WPANTUND_IF_CMD_HOST_DID_WAKE
+		    );
+
+		reply = dbus_connection_send_with_reply_and_block(
+		    connection,
+		    message,
+		    timeout,
+		    &error
+		    );
+
+		if (!reply) {
+			fprintf(stderr, "%s: error: %s\n", argv[0], error.message);
+			ret = ERRORCODE_TIMEOUT;
+			goto bail;
+		}
+
+		dbus_message_get_args(reply, &error,
+		                      DBUS_TYPE_INT32, &ret,
+		                      DBUS_TYPE_INVALID
+		                      );
+		if (ret == 6)
+			ret = 0;
+
+		if (ret) {
+			fprintf(stderr, "%s failed with error %d. %s\n", argv[0], ret, wpantund_status_to_cstr(ret));
+			print_error_diagnosis(ret);
+		}
+	}
+
+bail:
+
+	if (connection)
+		dbus_connection_unref(connection);
+
+	if (message)
+		dbus_message_unref(message);
+
+	if (reply)
+		dbus_message_unref(reply);
+
+	dbus_error_free(&error);
+
+	return ret;
+}
diff --git a/src/wpanctl/tool-cmd-host-did-wake.h b/src/wpanctl/tool-cmd-host-did-wake.h
new file mode 100644
index 0000000..f11e848
--- /dev/null
+++ b/src/wpanctl/tool-cmd-host-did-wake.h
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef WPANCTL_TOOL_CMD_HOST_DID_WAKE_H
+#define WPANCTL_TOOL_CMD_HOST_DID_WAKE_H
+
+#include "wpanctl-utils.h"
+
+int tool_cmd_host_did_wake(int argc, char* argv[]);
+
+#endif
diff --git a/src/wpanctl/tool-cmd-join.c b/src/wpanctl/tool-cmd-join.c
new file mode 100644
index 0000000..95c9fa1
--- /dev/null
+++ b/src/wpanctl/tool-cmd-join.c
@@ -0,0 +1,267 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <getopt.h>
+#include "wpanctl-utils.h"
+#include "tool-cmd-join.h"
+#include "tool-cmd-scan.h"
+#include "assert-macros.h"
+#include "args.h"
+#include "assert-macros.h"
+#include "wpan-dbus-v0.h"
+
+#include <errno.h>
+
+const char join_cmd_syntax[] = "[args] [network-name]";
+
+static const arg_list_item_t join_option_list[] = {
+	{'h', "help", NULL, "Print Help"},
+	{'t', "timeout", "ms", "Set timeout period"},
+	{'T', "type", "node-type", "Join as a specific node type"},
+	{'p', "panid", "panid", "Specify a specific PAN ID"},
+	{'x', "xpanid", "xpanid", "Specify a specific Extended PAN ID"},
+	{'c', "channel", "channel", "Specify a specific channel"},
+	{0}
+};
+
+int tool_cmd_join(int argc, char* argv[])
+{
+	int ret = 0;
+	int c;
+	int timeout = DEFAULT_TIMEOUT_IN_SECONDS * 1000;
+	DBusConnection* connection = NULL;
+	DBusMessage *message = NULL;
+	DBusMessage *reply = NULL;
+	DBusError error;
+	uint16_t node_type = WPAN_IFACE_ROLE_END_DEVICE;
+	struct wpan_network_info_s target_network = {};
+
+	dbus_error_init(&error);
+
+	while (1) {
+		static struct option long_options[] = {
+			{"help", no_argument, 0, 'h'},
+			{"timeout", required_argument, 0, 't'},
+			{"type", required_argument, 0, 'T'},
+			{"panid", required_argument, 0, 'p'},
+			{"xpanid", required_argument, 0, 'x'},
+			{"channel", required_argument, 0, 'c'},
+			{0, 0, 0, 0}
+		};
+
+		int option_index = 0;
+		c = getopt_long(argc, argv, "hc:t:T:x:p:", long_options,
+				&option_index);
+
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_arg_list_help(join_option_list, argv[0],
+					    join_cmd_syntax);
+			ret = ERRORCODE_HELP;
+			goto bail;
+
+		case 't':
+			timeout = strtol(optarg, NULL, 0);
+			break;
+
+		case 'p':
+			target_network.pan_id = strtol(optarg, NULL, 16);
+			break;
+
+		case 'c':
+			target_network.channel = strtol(optarg, NULL, 0);
+			break;
+
+		case 'x':
+			target_network.xpanid = strtoull(optarg, NULL, 16);
+			break;
+
+		case 'T':
+			node_type = node_type_str2int(optarg);
+			break;
+		}
+	}
+
+	if (optind < argc) {
+		if (!target_network.network_name[0]) {
+			int index;
+			if (1 == sscanf(argv[optind], "%d",
+					&index) && index
+			    && (index <= gScannedNetworkCount)) {
+				target_network = gScannedNetworks[index - 1];
+			} else {
+				strncpy(target_network.network_name,
+					argv[optind], 16);
+				target_network.network_name[16] = 0;
+			}
+			optind++;
+		}
+	}
+
+	if (optind < argc) {
+		if (!target_network.xpanid) {
+			target_network.xpanid = strtoull(argv[optind], NULL, 16);
+			optind++;
+		}
+	}
+
+	if (optind < argc) {
+		fprintf(stderr,
+			"%s: error: Unexpected extra argument: \"%s\"\n",
+			argv[0], argv[optind]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	if (!target_network.network_name[0]) {
+		fprintf(stderr, "%s: error: Missing network name.\n", argv[0]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	if (gInterfaceName[0] == 0) {
+		fprintf(stderr,
+		        "%s: error: No WPAN interface set (use the `cd` command, or the `-I` argument for `wpanctl`).\n",
+		        argv[0]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
+
+	if (!connection) {
+		dbus_error_free(&error);
+		dbus_error_init(&error);
+		connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+	}
+
+	require_string(connection != NULL, bail, error.message);
+
+	{
+		DBusMessageIter iter;
+		DBusMessageIter list_iter;
+		char path[DBUS_MAXIMUM_NAME_LENGTH+1];
+		char interface_dbus_name[DBUS_MAXIMUM_NAME_LENGTH+1];
+		ret = lookup_dbus_name_from_interface(interface_dbus_name, gInterfaceName);
+		if (ret != 0) {
+			goto bail;
+		}
+		snprintf(path,
+		         sizeof(path),
+		         "%s/%s",
+		         WPAN_TUNNEL_DBUS_PATH,
+		         gInterfaceName);
+		char* network_name = target_network.network_name;
+
+		message = dbus_message_new_method_call(
+		    interface_dbus_name,
+		    path,
+		    WPAN_TUNNEL_DBUS_INTERFACE,
+		    WPAN_IFACE_CMD_JOIN
+		    );
+
+		fprintf(stderr,
+		        "Joining \"%s\" %016llX\n",
+		        target_network.network_name,
+		        (unsigned long long)target_network.xpanid);
+
+		dbus_message_append_args(
+		    message,
+		    DBUS_TYPE_STRING, &network_name,
+		    DBUS_TYPE_INVALID
+		    );
+
+		dbus_message_append_args(
+		    message,
+		    DBUS_TYPE_INT16, &node_type,
+		    DBUS_TYPE_INVALID
+		    );
+
+		dbus_message_append_args(
+		    message,
+		    DBUS_TYPE_UINT64, &target_network.xpanid,
+		    DBUS_TYPE_INVALID
+		    );
+
+		dbus_message_append_args(
+		    message,
+		    DBUS_TYPE_UINT16, &target_network.pan_id,
+		    DBUS_TYPE_INVALID
+		    );
+
+		dbus_message_append_args(
+		    message,
+		    DBUS_TYPE_BYTE, &target_network.channel,
+		    DBUS_TYPE_INVALID
+		    );
+
+		reply = dbus_connection_send_with_reply_and_block(
+		    connection,
+		    message,
+		    timeout,
+		    &error
+		    );
+
+		if (!reply) {
+			fprintf(stderr, "%s: error: %s\n", argv[0], error.message);
+			ret = ERRORCODE_TIMEOUT;
+			goto bail;
+		}
+
+		dbus_message_get_args(reply, &error,
+		                      DBUS_TYPE_INT32, &ret,
+		                      DBUS_TYPE_INVALID
+		                      );
+
+		if (!ret) {
+			fprintf(stderr, "Successfully Joined!\n");
+		} else if ((ret == -EINPROGRESS) || (ret == kWPANTUNDStatus_InProgress)) {
+			fprintf(stderr,
+			        "Partial (insecure) join. Credentials needed. Update key to continue.\n");
+			ret = 0;
+		} else {
+			fprintf(stderr, "%s failed with error %d. %s\n", argv[0], ret, wpantund_status_to_cstr(ret));
+
+			print_error_diagnosis(ret);
+		}
+	}
+
+bail:
+
+	if (connection)
+		dbus_connection_unref(connection);
+
+	if (message)
+		dbus_message_unref(message);
+
+	if (reply)
+		dbus_message_unref(reply);
+
+	dbus_error_free(&error);
+
+	return ret;
+}
diff --git a/src/wpanctl/tool-cmd-join.h b/src/wpanctl/tool-cmd-join.h
new file mode 100644
index 0000000..4cbff53
--- /dev/null
+++ b/src/wpanctl/tool-cmd-join.h
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef WPANCTL_TOOL_CMD_JOIN_H
+#define WPANCTL_TOOL_CMD_JOIN_H
+
+#include "wpanctl-utils.h"
+
+int tool_cmd_join(int argc, char* argv[]);
+
+#endif
diff --git a/src/wpanctl/tool-cmd-leave.c b/src/wpanctl/tool-cmd-leave.c
new file mode 100644
index 0000000..fa1be89
--- /dev/null
+++ b/src/wpanctl/tool-cmd-leave.c
@@ -0,0 +1,165 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <getopt.h>
+#include "wpanctl-utils.h"
+#include "tool-cmd-leave.h"
+#include "assert-macros.h"
+#include "wpan-dbus-v1.h"
+#include "args.h"
+
+const char leave_cmd_syntax[] = "[args]";
+
+static const arg_list_item_t leave_option_list[] = {
+	{'h', "help", NULL, "Print Help"},
+	{'t', "timeout", "ms", "Set timeout period"},
+	{0}
+};
+
+int tool_cmd_leave(int argc, char* argv[])
+{
+	int ret = 0;
+	int c;
+	int timeout = 10 * 1000;
+	DBusConnection* connection = NULL;
+	DBusMessage *message = NULL;
+	DBusMessage *reply = NULL;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	while (1) {
+		static struct option long_options[] = {
+			{"help", no_argument, 0, 'h'},
+			{"timeout", required_argument, 0, 't'},
+			{0, 0, 0, 0}
+		};
+
+		int option_index = 0;
+		c = getopt_long(argc, argv, "ht:", long_options, &option_index);
+
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_arg_list_help(leave_option_list, argv[0],
+					    leave_cmd_syntax);
+			ret = ERRORCODE_HELP;
+			goto bail;
+
+		case 't':
+			timeout = strtol(optarg, NULL, 0);
+			break;
+		}
+	}
+
+	if (optind < argc) {
+		fprintf(stderr,
+		        "%s: error: Unexpected extra argument: \"%s\"\n",
+			argv[0], argv[optind]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	if (gInterfaceName[0] == 0) {
+		fprintf(stderr,
+		        "%s: error: No WPAN interface set (use the `cd` command, or the `-I` argument for `wpanctl`).\n",
+		        argv[0]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
+
+	if (!connection) {
+		dbus_error_free(&error);
+		dbus_error_init(&error);
+		connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+	}
+
+	require_string(connection != NULL, bail, error.message);
+
+	{
+		DBusMessageIter iter;
+		DBusMessageIter list_iter;
+		char path[DBUS_MAXIMUM_NAME_LENGTH+1];
+		char interface_dbus_name[DBUS_MAXIMUM_NAME_LENGTH+1];
+		ret = lookup_dbus_name_from_interface(interface_dbus_name, gInterfaceName);
+		if (ret != 0) {
+			goto bail;
+		}
+		snprintf(path,
+		         sizeof(path),
+		         "%s/%s",
+		         WPANTUND_DBUS_PATH,
+		         gInterfaceName);
+
+		message = dbus_message_new_method_call(
+		    interface_dbus_name,
+		    path,
+		    WPANTUND_DBUS_APIv1_INTERFACE,
+		    WPANTUND_IF_CMD_LEAVE
+		    );
+
+		fprintf(stderr, "Leaving current WPAN. . .\n");
+
+		reply = dbus_connection_send_with_reply_and_block(
+		    connection,
+		    message,
+		    timeout,
+		    &error
+		    );
+
+		if (!reply) {
+			fprintf(stderr, "%s: error: %s\n", argv[0], error.message);
+			ret = ERRORCODE_TIMEOUT;
+			goto bail;
+		}
+
+		dbus_message_get_args(reply, &error,
+		                      DBUS_TYPE_INT32, &ret,
+		                      DBUS_TYPE_INVALID
+		                      );
+
+		if (ret) {
+			fprintf(stderr, "%s failed with error %d. %s\n", argv[0], ret, wpantund_status_to_cstr(ret));
+			print_error_diagnosis(ret);
+		}
+	}
+
+bail:
+
+	if (connection)
+		dbus_connection_unref(connection);
+
+	if (message)
+		dbus_message_unref(message);
+
+	if (reply)
+		dbus_message_unref(reply);
+
+	dbus_error_free(&error);
+
+	return ret;
+}
diff --git a/src/wpanctl/tool-cmd-leave.h b/src/wpanctl/tool-cmd-leave.h
new file mode 100644
index 0000000..d113169
--- /dev/null
+++ b/src/wpanctl/tool-cmd-leave.h
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef WPANCTL_TOOL_CMD_LEAVE_H
+#define WPANCTL_TOOL_CMD_LEAVE_H
+
+#include "wpanctl-utils.h"
+
+int tool_cmd_leave(int argc, char* argv[]);
+
+#endif
diff --git a/src/wpanctl/tool-cmd-list.c b/src/wpanctl/tool-cmd-list.c
new file mode 100644
index 0000000..7f6a0e0
--- /dev/null
+++ b/src/wpanctl/tool-cmd-list.c
@@ -0,0 +1,164 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <getopt.h>
+#include "wpanctl-utils.h"
+#include "tool-cmd-list.h"
+#include "assert-macros.h"
+#include "wpan-dbus-v0.h"
+#include "args.h"
+
+const char list_cmd_syntax[] = "[args] <duration>]";
+
+static const arg_list_item_t list_option_list[] = {
+	{'h', "help", NULL, "Print Help"},
+	{'t', "timeout", "ms", "Set timeout period"},
+	{0}
+};
+
+int tool_cmd_list(int argc, char* argv[])
+{
+	int ret = 0;
+	int c;
+	int timeout = DEFAULT_TIMEOUT_IN_SECONDS * 1000;
+	DBusConnection* connection = NULL;
+	DBusMessage *message = NULL;
+	DBusMessage *reply = NULL;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	while (1) {
+		static struct option long_options[] = {
+			{"help", no_argument, 0, 'h'},
+			{"timeout", required_argument, 0, 't'},
+			{0, 0, 0, 0}
+		};
+
+		int option_index = 0;
+		c = getopt_long(argc, argv, "ht:", long_options, &option_index);
+
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_arg_list_help(list_option_list, argv[0],
+					    list_cmd_syntax);
+			ret = ERRORCODE_HELP;
+			goto bail;
+
+		case 't':
+			timeout = strtol(optarg, NULL, 0);
+			break;
+		}
+	}
+
+	if (optind < argc) {
+		fprintf(stderr,
+		        "%s: error: Unexpected extra argument: \"%s\"\n",
+			argv[0], argv[optind]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
+
+	if (!connection) {
+		dbus_error_free(&error);
+		dbus_error_init(&error);
+		connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+	}
+
+	require_string(connection != NULL, bail, error.message);
+
+	{
+		DBusMessageIter iter;
+		DBusMessageIter list_iter;
+
+		message = dbus_message_new_method_call(
+		    WPAN_TUNNEL_DBUS_NAME,
+		    WPAN_TUNNEL_DBUS_PATH,
+		    WPAN_TUNNEL_DBUS_INTERFACE,
+		    WPAN_TUNNEL_CMD_GET_INTERFACES
+		    );
+
+		reply = dbus_connection_send_with_reply_and_block(
+		    connection,
+		    message,
+		    timeout,
+		    &error
+		    );
+
+		if (!reply) {
+			fprintf(stderr, "%s: error: %s\n", argv[0], error.message);
+			ret = ERRORCODE_TIMEOUT;
+			goto bail;
+		}
+
+		dbus_message_iter_init(reply, &iter);
+
+		if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+			fprintf(stderr,
+			        "%s: error: Bad type for interface list (%c)\n",
+			        argv[0],
+			        dbus_message_iter_get_element_type(&iter));
+			ret = ERRORCODE_UNKNOWN;
+			goto bail;
+		}
+
+		dbus_message_iter_recurse(&iter, &list_iter);
+
+		for (;
+		     dbus_message_iter_get_arg_type(&list_iter) == DBUS_TYPE_ARRAY;
+		     dbus_message_iter_next(&list_iter)
+		) {
+			DBusMessageIter item_iter;
+			char *interface_name;
+			char *dbus_name;
+
+			dbus_message_iter_recurse(&list_iter, &item_iter);
+
+			dbus_message_iter_get_basic(&item_iter, &interface_name);
+			dbus_message_iter_next(&item_iter);
+			dbus_message_iter_get_basic(&item_iter, &dbus_name);
+
+			printf("%s (%s)\n", interface_name, dbus_name);
+		}
+	}
+
+bail:
+
+	if (connection)
+		dbus_connection_unref(connection);
+
+	if (message)
+		dbus_message_unref(message);
+
+	if (reply)
+		dbus_message_unref(reply);
+
+	dbus_error_free(&error);
+
+	return ret;
+}
diff --git a/src/wpanctl/tool-cmd-list.h b/src/wpanctl/tool-cmd-list.h
new file mode 100644
index 0000000..9e414c5
--- /dev/null
+++ b/src/wpanctl/tool-cmd-list.h
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef WPANCTL_TOOL_CMD_LIST_H
+#define WPANCTL_TOOL_CMD_LIST_H
+
+#include "wpanctl-utils.h"
+
+int tool_cmd_list(int argc, char* argv[]);
+
+#endif
diff --git a/src/wpanctl/tool-cmd-permit-join.c b/src/wpanctl/tool-cmd-permit-join.c
new file mode 100644
index 0000000..d7dcbb3
--- /dev/null
+++ b/src/wpanctl/tool-cmd-permit-join.c
@@ -0,0 +1,225 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <getopt.h>
+#include "wpanctl-utils.h"
+#include "tool-cmd-permit-join.h"
+#include "assert-macros.h"
+#include "wpan-dbus-v0.h"
+#include "args.h"
+
+const char permit_join_cmd_syntax[] = "[args] <duration> [commissioning-port]";
+
+static const arg_list_item_t permit_join_option_list[] = {
+	{'h', "help", NULL, "Print Help"},
+	{'t', "timeout", "ms", "Set timeout period"},
+	{'n', "network-wide", NULL, "Permit joining network-wide"},
+	{'c', "tcp", NULL, "Permit only TCP for commissioning traffic"},
+	{'d', "udp", NULL, "Permit only UDP for commissioning traffic"},
+	{0}
+};
+
+int tool_cmd_permit_join(int argc, char* argv[])
+{
+	int ret = 0;
+	int c;
+	int timeout = DEFAULT_TIMEOUT_IN_SECONDS * 1000;
+	DBusConnection* connection = NULL;
+	DBusMessage *message = NULL;
+	DBusMessage *reply = NULL;
+	DBusError error;
+	int32_t period = -1;
+	dbus_bool_t network_wide = FALSE;
+	uint16_t commissioning_traffic_port = 0;
+	uint8_t commissioning_traffic_type = 0xFF;
+
+	dbus_error_init(&error);
+
+	while (1) {
+		static struct option long_options[] = {
+			{"help", no_argument, 0, 'h'},
+			{"timeout", required_argument, 0, 't'},
+			{"network-wide", no_argument, 0, 'n'},
+			{"tcp", no_argument, 0, 'c'},
+			{"udp", no_argument, 0, 'd'},
+			{0, 0, 0, 0}
+		};
+
+		int option_index = 0;
+		c = getopt_long(argc, argv, "ht:ncd", long_options,
+				&option_index);
+
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_arg_list_help(permit_join_option_list,
+					    argv[0], permit_join_cmd_syntax);
+			ret = ERRORCODE_HELP;
+			goto bail;
+
+		case 't':
+			timeout = strtol(optarg, NULL, 0);
+			break;
+
+		case 'n':
+			network_wide = TRUE;
+			break;
+
+		case 'c':
+			commissioning_traffic_type = 6;
+			break;
+
+		case 'd':
+			commissioning_traffic_type = 17;
+			break;
+		}
+	}
+
+	if (optind < argc) {
+		if (period == -1) {
+			period = strtol(argv[optind], NULL, 0);
+			optind++;
+		}
+	}
+
+	if (optind < argc) {
+		if (!commissioning_traffic_port) {
+			commissioning_traffic_port =
+			    strtol(argv[optind], NULL, 0);
+			optind++;
+		}
+	}
+
+	if (optind < argc) {
+		fprintf(stderr,
+			"%s: error: Unexpected extra argument: \"%s\"\n",
+			argv[0], argv[optind]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	if (period == -1)
+		period = 240;
+
+	if (gInterfaceName[0] == 0) {
+		fprintf(stderr,
+			"%s: error: No WPAN interface set (use the `cd` command, or the `-I` argument for `wpanctl`).\n",
+			argv[0]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
+
+	if (!connection) {
+		dbus_error_free(&error);
+		dbus_error_init(&error);
+		connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+	}
+
+	require_string(connection != NULL, bail, error.message);
+
+	{
+		DBusMessageIter iter;
+		DBusMessageIter list_iter;
+		char path[DBUS_MAXIMUM_NAME_LENGTH+1];
+		char interface_dbus_name[DBUS_MAXIMUM_NAME_LENGTH+1];
+		ret = lookup_dbus_name_from_interface(interface_dbus_name, gInterfaceName);
+		if (ret != 0) {
+			goto bail;
+		}
+		snprintf(path,
+		         sizeof(path),
+		         "%s/%s",
+		         WPAN_TUNNEL_DBUS_PATH,
+		         gInterfaceName);
+
+		message = dbus_message_new_method_call(
+		    interface_dbus_name,
+		    path,
+		    WPAN_TUNNEL_DBUS_INTERFACE,
+		    WPAN_IFACE_CMD_PERMIT_JOIN
+		    );
+
+		dbus_message_append_args(
+		    message,
+		    DBUS_TYPE_INT32, &period,
+		    DBUS_TYPE_BOOLEAN, &network_wide,
+		    DBUS_TYPE_UINT16, &commissioning_traffic_port,
+		    DBUS_TYPE_BYTE, &commissioning_traffic_type,
+		    DBUS_TYPE_INVALID
+		    );
+
+		if (commissioning_traffic_port)
+			fprintf(stderr,
+			        "Permitting Joining on the current WPAN for %d seconds, commissioning traffic on %s port %d. . .\n",
+			        period,
+			        (commissioning_traffic_type ==
+			         6) ? "TCP" : (commissioning_traffic_type == 17) ? "UDP" : "TCP/UDP",
+			        commissioning_traffic_port);
+		else
+			fprintf(stderr,
+			        "Permitting Joining on the current WPAN for %d seconds. . .\n",
+			        period);
+
+		reply = dbus_connection_send_with_reply_and_block(
+		    connection,
+		    message,
+		    timeout,
+		    &error
+		    );
+
+		if (!reply) {
+			fprintf(stderr, "%s: error: %s\n", argv[0], error.message);
+			ret = ERRORCODE_TIMEOUT;
+			goto bail;
+		}
+
+		dbus_message_get_args(reply, &error,
+		                      DBUS_TYPE_INT32, &ret,
+		                      DBUS_TYPE_INVALID
+		                      );
+
+		if (ret) {
+			fprintf(stderr, "%s failed with error %d. %s\n", argv[0], ret, wpantund_status_to_cstr(ret));
+			print_error_diagnosis(ret);
+		}
+	}
+
+bail:
+
+	if (connection)
+		dbus_connection_unref(connection);
+
+	if (message)
+		dbus_message_unref(message);
+
+	if (reply)
+		dbus_message_unref(reply);
+
+	dbus_error_free(&error);
+
+	return ret;
+}
diff --git a/src/wpanctl/tool-cmd-permit-join.h b/src/wpanctl/tool-cmd-permit-join.h
new file mode 100644
index 0000000..a95f7b2
--- /dev/null
+++ b/src/wpanctl/tool-cmd-permit-join.h
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef WPANCTL_TOOL_CMD_PERMIT_JOIN_H
+#define WPANCTL_TOOL_CMD_PERMIT_JOIN_H
+
+#include "wpanctl-utils.h"
+
+int tool_cmd_permit_join(int argc, char* argv[]);
+
+#endif
diff --git a/src/wpanctl/tool-cmd-poll.c b/src/wpanctl/tool-cmd-poll.c
new file mode 100644
index 0000000..389ff04
--- /dev/null
+++ b/src/wpanctl/tool-cmd-poll.c
@@ -0,0 +1,168 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <getopt.h>
+#include "wpanctl-utils.h"
+#include "tool-cmd-poll.h"
+#include "assert-macros.h"
+#include "args.h"
+#include "assert-macros.h"
+#include "wpan-dbus-v1.h"
+
+const char poll_cmd_syntax[] = "[args]";
+
+static const arg_list_item_t poll_option_list[] = {
+	{'h', "help", NULL, "Print Help"},
+	{'t', "timeout", "ms", "Set timeout period"},
+	{0}
+};
+
+int tool_cmd_poll(int argc, char *argv[])
+{
+	int ret = 0;
+	int c;
+	int timeout = 10 * 1000;
+	DBusConnection *connection = NULL;
+	DBusMessage *message = NULL;
+	DBusMessage *reply = NULL;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	while (1) {
+		static struct option long_options[] = {
+			{"help", no_argument, 0, 'h'},
+			{"timeout", required_argument, 0, 't'},
+			{0, 0, 0, 0}
+		};
+
+		int option_index = 0;
+		c = getopt_long(argc, argv, "ht:", long_options, &option_index);
+
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_arg_list_help(poll_option_list, argv[0],
+					    poll_cmd_syntax);
+			ret = ERRORCODE_HELP;
+			goto bail;
+
+		case 't':
+			timeout = strtol(optarg, NULL, 0);
+			break;
+		}
+	}
+
+	if (optind < argc) {
+		fprintf(stderr,
+		        "%s: error: Unexpected extra argument: \"%s\"\n",
+			argv[0], argv[optind]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	if (gInterfaceName[0] == 0) {
+		fprintf(stderr,
+		        "%s: error: No WPAN interface set (use the `cd` command, or the `-I` argument for `wpanctl`).\n",
+		        argv[0]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
+
+	if (!connection) {
+		dbus_error_free(&error);
+		dbus_error_init(&error);
+		connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+	}
+
+	require_string(connection != NULL, bail, error.message);
+
+	{
+		DBusMessageIter iter;
+		DBusMessageIter list_iter;
+		char path[DBUS_MAXIMUM_NAME_LENGTH+1];
+		char interface_dbus_name[DBUS_MAXIMUM_NAME_LENGTH+1];
+		ret = lookup_dbus_name_from_interface(interface_dbus_name, gInterfaceName);
+		if (ret != 0) {
+			goto bail;
+		}
+		snprintf(path,
+		         sizeof(path),
+		         "%s/%s",
+		         WPANTUND_DBUS_PATH,
+		         gInterfaceName);
+
+		message = dbus_message_new_method_call(
+		    interface_dbus_name,
+		    path,
+		    WPANTUND_DBUS_APIv1_INTERFACE,
+		    WPANTUND_IF_CMD_DATA_POLL
+		    );
+
+		fprintf(stderr, "Polling parent node for IP traffic. . .\n");
+
+		reply = dbus_connection_send_with_reply_and_block(
+		    connection,
+		    message,
+		    timeout,
+		    &error
+		    );
+
+		if (!reply) {
+			fprintf(stderr, "%s: error: %s\n", argv[0], error.message);
+			ret = ERRORCODE_TIMEOUT;
+			goto bail;
+		}
+
+		dbus_message_get_args(reply, &error,
+		                      DBUS_TYPE_INT32, &ret,
+		                      DBUS_TYPE_INVALID
+		                      );
+		if (ret == 6)
+			ret = 0;
+
+		if (ret) {
+			fprintf(stderr, "%s failed with error %d. %s\n", argv[0], ret, wpantund_status_to_cstr(ret));
+			print_error_diagnosis(ret);
+		}
+	}
+
+bail:
+
+	if (connection)
+		dbus_connection_unref(connection);
+
+	if (message)
+		dbus_message_unref(message);
+
+	if (reply)
+		dbus_message_unref(reply);
+
+	dbus_error_free(&error);
+
+	return ret;
+}
diff --git a/src/wpanctl/tool-cmd-poll.h b/src/wpanctl/tool-cmd-poll.h
new file mode 100644
index 0000000..681f585
--- /dev/null
+++ b/src/wpanctl/tool-cmd-poll.h
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef WPANCTL_TOOL_CMD_POLL_H
+#define WPANCTL_TOOL_CMD_POLL_H
+
+#include "wpanctl-utils.h"
+
+int tool_cmd_poll(int argc, char* argv[]);
+
+#endif
diff --git a/src/wpanctl/tool-cmd-remove-route.c b/src/wpanctl/tool-cmd-remove-route.c
new file mode 100644
index 0000000..aac30a8
--- /dev/null
+++ b/src/wpanctl/tool-cmd-remove-route.c
@@ -0,0 +1,248 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      This file implements the "remove-route" command in wpanctl
+ *
+ */
+
+#include <getopt.h>
+#include "wpanctl-utils.h"
+#include "tool-cmd-remove-route.h"
+#include "assert-macros.h"
+#include "args.h"
+#include "assert-macros.h"
+#include "wpan-dbus-v1.h"
+#include "string-utils.h"
+
+#include <arpa/inet.h>
+#include <errno.h>
+
+const char remove_route_cmd_syntax[] = "[args] <prefix>";
+
+static const arg_list_item_t remove_route_option_list[] = {
+	{'h', "help", NULL, "Print Help"},
+	{'l', "length", "in bytes", "Specifies the route prefix length (default is 8)"},
+	{'d', "domain", NULL, "Domain id for the route (default is zero)"},
+	{0}
+};
+
+int tool_cmd_remove_route(int argc, char* argv[])
+{
+	int ret = 0;
+	int c;
+	int timeout = DEFAULT_TIMEOUT_IN_SECONDS * 1000;
+
+	DBusConnection* connection = NULL;
+	DBusMessage *message = NULL;
+	DBusMessage *reply = NULL;
+	DBusError error;
+
+	const char *route_prefix = NULL;
+	int prefix_len = 8;
+	uint16_t domain_id = 0;
+
+	dbus_error_init(&error);
+
+	while (1) {
+		static struct option long_options[] = {
+			{"help", no_argument, 0, 'h'},
+			{"length", required_argument, 0, 'l'},
+			{"domain", required_argument, 0, 'd'},
+			{0, 0, 0, 0}
+		};
+
+		int option_index = 0;
+		c = getopt_long(argc, argv, "hl:d:", long_options,
+				&option_index);
+
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_arg_list_help(remove_route_option_list, argv[0],
+					    remove_route_cmd_syntax);
+			ret = ERRORCODE_HELP;
+			goto bail;
+
+		case 'l' :
+			prefix_len = (int) strtol(optarg, NULL, 0);
+			break;
+
+		case 'd':
+			domain_id = (uint16_t) strtol(optarg, NULL, 0);
+			break;
+		}
+	}
+
+	if (optind < argc) {
+		route_prefix = argv[optind];
+		optind++;
+	}
+
+	if (optind < argc) {
+		fprintf(stderr,
+		        "%s: error: Unexpected extra argument: \"%s\"\n",
+		        argv[0], argv[optind]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	if (gInterfaceName[0] == 0) {
+		fprintf(stderr,
+		        "%s: error: No WPAN interface set (use the `cd` command, or the `-I` argument for `wpanctl`).\n",
+		        argv[0]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
+
+	if (!connection) {
+		dbus_error_free(&error);
+		dbus_error_init(&error);
+		connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+	}
+
+	require_string(connection != NULL, bail, error.message);
+
+	{
+		DBusMessageIter iter;
+		DBusMessageIter list_iter;
+		char path[DBUS_MAXIMUM_NAME_LENGTH+1];
+		char interface_dbus_name[DBUS_MAXIMUM_NAME_LENGTH+1];
+		ret = lookup_dbus_name_from_interface(interface_dbus_name, gInterfaceName);
+		if (ret != 0) {
+			goto bail;
+		}
+		snprintf(path,
+		         sizeof(path),
+		         "%s/%s",
+		         WPANTUND_DBUS_PATH,
+		         gInterfaceName);
+
+		message = dbus_message_new_method_call(
+		    interface_dbus_name,
+		    path,
+		    WPANTUND_DBUS_APIv1_INTERFACE,
+		    WPANTUND_IF_CMD_ROUTE_REMOVE
+		);
+
+		if ((route_prefix != NULL) && (0 <= prefix_len) && (prefix_len <= 16)) {
+			uint8_t prefix_bytes[16];
+
+			memset(prefix_bytes, 0, sizeof(prefix_bytes));
+
+			// So the prefix could either be
+			// specified like an IPv6 address, or
+			// specified as a bunch of hex numbers.
+			// We use the presence of a colon (':')
+			// to differentiate.
+			if (strstr(route_prefix, ":")) {
+
+				// Address-style
+				int bits = inet_pton(AF_INET6, route_prefix, prefix_bytes);
+				if (bits < 0) {
+					fprintf(stderr,
+					        "Bad prefix \"%s\", errno=%d (%s)\n",
+					        route_prefix,
+					        errno,
+					        strerror(errno));
+					goto bail;
+				} else if (bits == 0) {
+					fprintf(stderr, "Bad prefix \"%s\"\n", route_prefix);
+					goto bail;
+				}
+			} else {
+				// DATA-style
+				int length = parse_string_into_data(prefix_bytes,
+				                                    16,
+				                                    route_prefix);
+				if(length<=0) {
+					fprintf(stderr, "Bad prefix \"%s\"\n", route_prefix);
+					goto bail;
+				}
+			}
+
+			fprintf(stderr, "Removing route prefix \"%s\" with len %d, domain-id %d.\n",
+				route_prefix, prefix_len, domain_id
+			);
+
+			uint8_t *addr = prefix_bytes;
+			uint8_t len = (uint8_t)prefix_len;
+
+			dbus_message_append_args(
+			    message,
+			    DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &addr, len,
+			    DBUS_TYPE_INVALID
+			);
+		} else {
+			dbus_message_append_args(
+			    message,
+			    DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, "", 0,
+			    DBUS_TYPE_INVALID
+			);
+		}
+
+		dbus_message_append_args(
+		    message,
+		    DBUS_TYPE_UINT16, &domain_id,
+		    DBUS_TYPE_INVALID
+		);
+
+		reply = dbus_connection_send_with_reply_and_block(
+		    connection,
+		    message,
+		    timeout,
+		    &error
+		);
+
+		if (!reply) {
+			fprintf(stderr, "%s: error: %s\n", argv[0], error.message);
+			ret = ERRORCODE_TIMEOUT;
+			goto bail;
+		}
+
+		dbus_message_get_args(reply, &error,
+		                      DBUS_TYPE_INT32, &ret,
+		                      DBUS_TYPE_INVALID
+		                      );
+
+		if (!ret) {
+			fprintf(stderr, "Route prefix removed.\n");
+		} else {
+			fprintf(stderr, "%s failed with error %d. %s\n", argv[0], ret, wpantund_status_to_cstr(ret));
+			print_error_diagnosis(ret);
+		}
+	}
+
+bail:
+
+	if (connection)
+		dbus_connection_unref(connection);
+
+	if (message)
+		dbus_message_unref(message);
+
+	if (reply)
+		dbus_message_unref(reply);
+
+	dbus_error_free(&error);
+
+	return ret;
+}
diff --git a/src/wpanctl/tool-cmd-remove-route.h b/src/wpanctl/tool-cmd-remove-route.h
new file mode 100644
index 0000000..ec10115
--- /dev/null
+++ b/src/wpanctl/tool-cmd-remove-route.h
@@ -0,0 +1,31 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      This file declares the methods for "remove-route" command
+ *      in wpanctl.
+ *
+ */
+
+#ifndef WPANCTL_TOOL_CMD_REMOVE_ROUTE_H
+#define WPANCTL_TOOL_CMD_REMOVE_ROUTE_H
+
+#include "wpanctl-utils.h"
+
+int tool_cmd_remove_route(int argc, char* argv[]);
+
+#endif
diff --git a/src/wpanctl/tool-cmd-reset.c b/src/wpanctl/tool-cmd-reset.c
new file mode 100644
index 0000000..9f260e4
--- /dev/null
+++ b/src/wpanctl/tool-cmd-reset.c
@@ -0,0 +1,163 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <getopt.h>
+#include "wpanctl-utils.h"
+#include "tool-cmd-scan.h"
+#include "assert-macros.h"
+#include "wpan-dbus-v1.h"
+#include "args.h"
+
+const char reset_cmd_syntax[] = "[args]";
+
+static const arg_list_item_t reset_option_list[] = {
+	{'h', "help", NULL, "Print Help"},
+	{'t', "timeout", "ms", "Set timeout period"},
+	{0}
+};
+
+int tool_cmd_reset(int argc, char *argv[])
+{
+	int ret = 0;
+	int c;
+	int timeout = 10 * 1000;
+	DBusConnection* connection = NULL;
+	DBusMessage *message = NULL;
+	DBusMessage *reply = NULL;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	while (1) {
+		static struct option long_options[] = {
+			{"help", no_argument, 0, 'h'},
+			{"timeout", required_argument, 0, 't'},
+			{0, 0, 0, 0}
+		};
+
+		int option_index = 0;
+		c = getopt_long(argc, argv, "ht:", long_options, &option_index);
+
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_arg_list_help(reset_option_list, argv[0],
+					    reset_cmd_syntax);
+			ret = ERRORCODE_HELP;
+			goto bail;
+
+		case 't':
+			timeout = strtol(optarg, NULL, 0);
+			break;
+		}
+	}
+
+	if (optind < argc) {
+		fprintf(stderr,
+		        "%s: error: Unexpected extra argument: \"%s\"\n",
+			argv[0], argv[optind]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	if (gInterfaceName[0] == 0) {
+		fprintf(stderr,
+		        "%s: error: No WPAN interface set (use the `cd` command, or the `-I` argument for `wpanctl`).\n",
+		        argv[0]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
+
+	if (!connection) {
+		dbus_error_free(&error);
+		dbus_error_init(&error);
+		connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+	}
+
+	require_string(connection != NULL, bail, error.message);
+
+	{
+		DBusMessageIter iter;
+		DBusMessageIter list_iter;
+		char path[DBUS_MAXIMUM_NAME_LENGTH+1];
+		char interface_dbus_name[DBUS_MAXIMUM_NAME_LENGTH+1];
+		ret = lookup_dbus_name_from_interface(interface_dbus_name, gInterfaceName);
+		if (ret != 0) {
+			goto bail;
+		}
+		snprintf(path,
+		         sizeof(path),
+		         "%s/%s",
+		         WPANTUND_DBUS_PATH,
+		         gInterfaceName);
+
+		message = dbus_message_new_method_call(
+		    interface_dbus_name,
+		    path,
+		    WPANTUND_DBUS_APIv1_INTERFACE,
+		    WPANTUND_IF_CMD_RESET
+		    );
+
+		fprintf(stderr, "Resetting NCP. . .\n");
+
+		reply = dbus_connection_send_with_reply_and_block(
+		    connection,
+		    message,
+		    timeout,
+		    &error
+		    );
+
+		if (!reply) {
+			fprintf(stderr, "%s: error: %s\n", argv[0], error.message);
+			ret = ERRORCODE_TIMEOUT;
+			goto bail;
+		}
+
+		dbus_message_get_args(reply, &error,
+		                      DBUS_TYPE_INT32, &ret,
+		                      DBUS_TYPE_INVALID
+		                      );
+
+		if (ret == 6)
+			ret = 0;
+	}
+
+bail:
+
+	if (connection)
+		dbus_connection_unref(connection);
+
+	if (message)
+		dbus_message_unref(message);
+
+	if (reply)
+		dbus_message_unref(reply);
+
+	dbus_error_free(&error);
+
+	return ret;
+}
diff --git a/src/wpanctl/tool-cmd-reset.h b/src/wpanctl/tool-cmd-reset.h
new file mode 100644
index 0000000..7283b28
--- /dev/null
+++ b/src/wpanctl/tool-cmd-reset.h
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef WPANCTL_TOOL_CMD_RESET_H
+#define WPANCTL_TOOL_CMD_RESET_H
+
+#include "wpanctl-utils.h"
+
+int tool_cmd_reset(int argc, char* argv[]);
+
+#endif
diff --git a/src/wpanctl/tool-cmd-resume.c b/src/wpanctl/tool-cmd-resume.c
new file mode 100644
index 0000000..69c2ba5
--- /dev/null
+++ b/src/wpanctl/tool-cmd-resume.c
@@ -0,0 +1,164 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <getopt.h>
+#include "wpanctl-utils.h"
+#include "tool-cmd-resume.h"
+#include "assert-macros.h"
+#include "wpan-dbus-v1.h"
+#include "args.h"
+
+const char resume_cmd_syntax[] = "[args]";
+
+static const arg_list_item_t resume_option_list[] = {
+	{'h', "help", NULL, "Print Help"},
+	{'t', "timeout", "ms", "Set timeout period"},
+	{0}
+};
+
+int tool_cmd_resume(int argc, char *argv[])
+{
+	int ret = 0;
+	int c;
+	int timeout = 10 * 1000;
+	DBusConnection *connection = NULL;
+	DBusMessage *message = NULL;
+	DBusMessage *reply = NULL;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	while (1) {
+		static struct option long_options[] = {
+			{"help", no_argument, 0, 'h'},
+			{"timeout", required_argument, 0, 't'},
+			{0, 0, 0, 0}
+		};
+
+		int option_index = 0;
+		c = getopt_long(argc, argv, "ht:", long_options, &option_index);
+
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_arg_list_help(resume_option_list, argv[0],
+					    resume_cmd_syntax);
+			ret = ERRORCODE_HELP;
+			goto bail;
+
+		case 't':
+			timeout = strtol(optarg, NULL, 0);
+			break;
+		}
+	}
+
+	if (optind < argc) {
+		fprintf(stderr,
+		        "%s: error: Unexpected extra argument: \"%s\"\n",
+			argv[0], argv[optind]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	if (gInterfaceName[0] == 0) {
+		fprintf(stderr,
+		        "%s: error: No WPAN interface set (use the `cd` command, or the `-I` argument for `wpanctl`).\n",
+		        argv[0]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
+
+	if (!connection) {
+		dbus_error_free(&error);
+		dbus_error_init(&error);
+		connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+	}
+
+	require_string(connection != NULL, bail, error.message);
+
+	{
+		DBusMessageIter iter;
+		DBusMessageIter list_iter;
+		char path[DBUS_MAXIMUM_NAME_LENGTH+1];
+		char interface_dbus_name[DBUS_MAXIMUM_NAME_LENGTH+1];
+		ret = lookup_dbus_name_from_interface(interface_dbus_name, gInterfaceName);
+		if (ret != 0) {
+			goto bail;
+		}
+		snprintf(path,
+		         sizeof(path),
+		         "%s/%s",
+		         WPANTUND_DBUS_PATH,
+		         gInterfaceName);
+
+		message = dbus_message_new_method_call(
+		    interface_dbus_name,
+		    path,
+		    WPANTUND_DBUS_APIv1_INTERFACE,
+		    WPANTUND_IF_CMD_ATTACH
+		    );
+
+		fprintf(stderr, "Resuming saved WPAN. . .\n");
+
+		reply = dbus_connection_send_with_reply_and_block(
+		    connection,
+		    message,
+		    timeout,
+		    &error
+		    );
+
+		if (!reply) {
+			fprintf(stderr, "%s: error: %s\n", argv[0], error.message);
+			ret = ERRORCODE_TIMEOUT;
+			goto bail;
+		}
+
+		dbus_message_get_args(reply, &error,
+		                      DBUS_TYPE_INT32, &ret,
+		                      DBUS_TYPE_INVALID
+		                      );
+		if (ret) {
+			fprintf(stderr, "%s failed with error %d. %s\n", argv[0], ret, wpantund_status_to_cstr(ret));
+			print_error_diagnosis(ret);
+		}
+	}
+
+bail:
+
+	if (connection)
+		dbus_connection_unref(connection);
+
+	if (message)
+		dbus_message_unref(message);
+
+	if (reply)
+		dbus_message_unref(reply);
+
+	dbus_error_free(&error);
+
+	return ret;
+}
diff --git a/src/wpanctl/tool-cmd-resume.h b/src/wpanctl/tool-cmd-resume.h
new file mode 100644
index 0000000..991d277
--- /dev/null
+++ b/src/wpanctl/tool-cmd-resume.h
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef WPANCTL_TOOL_CMD_RESUME_H
+#define WPANCTL_TOOL_CMD_RESUME_H
+
+#include "wpanctl-utils.h"
+
+int tool_cmd_resume(int argc, char* argv[]);
+
+#endif
diff --git a/src/wpanctl/tool-cmd-scan.c b/src/wpanctl/tool-cmd-scan.c
new file mode 100644
index 0000000..3f50ebf
--- /dev/null
+++ b/src/wpanctl/tool-cmd-scan.c
@@ -0,0 +1,306 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <getopt.h>
+#include "wpanctl-utils.h"
+#include "tool-cmd-scan.h"
+#include "assert-macros.h"
+#include "wpan-dbus-v1.h"
+#include "string-utils.h"
+#include "args.h"
+
+const char scan_cmd_syntax[] = "[args] [seconds-to-scan]";
+
+static const arg_list_item_t scan_option_list[] = {
+	{'h', "help", NULL, "Print Help"},
+	{'t', "timeout", "ms", "Set timeout period"},
+	{'c', "channel", "channel", "Set the desired channel"},
+	{0}
+};
+
+int gScannedNetworkCount = 0;
+struct wpan_network_info_s gScannedNetworks[SCANNED_NET_BUFFER_SIZE];
+
+static void
+print_scan_header(void)
+{
+	printf(
+		"   | Joinable | NetworkName        | PAN ID | Ch | XPanID           | HWAddr           | RSSI\n");
+	printf(
+		"---+----------+--------------------+--------+----+------------------+------------------+------\n");
+}
+
+static DBusHandlerResult
+dbus_beacon_handler(
+    DBusConnection *connection,
+    DBusMessage *   message,
+    void *          user_data
+) {
+	DBusMessageIter iter;
+	int ret;
+	struct wpan_network_info_s network_info;
+
+	if (!dbus_message_is_signal(message, WPANTUND_DBUS_APIv1_INTERFACE, WPANTUND_IF_SIGNAL_NET_SCAN_BEACON)) {
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	}
+
+	dbus_message_iter_init(message, &iter);
+
+	ret = parse_network_info_from_iter(&network_info, &iter);
+
+	require_noerr(ret, bail);
+
+	if (network_info.network_name[0]) {
+		if (gScannedNetworkCount < SCANNED_NET_BUFFER_SIZE) {
+			gScannedNetworks[gScannedNetworkCount++] = network_info;
+			printf("%2d", gScannedNetworkCount);
+		} else {
+			printf("--");  //This means that we cannot act on the PAN as we do not save the info
+		}
+	} else {
+		printf("  ");
+	}
+
+	printf(" | %s", network_info.allowing_join ? "     YES" : "      NO");
+
+	if (network_info.network_name[0]) {
+		printf(" | \"%s\"%s",
+			   network_info.network_name,
+			   &"                "[strlen(network_info.network_name)]);
+	} else {
+		printf(" | ------ NONE ------");
+	}
+
+	printf(" | 0x%04X", network_info.pan_id);
+	printf(" | %2d", network_info.channel);
+	printf(" | %016llX", (unsigned long long)network_info.xpanid);
+	printf(" | %02X%02X%02X%02X%02X%02X%02X%02X",
+		   network_info.hwaddr[0],
+		   network_info.hwaddr[1],
+		   network_info.hwaddr[2],
+		   network_info.hwaddr[3],
+		   network_info.hwaddr[4],
+		   network_info.hwaddr[5],
+		   network_info.hwaddr[6],
+		   network_info.hwaddr[7]);
+	printf(" | %4d", network_info.rssi);
+	printf("\n");
+
+bail:
+	return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static const char gDBusObjectManagerMatchString[] =
+	"type='signal'"
+//	",interface='" WPANTUND_DBUS_APIv1_INTERFACE "'"
+	;
+
+
+int tool_cmd_scan(int argc, char *argv[])
+{
+	int ret = 0;
+	int c;
+	int timeout = DEFAULT_TIMEOUT_IN_SECONDS * 1000;
+	DBusConnection* connection = NULL;
+	DBusMessage *message = NULL;
+	DBusMessage *reply = NULL;
+	DBusPendingCall *pending = NULL;
+	DBusError error;
+	int32_t scan_period = 0;
+	uint32_t channel_mask = 0;
+
+	dbus_error_init(&error);
+
+	while (1) {
+		static struct option long_options[] = {
+			{"help", no_argument, 0, 'h'},
+			{"timeout", required_argument, 0, 't'},
+			{"channel", required_argument, 0, 'c'},
+			{0, 0, 0, 0}
+		};
+
+		int option_index = 0;
+		c = getopt_long(argc, argv, "hc:t:", long_options,
+				&option_index);
+
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_arg_list_help(scan_option_list, argv[0],
+					    scan_cmd_syntax);
+			ret = ERRORCODE_HELP;
+			goto bail;
+
+		case 't':
+			timeout = strtol(optarg, NULL, 0);
+			break;
+
+		case 'c':
+			channel_mask = strtomask_uint32(optarg);
+			break;
+		}
+	}
+
+	if (optind < argc) {
+		if (scan_period == 0) {
+			scan_period = strtol(argv[optind], NULL, 0);
+			optind++;
+		}
+	}
+
+	if (optind < argc) {
+			fprintf(stderr,
+			        "%s: error: Unexpected extra argument: \"%s\"\n",
+			argv[0], argv[optind]);
+			ret = ERRORCODE_BADARG;
+			goto bail;
+		}
+
+	if (gInterfaceName[0] == 0) {
+		fprintf(stderr,
+		        "%s: error: No WPAN interface set (use the `cd` command, or the `-I` argument for `wpanctl`).\n",
+		        argv[0]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
+
+	if (!connection) {
+		dbus_error_free(&error);
+		dbus_error_init(&error);
+		connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+	}
+
+	require_string(connection != NULL, bail, error.message);
+
+	dbus_bus_add_match(connection, gDBusObjectManagerMatchString, &error);
+
+	require_string(error.name == NULL, bail, error.message);
+
+	dbus_connection_add_filter(connection, &dbus_beacon_handler, NULL, NULL);
+
+	{
+		char path[DBUS_MAXIMUM_NAME_LENGTH+1];
+		char interface_dbus_name[DBUS_MAXIMUM_NAME_LENGTH+1];
+		DBusMessageIter iter;
+		ret = lookup_dbus_name_from_interface(interface_dbus_name, gInterfaceName);
+
+		if (ret != 0) {
+			goto bail;
+		}
+
+		snprintf(
+			path,
+			sizeof(path),
+			"%s/%s",
+			WPANTUND_DBUS_PATH,
+			gInterfaceName
+		);
+
+		message = dbus_message_new_method_call(
+			interface_dbus_name,
+			path,
+			WPANTUND_DBUS_APIv1_INTERFACE,
+			WPANTUND_IF_CMD_NET_SCAN_START
+		);
+
+		dbus_message_append_args(
+			message,
+			DBUS_TYPE_UINT32, &channel_mask,
+			DBUS_TYPE_INVALID
+		);
+
+		print_scan_header();
+
+		gScannedNetworkCount = 0;
+
+		if(!dbus_connection_send_with_reply(
+		    connection,
+		    message,
+			&pending,
+		    timeout
+	    )) {
+			fprintf(stderr, "%s: error: IPC failure\n", argv[0]);
+			ret = ERRORCODE_UNKNOWN;
+			goto bail;
+		}
+
+		while ((dbus_connection_get_dispatch_status(connection) == DBUS_DISPATCH_DATA_REMAINS)
+			|| dbus_connection_has_messages_to_send(connection)
+			|| !dbus_pending_call_get_completed(pending)
+		) {
+			dbus_connection_read_write_dispatch(connection, 5000 /*ms*/);
+		}
+
+		reply = dbus_pending_call_steal_reply(pending);
+
+		require(reply!=NULL, bail);
+
+
+		dbus_message_iter_init(reply, &iter);
+
+		if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
+			fprintf(stderr, "%s: error: Server returned a bad response ('%c')\n",
+			        argv[0], dbus_message_iter_get_arg_type(&iter));
+			ret = ERRORCODE_UNKNOWN;
+			goto bail;
+		}
+
+		// Get return code
+		dbus_message_iter_get_basic(&iter, &ret);
+
+		if (ret) {
+			fprintf(stderr, "%s failed with error %d. %s\n", argv[0], ret, wpantund_status_to_cstr(ret));
+			print_error_diagnosis(ret);
+			goto bail;
+		}
+	}
+
+
+bail:
+
+	if (reply) {
+		dbus_message_unref(reply);
+	}
+
+	if (pending != NULL) {
+		dbus_pending_call_unref(pending);
+	}
+
+	if (message) {
+		dbus_message_unref(message);
+	}
+
+	if (connection) {
+		dbus_bus_remove_match(connection, gDBusObjectManagerMatchString, NULL);
+		dbus_connection_remove_filter(connection,&dbus_beacon_handler,NULL);
+		dbus_connection_unref(connection);
+	}
+
+	dbus_error_free(&error);
+
+	return ret;
+}
diff --git a/src/wpanctl/tool-cmd-scan.h b/src/wpanctl/tool-cmd-scan.h
new file mode 100644
index 0000000..0029a66
--- /dev/null
+++ b/src/wpanctl/tool-cmd-scan.h
@@ -0,0 +1,32 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef WPANCTL_TOOL_CMD_SCAN_H
+#define WPANCTL_TOOL_CMD_SCAN_H
+
+#define SCANNED_NET_BUFFER_SIZE	250
+
+#include "wpanctl-utils.h"
+
+int tool_cmd_scan(int argc, char* argv[]);
+
+extern int gScannedNetworkCount;
+extern struct wpan_network_info_s gScannedNetworks[];
+
+#endif
diff --git a/src/wpanctl/tool-cmd-setprop.c b/src/wpanctl/tool-cmd-setprop.c
new file mode 100644
index 0000000..20e0633
--- /dev/null
+++ b/src/wpanctl/tool-cmd-setprop.c
@@ -0,0 +1,252 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <getopt.h>
+#include "wpanctl-utils.h"
+#include "tool-cmd-setprop.h"
+#include "assert-macros.h"
+#include "args.h"
+#include "assert-macros.h"
+#include "wpan-dbus-v1.h"
+#include "string-utils.h"
+
+const char setprop_cmd_syntax[] = "[args] <property-name> <property-value>";
+
+static const arg_list_item_t setprop_option_list[] = {
+	{'h', "help", NULL, "Print Help"},
+	{'t', "timeout", "ms", "Set timeout period"},
+	{'d', "data", "hex", "Value is binary data (in hex)"},
+	{'s', "string", "string", "Value is a string"},
+	{'i', "integer", "integer", "Value is an integer"},
+	{'v', "value", "property-value",
+	 "Useful when the value starts with a '-'"},
+	{0}
+};
+
+int tool_cmd_setprop(int argc, char* argv[])
+{
+	int ret = 0;
+	int c;
+	int timeout = 30 * 1000;
+	DBusConnection* connection = NULL;
+	DBusMessage *message = NULL;
+	DBusMessage *reply = NULL;
+	DBusError error;
+	const char* property_name = NULL;
+	char* property_value = NULL;
+
+	enum {
+		kPropertyType_String,
+		kPropertyType_Data,
+		kPropertyType_Integer,
+	} property_type = kPropertyType_String;
+
+	dbus_error_init(&error);
+
+	while (1) {
+		static struct option long_options[] = {
+			{"help", no_argument, 0, 'h'},
+			{"timeout", required_argument, 0, 't'},
+			{"data", required_argument, 0, 'd'},
+			{"string", required_argument, 0, 's'},
+			{"integer", required_argument, 0, 'i'},
+			{"value", required_argument, 0, 'v'},
+			{0, 0, 0, 0}
+		};
+
+		int option_index = 0;
+		c = getopt_long(argc, argv, "ht:dsiv:", long_options,
+				&option_index);
+
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_arg_list_help(setprop_option_list,
+					    argv[0], setprop_cmd_syntax);
+			ret = ERRORCODE_HELP;
+			goto bail;
+
+		case 't':
+			timeout = strtol(optarg, NULL, 0);
+			break;
+
+		case 'd':
+			property_type = kPropertyType_Data;
+			property_value = optarg;
+			break;
+
+		case 's':
+			property_type = kPropertyType_String;
+			property_value = optarg;
+			break;
+
+		case 'v':
+			property_value = optarg;
+			break;
+
+		case 'i':
+			property_type = kPropertyType_Integer;
+			property_value = optarg;
+			break;
+		}
+	}
+
+	if (optind < argc) {
+		if (!property_name) {
+			property_name = argv[optind];
+			optind++;
+		}
+	}
+
+	if (optind < argc) {
+		if (!property_value) {
+			property_value = argv[optind];
+			optind++;
+		}
+	}
+
+	if (optind < argc) {
+		fprintf(stderr,
+			"%s: error: Unexpected extra argument: \"%s\"\n",
+			argv[0], argv[optind]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	if (!property_name) {
+		fprintf(stderr, "%s: error: Missing property name.\n", argv[0]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	if (!property_value) {
+		fprintf(stderr, "%s: error: Missing property value.\n", argv[0]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	if (gInterfaceName[0] == 0) {
+		fprintf(stderr,
+		        "%s: error: No WPAN interface set (use the `cd` command, or the `-I` argument for `wpanctl`).\n",
+		        argv[0]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
+
+	if (!connection) {
+		dbus_error_free(&error);
+		dbus_error_init(&error);
+		connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+	}
+
+	require_string(connection != NULL, bail, error.message);
+
+	{
+		DBusMessageIter iter;
+		DBusMessageIter list_iter;
+		char path[DBUS_MAXIMUM_NAME_LENGTH+1];
+		char interface_dbus_name[DBUS_MAXIMUM_NAME_LENGTH+1];
+		ret = lookup_dbus_name_from_interface(interface_dbus_name, gInterfaceName);
+		if (ret != 0) {
+			goto bail;
+		}
+		snprintf(path,
+		         sizeof(path),
+		         "%s/%s",
+		         WPANTUND_DBUS_PATH,
+		         gInterfaceName);
+
+		message = dbus_message_new_method_call(
+		    interface_dbus_name,
+		    path,
+		    WPANTUND_DBUS_APIv1_INTERFACE,
+		    WPANTUND_IF_CMD_PROP_SET
+		    );
+
+		dbus_message_append_args(
+		    message,
+		    DBUS_TYPE_STRING, &property_name,
+		    DBUS_TYPE_INVALID
+		    );
+
+		if (property_type == kPropertyType_String) {
+			dbus_message_append_args(
+			    message,
+			    DBUS_TYPE_STRING, &property_value,
+			    DBUS_TYPE_INVALID
+			    );
+		} else if (property_type == kPropertyType_Data) {
+			int length = parse_string_into_data((uint8_t*)property_value,
+			                                    strlen(property_value),
+			                                    property_value);
+			dbus_message_append_args(
+			    message,
+			    DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &property_value, length,
+			    DBUS_TYPE_INVALID
+			    );
+		} else {
+			fprintf(stderr, "%s: error: Bad property type\n", argv[0]);
+			ret = ERRORCODE_UNKNOWN;
+			goto bail;
+		}
+
+		reply = dbus_connection_send_with_reply_and_block(
+		    connection,
+		    message,
+		    timeout,
+		    &error
+		    );
+
+		if (!reply) {
+			fprintf(stderr, "%s: error: %s\n", argv[0], error.message);
+			ret = ERRORCODE_TIMEOUT;
+			goto bail;
+		}
+
+		dbus_message_get_args(reply, &error,
+		                      DBUS_TYPE_INT32, &ret,
+		                      DBUS_TYPE_INVALID
+		                      );
+		if (ret)
+			fprintf(stderr, "%s failed with error %d. %s\n", argv[0], ret, wpantund_status_to_cstr(ret));
+	}
+
+bail:
+
+	if (connection)
+		dbus_connection_unref(connection);
+
+	if (message)
+		dbus_message_unref(message);
+
+	if (reply)
+		dbus_message_unref(reply);
+
+	dbus_error_free(&error);
+
+	return ret;
+}
diff --git a/src/wpanctl/tool-cmd-setprop.h b/src/wpanctl/tool-cmd-setprop.h
new file mode 100644
index 0000000..e20322d
--- /dev/null
+++ b/src/wpanctl/tool-cmd-setprop.h
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef WPANCTL_TOOL_CMD_SETPROP_H
+#define WPANCTL_TOOL_CMD_SETPROP_H
+
+#include "wpanctl-utils.h"
+
+int tool_cmd_setprop(int argc, char* argv[]);
+
+#endif
diff --git a/src/wpanctl/tool-cmd-status.c b/src/wpanctl/tool-cmd-status.c
new file mode 100644
index 0000000..b520ffe
--- /dev/null
+++ b/src/wpanctl/tool-cmd-status.c
@@ -0,0 +1,158 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <getopt.h>
+#include "wpanctl-utils.h"
+#include "tool-cmd-status.h"
+#include "assert-macros.h"
+#include "wpan-dbus-v1.h"
+#include "args.h"
+
+const char status_cmd_syntax[] = "[args]";
+
+static const arg_list_item_t status_option_list[] = {
+	{'h', "help", NULL, "Print Help"},
+	{'t', "timeout", "ms", "Set timeout period"},
+	{0}
+};
+
+int tool_cmd_status(int argc, char *argv[])
+{
+	int ret = 0;
+	int c;
+	int timeout = 10 * 1000;
+	DBusConnection *connection = NULL;
+	DBusMessage *message = NULL;
+	DBusMessage *reply = NULL;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	while (1) {
+		static struct option long_options[] = {
+			{"help", no_argument, 0, 'h'},
+			{"timeout", required_argument, 0, 't'},
+			{0, 0, 0, 0}
+		};
+
+		int option_index = 0;
+		c = getopt_long(argc, argv, "ht:", long_options, &option_index);
+
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_arg_list_help(status_option_list, argv[0],
+					    status_cmd_syntax);
+			ret = ERRORCODE_HELP;
+			goto bail;
+
+		case 't':
+			timeout = strtol(optarg, NULL, 0);
+			break;
+		}
+	}
+
+	if (optind < argc) {
+		fprintf(stderr,
+		        "%s: error: Unexpected extra argument: \"%s\"\n",
+			argv[0], argv[optind]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	if (gInterfaceName[0] == 0) {
+		fprintf(stderr,
+		        "%s: error: No WPAN interface set (use the `cd` command, or the `-I` argument for `wpanctl`).\n",
+		        argv[0]);
+		ret = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
+
+	if (!connection) {
+		dbus_error_free(&error);
+		dbus_error_init(&error);
+		connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+	}
+
+	require_string(connection != NULL, bail, error.message);
+
+	{
+		DBusMessageIter iter;
+		DBusMessageIter list_iter;
+		char path[DBUS_MAXIMUM_NAME_LENGTH+1];
+		char interface_dbus_name[DBUS_MAXIMUM_NAME_LENGTH+1];
+		ret = lookup_dbus_name_from_interface(interface_dbus_name, gInterfaceName);
+		if (ret != 0) {
+			goto bail;
+		}
+		snprintf(path,
+		         sizeof(path),
+		         "%s/%s",
+		         WPANTUND_DBUS_PATH,
+		         gInterfaceName);
+
+		message = dbus_message_new_method_call(
+		    interface_dbus_name,
+		    path,
+		    WPANTUND_DBUS_APIv1_INTERFACE,
+		    WPANTUND_IF_CMD_STATUS
+		    );
+
+		reply = dbus_connection_send_with_reply_and_block(
+		    connection,
+		    message,
+		    timeout,
+		    &error
+		    );
+
+		if (!reply) {
+			fprintf(stderr, "%s: error: %s\n", argv[0], error.message);
+			ret = ERRORCODE_TIMEOUT;
+			goto bail;
+		}
+
+		dbus_message_iter_init(reply, &iter);
+
+		fprintf(stdout, "%s => ", gInterfaceName);
+		dump_info_from_iter(stdout, &iter, 0, false, false);
+	}
+
+bail:
+
+	if (connection)
+		dbus_connection_unref(connection);
+
+	if (message)
+		dbus_message_unref(message);
+
+	if (reply)
+		dbus_message_unref(reply);
+
+	dbus_error_free(&error);
+
+	return ret;
+}
diff --git a/src/wpanctl/tool-cmd-status.h b/src/wpanctl/tool-cmd-status.h
new file mode 100644
index 0000000..8b69576
--- /dev/null
+++ b/src/wpanctl/tool-cmd-status.h
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef WPANCTL_TOOL_CMD_STATUS_H
+#define WPANCTL_TOOL_CMD_STATUS_H
+
+#include "wpanctl-utils.h"
+
+int tool_cmd_status(int argc, char* argv[]);
+
+#endif
diff --git a/src/wpanctl/wpanctl-cmds.h b/src/wpanctl/wpanctl-cmds.h
new file mode 100644
index 0000000..cc9eb95
--- /dev/null
+++ b/src/wpanctl/wpanctl-cmds.h
@@ -0,0 +1,149 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef wpantund_wpanctl_cmds_h
+#define wpantund_wpanctl_cmds_h
+
+#include "tool-cmd-scan.h"
+#include "tool-cmd-join.h"
+#include "tool-cmd-form.h"
+#include "tool-cmd-leave.h"
+#include "tool-cmd-permit-join.h"
+#include "tool-cmd-list.h"
+#include "tool-cmd-status.h"
+#include "tool-cmd-resume.h"
+#include "tool-cmd-reset.h"
+#include "tool-cmd-begin-low-power.h"
+#include "tool-cmd-begin-net-wake.h"
+#include "tool-cmd-host-did-wake.h"
+#include "tool-cmd-getprop.h"
+#include "tool-cmd-setprop.h"
+#include "tool-cmd-cd.h"
+#include "tool-cmd-poll.h"
+#include "tool-cmd-config-gateway.h"
+#include "tool-cmd-add-route.h"
+#include "tool-cmd-remove-route.h"
+
+#include "wpanctl-utils.h"
+
+#define WPANCTL_CLI_COMMANDS \
+	{ \
+		"join", \
+		"Join a WPAN.", \
+		&tool_cmd_join \
+	}, \
+	{ "connect", "", &tool_cmd_join, 1 }, \
+	{ \
+		"form", \
+		"Form a new WPAN.", \
+		&tool_cmd_form \
+	}, \
+	{ \
+		"attach", \
+		"Attach/resume a previously commissioned network", \
+		&tool_cmd_resume \
+	}, \
+	{ "resume", "", &tool_cmd_resume, 1 }, \
+	{ \
+		"reset", \
+		"Reset the NCP", \
+		&tool_cmd_reset \
+	}, \
+	{ \
+		"begin-low-power", \
+		"Enter low-power mode", \
+		&tool_cmd_begin_low_power \
+	}, \
+	{ "lurk", "", &tool_cmd_begin_low_power, 1 }, \
+	{ "wake", "", &tool_cmd_status, 1 }, \
+	{ \
+		"leave", \
+		"Abandon the currently connected WPAN.", \
+		&tool_cmd_leave \
+	}, \
+	{ "disconnect", "", &tool_cmd_leave, 1 }, \
+	{ \
+		"poll", \
+		"Poll the parent immediately to see if there is IP traffic", \
+		&tool_cmd_poll \
+	}, \
+	{ \
+		"config-gateway", \
+		"Configure gateway", \
+		&tool_cmd_config_gateway \
+	}, \
+	{ \
+		"add-route", \
+		"Add external route prefix", \
+		&tool_cmd_add_route \
+	}, \
+	{ \
+		"remove-route", \
+		"Remove external route prefix", \
+		&tool_cmd_remove_route \
+	}, \
+	{ \
+		"list", \
+		"List available interfaces.", \
+		&tool_cmd_list \
+	}, \
+	{ "ls", "", &tool_cmd_list, 1 }, \
+	{ \
+		"status", \
+		"Retrieve the status of the interface.", \
+		&tool_cmd_status \
+	}, \
+	{ \
+		"permit-join", \
+		"Permit other devices to join the current network.", \
+		&tool_cmd_permit_join \
+	}, \
+	{ "pj", "", &tool_cmd_permit_join, 1 }, \
+	{ "permit", "", &tool_cmd_permit_join, 1 }, \
+	{ \
+		"scan", \
+		"Scan for nearby networks.", \
+		&tool_cmd_scan \
+	}, \
+	{ \
+		"getprop", \
+		"Get a property.", \
+		&tool_cmd_getprop \
+	}, \
+	{ "get", "", &tool_cmd_getprop, 1 }, \
+	{ \
+		"setprop", \
+		"Set a property.", \
+		&tool_cmd_setprop \
+	}, \
+	{ "set", "", &tool_cmd_setprop, 1 }, \
+	{ \
+		"begin-net-wake", \
+		"Initiate a network wakeup", \
+		&tool_cmd_begin_net_wake \
+	}, \
+	{ \
+		"host-did-wake", \
+		"Perform any host-wakeup related tasks", \
+		&tool_cmd_host_did_wake \
+	}, \
+	{ "cd",   "Change current interface (command mode)", \
+	  &tool_cmd_cd                                            }
+
+#endif
diff --git a/src/wpanctl/wpanctl-utils.c b/src/wpanctl/wpanctl-utils.c
new file mode 100644
index 0000000..21942c1
--- /dev/null
+++ b/src/wpanctl/wpanctl-utils.c
@@ -0,0 +1,516 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "assert-macros.h"
+#include "string-utils.h"
+#include "wpanctl-utils.h"
+#include "wpan-dbus-v0.h"
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+
+#ifdef __APPLE__
+char gInterfaceName[32] = "utun2";
+#else
+char gInterfaceName[32] = "wpan0";
+#endif
+int gRet = 0;
+
+void dump_info_from_iter(FILE* file, DBusMessageIter *iter, int indent, bool bare, bool indentFirstLine)
+{
+	DBusMessageIter sub_iter;
+	int i;
+
+	if (!bare && indentFirstLine) for (i = 0; i < indent; i++) fprintf(file, "\t");
+
+	switch (dbus_message_iter_get_arg_type(iter)) {
+	case DBUS_TYPE_DICT_ENTRY:
+		dbus_message_iter_recurse(iter, &sub_iter);
+		dump_info_from_iter(file, &sub_iter, indent + 1, true, false);
+		fprintf(file, " => ");
+		dbus_message_iter_next(&sub_iter);
+		dump_info_from_iter(file, &sub_iter, indent + 1, bare, false);
+		bare = true;
+		break;
+	case DBUS_TYPE_ARRAY:
+		dbus_message_iter_recurse(iter, &sub_iter);
+		if (dbus_message_iter_get_arg_type(&sub_iter) == DBUS_TYPE_BYTE ||
+		    dbus_message_iter_get_arg_type(&sub_iter) == DBUS_TYPE_INVALID) {
+			fprintf(file, "[");
+			indent = 0;
+		} else {
+			fprintf(file, "[\n");
+		}
+
+		for (;
+		     dbus_message_iter_get_arg_type(&sub_iter) != DBUS_TYPE_INVALID;
+		     dbus_message_iter_next(&sub_iter)
+		) {
+			dump_info_from_iter(file,
+			                    &sub_iter,
+			                    indent + 1,
+			                    dbus_message_iter_get_arg_type(&sub_iter) == DBUS_TYPE_BYTE,
+								true);
+		}
+		for (i = 0; i < indent; i++) fprintf(file, "\t");
+		fprintf(file, "]");
+
+		break;
+	case DBUS_TYPE_VARIANT:
+		dbus_message_iter_recurse(iter, &sub_iter);
+		dump_info_from_iter(file, &sub_iter, indent, bare, false);
+		bare = true;
+		break;
+	case DBUS_TYPE_STRING:
+	{
+		const char* string;
+		dbus_message_iter_get_basic(iter, &string);
+		fprintf(file, "\"%s\"", string);
+	}
+	break;
+
+	case DBUS_TYPE_BYTE:
+	{
+		uint8_t v;
+		dbus_message_iter_get_basic(iter, &v);
+		if (!bare) {
+			fprintf(file, "0x%02X", v);
+		} else {
+			fprintf(file, "%02X", v);
+		}
+	}
+	break;
+	case DBUS_TYPE_UINT16:
+	{
+		uint16_t v;
+		dbus_message_iter_get_basic(iter, &v);
+		fprintf(file, "0x%04X", v);
+	}
+	break;
+	case DBUS_TYPE_INT16:
+	{
+		int16_t v;
+		dbus_message_iter_get_basic(iter, &v);
+		fprintf(file, "%d", v);
+	}
+	break;
+	case DBUS_TYPE_UINT32:
+	{
+		uint32_t v;
+		dbus_message_iter_get_basic(iter, &v);
+		fprintf(file, "%d", v);
+	}
+	break;
+	case DBUS_TYPE_BOOLEAN:
+	{
+		dbus_bool_t v;
+		dbus_message_iter_get_basic(iter, &v);
+		fprintf(file, "%s", v ? "true" : "false");
+	}
+	break;
+	case DBUS_TYPE_INT32:
+	{
+		int32_t v;
+		dbus_message_iter_get_basic(iter, &v);
+		fprintf(file, "%d", v);
+	}
+	break;
+	case DBUS_TYPE_UINT64:
+	{
+		uint64_t v;
+		dbus_message_iter_get_basic(iter, &v);
+		fprintf(file, "0x%016llX", (unsigned long long)v);
+	}
+	break;
+	default:
+		fprintf(file, "<%s>",
+		        dbus_message_type_to_string(dbus_message_iter_get_arg_type(iter)));
+		break;
+	}
+	if (!bare)
+		fprintf(file, "\n");
+}
+
+int parse_network_info_from_iter(struct wpan_network_info_s *network_info, DBusMessageIter *iter)
+{
+	int ret = 0;
+	DBusMessageIter outer_iter;
+	DBusMessageIter dict_iter;
+
+	if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY) {
+		dbus_message_iter_recurse(iter, &outer_iter);
+		iter = &outer_iter;
+	}
+
+	memset(network_info, 0, sizeof(*network_info));
+
+	for (;
+	     dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INVALID;
+	     dbus_message_iter_next(iter)) {
+		DBusMessageIter value_iter;
+		char* key;
+
+		if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) {
+			fprintf(stderr,
+			        "error: Bad type for network (%c)\n",
+			        dbus_message_iter_get_arg_type(iter));
+			ret = ERRORCODE_UNKNOWN;
+			goto bail;
+		}
+
+		dbus_message_iter_recurse(iter, &dict_iter);
+
+		if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) {
+			fprintf(stderr,
+			        "error: Bad type for network list (%c)\n",
+			        dbus_message_iter_get_arg_type(&dict_iter));
+			ret = ERRORCODE_UNKNOWN;
+			goto bail;
+		}
+
+		// Get the key
+		dbus_message_iter_get_basic(&dict_iter, &key);
+		dbus_message_iter_next(&dict_iter);
+
+		if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_VARIANT) {
+			fprintf(stderr,
+			        "error: Bad type for network list (%c)\n",
+			        dbus_message_iter_get_arg_type(&dict_iter));
+			ret = ERRORCODE_UNKNOWN;
+			goto bail;
+		}
+
+		dbus_message_iter_recurse(&dict_iter, &value_iter);
+
+		if (strcmp(key, kWPANTUNDProperty_NetworkName) == 0) {
+			char* network_name = NULL;
+			dbus_message_iter_get_basic(&value_iter, &network_name);
+			snprintf(network_info->network_name,
+			         sizeof(network_info->network_name), "%s", network_name);
+		} else if (strcmp(key, kWPANTUNDProperty_NCPChannel) == 0) {
+			dbus_message_iter_get_basic(&value_iter, &network_info->channel);
+		} else if (strcmp(key, kWPANTUNDProperty_NetworkPANID) == 0) {
+			dbus_message_iter_get_basic(&value_iter, &network_info->pan_id);
+		} else if (strcmp(key, kWPANTUNDProperty_NestLabs_NetworkAllowingJoin) == 0) {
+			dbus_message_iter_get_basic(&value_iter,
+			                            &network_info->allowing_join);
+		} else if (strcmp(key, "RSSI") == 0) {
+			dbus_message_iter_get_basic(&value_iter, &network_info->rssi);
+		} else if (strcmp(key, kWPANTUNDProperty_NetworkXPANID) == 0) {
+			dbus_message_iter_get_basic(&value_iter, &network_info->xpanid);
+		} else if (strcmp(key, kWPANTUNDProperty_NetworkNodeType) == 0) {
+			dbus_message_iter_get_basic(&value_iter, &network_info->type);
+		} else if (strcmp(key, kWPANTUNDProperty_NCPHardwareAddress) == 0) {
+			DBusMessageIter sub_iter;
+			dbus_message_iter_recurse(&value_iter, &sub_iter);
+			if (dbus_message_iter_get_arg_type(&sub_iter) == DBUS_TYPE_BYTE) {
+				const uint8_t* value = NULL;
+				int nelements = 0;
+				dbus_message_iter_get_fixed_array(&sub_iter, &value,
+				                                  &nelements);
+				if (nelements == 8)
+					memcpy(network_info->hwaddr, value, nelements);
+			}
+		} else {
+#if DEBUG
+			fprintf(stderr,
+			        "info: %s -> (%c)\n",
+			        key,
+			        dbus_message_iter_get_arg_type(&value_iter));
+#endif
+		}
+	}
+
+bail:
+	if (ret) fprintf(stderr, "Network parse failed.\n");
+	return ret;
+}
+
+int
+lookup_dbus_name_from_interface(char* dbus_bus_name, const char* interface_name)
+{
+	int ret = ERRORCODE_NOTFOUND;
+	int i;
+	int timeout = DEFAULT_TIMEOUT_IN_SECONDS * 1000;
+	DBusConnection* connection = NULL;
+	DBusMessage *message = NULL;
+	DBusMessage *reply = NULL;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	memset(dbus_bus_name, 0, DBUS_MAXIMUM_NAME_LENGTH+1);
+
+	connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
+
+	if (!connection) {
+		dbus_error_free(&error);
+		dbus_error_init(&error);
+		connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+	}
+
+	require_string(connection != NULL, bail, error.message);
+
+	{
+		DBusMessageIter iter;
+		DBusMessageIter list_iter;
+
+		message = dbus_message_new_method_call(
+		    WPAN_TUNNEL_DBUS_NAME,
+		    WPAN_TUNNEL_DBUS_PATH,
+		    WPAN_TUNNEL_DBUS_INTERFACE,
+		    WPAN_TUNNEL_CMD_GET_INTERFACES
+		    );
+
+		reply = dbus_connection_send_with_reply_and_block(
+		    connection,
+		    message,
+		    timeout,
+		    &error
+		    );
+
+		if (!reply) {
+			fprintf(stderr, "%s: error: %s\n", "lookup_dbus_name_from_interface", error.message);
+			ret = ERRORCODE_TIMEOUT;
+			goto bail;
+		}
+
+		dbus_message_iter_init(reply, &iter);
+
+		if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+			fprintf(stderr,
+			        "%s: error: Bad type for interface list (%c)\n",
+			        "lookup_dbus_name_from_interface",
+			        dbus_message_iter_get_element_type(&iter));
+			ret = ERRORCODE_UNKNOWN;
+			goto bail;
+		}
+
+		dbus_message_iter_recurse(&iter, &list_iter);
+
+		for (;
+		     dbus_message_iter_get_arg_type(&list_iter) == DBUS_TYPE_ARRAY;
+		     dbus_message_iter_next(&list_iter)
+		) {
+			DBusMessageIter item_iter;
+			char *item_interface_name = NULL;
+			char *item_dbus_name = NULL;
+
+			dbus_message_iter_recurse(&list_iter, &item_iter);
+
+			dbus_message_iter_get_basic(&item_iter, &item_interface_name);
+			dbus_message_iter_next(&item_iter);
+			dbus_message_iter_get_basic(&item_iter, &item_dbus_name);
+			if ((NULL != item_interface_name)
+				&& (NULL != item_dbus_name)
+				&& (strcmp(item_interface_name, interface_name) == 0)
+			) {
+				strncpy(dbus_bus_name, item_dbus_name, DBUS_MAXIMUM_NAME_LENGTH);
+				ret = 0;
+				break;
+			}
+		}
+	}
+
+bail:
+
+	if (connection)
+		dbus_connection_unref(connection);
+
+	if (message)
+		dbus_message_unref(message);
+
+	if (reply)
+		dbus_message_unref(reply);
+
+	dbus_error_free(&error);
+
+	return ret;
+}
+
+const char*
+wpantund_status_to_cstr(int status)
+{
+	if (status<0) {
+		return strerror(status);
+	}
+	if (WPANTUND_STATUS_IS_NCPERROR(status)) {
+		return "NCP-Specific Errorcode";
+	}
+	switch(status) {
+	case kWPANTUNDStatus_Ok: return "Ok";
+	case kWPANTUNDStatus_Failure: return "Failure";
+	case kWPANTUNDStatus_Timeout: return "Timeout";
+	case kWPANTUNDStatus_SocketReset: return "SocketReset";
+	case kWPANTUNDStatus_InvalidArgument: return "InvalidArgument";
+	case kWPANTUNDStatus_Busy: return "Busy";
+	case kWPANTUNDStatus_InvalidWhenDisabled: return "InvalidWhenDisabled";
+	case kWPANTUNDStatus_InvalidForCurrentState: return "InvalidForCurrentState";
+	case kWPANTUNDStatus_PropertyEmpty: return "PropertyEmpty";
+	case kWPANTUNDStatus_InvalidType: return "InvalidType";
+	case kWPANTUNDStatus_FeatureNotSupported: return "FeatureNotSupported";
+	case kWPANTUNDStatus_FeatureNotImplemented: return "FeatureNotImplemented";
+	case kWPANTUNDStatus_PropertyNotFound: return "PropertyNotFound";
+	case kWPANTUNDStatus_Canceled: return "Canceled";
+	case kWPANTUNDStatus_InProgress: return "InProgress";
+	case kWPANTUNDStatus_NCP_Crashed: return "NCPCrashed";
+	case kWPANTUNDStatus_JoinFailedAtScan: return "JoinFailedAtScan";
+	case kWPANTUNDStatus_JoinFailedAtAuthenticate: return "JoinFailedAtAuthenticate";
+	case kWPANTUNDStatus_Already: return "Already";
+	case kWPANTUNDStatus_TryAgainLater: return "TryAgainLater";
+	case kWPANTUNDStatus_InvalidRange: return "InvalidRange";
+	case kWPANTUNDStatus_MissingXPANID: return "MissingXPANID";
+	default: break;
+	}
+	return "";
+}
+
+void print_error_diagnosis(int error)
+{
+	switch(error) {
+	case kWPANTUNDStatus_Busy:
+	case -EBUSY:
+		fprintf(stderr, "\nDIAGNOSIS: The requested operation can't be completed because the NCP\n"
+						"is busy doing something else, like scanning or joining. If you are persistently\n"
+						"getting this error, try resetting the NCP via the \"reset\" command. You can\n"
+						"help diagnose why this is occuring using the \"state\" command.\n"
+						"\n"
+		);
+		break;
+
+
+	case kWPANTUNDStatus_Canceled:
+	case -ECONNABORTED:
+		fprintf(stderr, "\nDIAGNOSIS: This action was aborted due to a change in the NCP's state.\n"
+						"This can occur if the interface is disabled while you were trying to join,\n"
+						"or if AutoDeepSleep kicked in for some reason.\n"
+						"\n"
+		);
+		break;
+
+	case kWPANTUNDStatus_NCP_Crashed:
+	case -ECONNRESET:
+		fprintf(stderr, "\nDIAGNOSIS: The NCP has unexpectedly crashed and rebooted. Please see the\n"
+						"wpantund logs for more information and try again.\n"
+						"\n"
+		);
+		break;
+
+	case kWPANTUNDStatus_InvalidArgument:
+	case -EINVAL:
+		fprintf(stderr, "\nDIAGNOSIS: This error indicates that either the device in a state where your\n"
+						"request makes no sense or the parameters of your request were invalid. Check your\n"
+						"arguments and verify that you are allowed to perform the given operation when the\n"
+						"NCP is in its current state.\n"
+						"\n"
+		);
+		break;
+
+	case kWPANTUNDStatus_InvalidWhenDisabled:
+		fprintf(stderr, "\nDIAGNOSIS: This error indicates that this operation is not valid when the interface\n"
+						"is disabled. Enable the interface first and try again. You can enable the interface\n"
+						"with the command `setprop enabled true`.\n"
+						"\n"
+		);
+		break;
+
+	case kWPANTUNDStatus_InvalidForCurrentState:
+	case kWPANTUNDStatus_InProgress:
+	case -EALREADY:
+		fprintf(stderr, "\nDIAGNOSIS: This error indicates that the device is not in a state where\n"
+						"it can complete your request, typically because a request is already in progress or\n"
+						"the NCP is already in the requested state.\n"
+						"If you are getting this error persistently, you should try reseting the network\n"
+						"settings on the NCP (via the \"leave\" command). The \"status\" command can be\n"
+						"helpful to further diagnose the issue.\n"
+						"\n"
+		);
+		break;
+
+	case kWPANTUNDStatus_JoinFailedAtScan:
+		fprintf(stderr, "\nDIAGNOSIS: This error indicates that the NCP could not find a device in\n"
+						"range that would allow it to join the given network. This can occur if\n"
+						"the closest device on the network you are trying to join is out of range,\n"
+						"the devices on the network you are trying to join are running an\n"
+						"incompatible network stack, or if there are no devices on the target\n"
+						"network which are permitting joining.\n"
+						"\n"
+		);
+		break;
+
+	case kWPANTUNDStatus_JoinFailedAtAuthenticate:
+		fprintf(stderr, "\nDIAGNOSIS: Join failed while authenticating. This is typically due to using the wrong\n"
+		                "key or because this NCP's network stack is not compatible with this network.\n"
+						"\n"
+		);
+		break;
+	}
+
+	if (WPANTUND_STATUS_IS_NCPERROR(error)) {
+		fprintf(stderr, "\nDIAGNOSIS: This error is specific to this specific type of NCP. The error\n"
+		                "code is %d (0x%02X). Consult the NCP documentation for an explantion of this\n"
+						"error code.\n"
+						"\n", WPANTUND_STATUS_TO_NCPERROR(error), WPANTUND_STATUS_TO_NCPERROR(error)
+		);
+	}
+}
+
+uint16_t
+node_type_str2int(const char *node_type)
+{
+	uint16_t type;
+
+	if (strcasecmp(node_type, "router") == 0) {
+		return WPAN_IFACE_ROLE_ROUTER;
+	} else if (strcasecmp(node_type, "r") == 0) {
+		return WPAN_IFACE_ROLE_ROUTER;
+
+	} else if (strcasecmp(node_type, "end-device") == 0) {
+		return WPAN_IFACE_ROLE_END_DEVICE;
+	} else if (strcasecmp(node_type, "end") == 0) {
+		return WPAN_IFACE_ROLE_END_DEVICE;
+	} else if (strcasecmp(node_type, "e") == 0) {
+		return WPAN_IFACE_ROLE_END_DEVICE;
+
+	} else if (strcasecmp(node_type, "sleepy-end-device") == 0) {
+		return WPAN_IFACE_ROLE_SLEEPY_END_DEVICE;
+	} else if (strcasecmp(node_type, "sleepy") == 0) {
+		return WPAN_IFACE_ROLE_SLEEPY_END_DEVICE;
+	} else if (strcasecmp(node_type, "s") == 0) {
+		return WPAN_IFACE_ROLE_SLEEPY_END_DEVICE;
+
+	} else if (strcasecmp(node_type, "lurker") == 0) {
+		return WPAN_IFACE_ROLE_LURKER;
+	} else if (strcasecmp(node_type, "nl-lurker") == 0) {
+		return WPAN_IFACE_ROLE_LURKER;
+	} else if (strcasecmp(node_type, "l") == 0) {
+		return WPAN_IFACE_ROLE_LURKER;
+
+	} else {
+		// At this moment it should be a number
+		return strtol(node_type, NULL, 0);
+	}
+}
diff --git a/src/wpanctl/wpanctl-utils.h b/src/wpanctl/wpanctl-utils.h
new file mode 100644
index 0000000..e385b95
--- /dev/null
+++ b/src/wpanctl/wpanctl-utils.h
@@ -0,0 +1,81 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef WPANCTL_UTILS_H
+#define WPANCTL_UTILS_H
+
+#include <dbus/dbus.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "wpan-error.h"
+
+#define ERRORCODE_OK            (0)
+#define ERRORCODE_HELP          (1)
+#define ERRORCODE_BADARG        (2)
+#define ERRORCODE_NOCOMMAND     (3)
+#define ERRORCODE_UNKNOWN       (4)
+#define ERRORCODE_BADCOMMAND    (5)
+#define ERRORCODE_NOREADLINE    (6)
+#define ERRORCODE_QUIT          (7)
+#define ERRORCODE_BADCONFIG     (8)
+#define ERRORCODE_ERRNO         (9)
+#define ERRORCODE_NOT_IMPLEMENTED           (10)
+#define ERRORCODE_TIMEOUT           (11)
+#define ERRORCODE_BADVERSION     (12)
+#define ERRORCODE_ALLOC           (13)
+#define ERRORCODE_NOTFOUND     (14)
+
+#define ERRORCODE_INTERRUPT     (128 + SIGINT)
+#define ERRORCODE_SIGHUP        (128 + SIGHUP)
+
+#define DEFAULT_TIMEOUT_IN_SECONDS      60
+
+struct command_info_s {
+	const char* name;
+	const char* desc;
+	int (*entrypoint)(
+	    int argc, char* argv[]);
+	int isHidden;
+};
+
+struct wpan_network_info_s {
+	char network_name[17];
+	dbus_bool_t allowing_join;
+	uint16_t pan_id;
+	int16_t channel;
+	uint64_t xpanid;
+	int8_t rssi;
+	uint8_t type;
+	uint8_t hwaddr[8];
+};
+
+const char* wpantund_status_to_cstr(int status);
+void print_error_diagnosis(int error);
+int parse_network_info_from_iter(struct wpan_network_info_s *network_info, DBusMessageIter *iter);
+int lookup_dbus_name_from_interface(char* dbus_bus_name, const char* interface_name);
+void dump_info_from_iter(FILE* file, DBusMessageIter *iter, int indent, bool bare, bool indentFirstLine);
+uint16_t node_type_str2int(const char *node_type);
+
+extern char gInterfaceName[32];
+extern int gRet;
+
+#endif
diff --git a/src/wpanctl/wpanctl.c b/src/wpanctl/wpanctl.c
new file mode 100644
index 0000000..531f06f
--- /dev/null
+++ b/src/wpanctl/wpanctl.c
@@ -0,0 +1,681 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *		This file implements the main program entry point for the
+ *		WPAN control utility, `wpanctl`.
+ *
+ */
+
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <getopt.h>
+#include "assert-macros.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <ctype.h>
+#include <libgen.h>
+#include <time.h>
+
+#include <dbus/dbus.h>
+
+#if HAVE_LIBREADLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif // HAVE_LIBREADLINE
+
+#include <poll.h>
+#include "args.h"
+#include "config-file.h"
+#include "wpanctl-cmds.h"
+#include "version.h"
+#include "string-utils.h"
+#include "wpanctl-utils.h"
+
+#include "wpan-dbus-v0.h"
+
+static bool istty = true;
+int gDebugMode = 0;
+
+static arg_list_item_t option_list[] = {
+	{ 'h', "help",          NULL,
+	  "Print Help"                                                                   },
+	{ 'v', "version",       NULL,
+	  "Print Version Information"                                    },
+	{ 'f', NULL,            "filename",
+	  "Read commands from file"                                              },
+	{ 'I', "interface", "iface",
+	  "Set interface to use"                                                 },
+	{ 0, "ignore-mismatch", NULL, "Ignore driver version mismatch" },
+	{ 0 }
+};
+
+void print_commands();
+int exec_command(
+    int argc, char * argv[]);
+
+static int
+tool_cmd_help(
+    int argc, char* argv[]
+    )
+{
+	if ((2 == argc) && (0 == strcmp(argv[1], "--help"))) {
+		printf("Help not yet implemented for this command.\n");
+		return ERRORCODE_HELP;
+	}
+
+	if ((argc == 2) && argv[1][0] != '-') {
+		const char *argv2[2] = {
+			argv[1],
+			"--help"
+		};
+		return exec_command(2, (char**)argv2);
+	} else {
+		print_commands();
+	}
+	return ERRORCODE_HELP;
+}
+
+static int tool_cmd_clear(int argc, char *argv[])
+{
+	if (system("clear") == -1) {
+		printf("\n\n\n\n\n\n\n\n");
+	}
+	return 0;
+}
+
+struct command_info_s commandList[] = {
+	WPANCTL_CLI_COMMANDS,
+	{"quit", "Terminate command line mode.", NULL},
+	{"help", "Display this help.", &tool_cmd_help},
+	{"clear", "Clear shell.", &tool_cmd_clear},
+	{"?", NULL, &tool_cmd_help, 1},
+	{NULL}
+};
+
+void
+print_commands()
+{
+	int i;
+
+	printf("Commands:\n");
+	for (i = 0; commandList[i].name; ++i) {
+		if (commandList[i].isHidden)
+			continue;
+		printf(
+		    "   %s %s%s\n",
+		    commandList[i].name,
+		    &"                          "[strlen(commandList[i].name)],
+		    commandList[i].desc
+		    );
+	}
+}
+
+struct command_info_s * find_cmd(char *cmd_name)
+{
+	int idx;
+	for (idx = 0; commandList[idx].name; idx++) {
+		if (strcmp(cmd_name, commandList[idx].name) == 0) {
+			return commandList + idx;
+		}
+	}
+	return NULL;
+}
+
+int exec_command(int argc, char * argv[])
+{
+	int ret = 0;
+	struct command_info_s *cmd_entry;
+
+	require(argc, bail);
+
+	if ((strcmp(argv[0], "quit") == 0) || (strcmp(argv[0], "exit") == 0) ||
+			(strcmp(argv[0], "q") == 0)) {
+		ret = ERRORCODE_QUIT;
+		goto bail;
+	}
+
+	if ((cmd_entry = find_cmd(argv[0])) == NULL) {
+		fprintf(stderr, "The command \"%s\" is not recognised.\n",
+			argv[0]);
+		ret = ERRORCODE_BADCOMMAND;
+		goto bail;
+	}
+
+	if (cmd_entry->entrypoint == NULL) {
+		fprintf(stderr,
+		        "The command \"%s\" is not yet implemented.\n",
+		        cmd_entry->name);
+		ret = ERRORCODE_NOCOMMAND;
+		goto bail;
+	}
+
+	ret = cmd_entry->entrypoint(argc, argv);
+bail:
+	return ret;
+}
+
+#if HAVE_LIBREADLINE
+static bool history_disabled;
+char* wpanctl_generator(const char* text, int state)
+{
+	static int idx, len;
+	const char *name;
+
+	if (!state) {
+		idx = 0;
+		len = strlen(text);
+	}
+
+	while ((name = commandList[idx].name)) {
+		idx++;
+
+		if (commandList[idx - 1].isHidden)
+			continue;
+
+		if (strncmp(name, text, len) == 0)
+			return strdup(name);
+	}
+
+	return NULL;
+}
+
+static char** wpanctl_completion(const char *text, int start, int end)
+{
+	if (start != 0)
+		return NULL;
+
+	return rl_completion_matches(text, wpanctl_generator);
+}
+#endif // HAVE_LIBREADLINE
+
+void
+process_input_line(char *l)
+{
+	char *inputstring;
+	char *argv2[100];
+	char **ap = argv2;
+	int argc2 = 0;
+
+	if (!l[0]) {
+		l = NULL;
+		goto bail;
+	}
+	l = strdup(l);
+#if HAVE_LIBREADLINE
+	if (!history_disabled) {
+		add_history(l);
+	}
+#endif // HAVE_LIBREADLINE
+
+	inputstring = l;
+
+	while ((*ap = get_next_arg(inputstring, &inputstring))) {
+		if (**ap != '\0') {
+			ap++;
+			argc2++;
+		}
+	}
+	if (argc2 > 0) {
+		gRet = exec_command(argc2, argv2);
+		if (gRet == ERRORCODE_QUIT)
+			goto bail;
+		else if (gRet == ERRORCODE_ERRNO)
+			fprintf(stderr, "errno=%d %s\n", errno, strerror(errno));
+		else if (gRet < 0 && (gRet != ERRORCODE_HELP))
+			fprintf(stderr, "Error %d %s\n", gRet, strerror(-gRet));
+		else if (gRet && (gRet != ERRORCODE_HELP))
+			fprintf(stderr, "Error %d (0x%02X)\n", gRet, gRet);
+
+#if HAVE_LIBREADLINE
+		if (!history_disabled)
+			write_history(getenv("WPANCTL_HISTORY_FILE"));
+#endif // HAVE_LIBREADLINE
+	}
+
+bail:
+	free(l);
+	return;
+}
+
+#if HAVE_LIBREADLINE
+static char*
+get_current_prompt()
+{
+	static char prompt[64] = {};
+
+	if (!gInterfaceName[0]) {
+		snprintf(prompt,
+		         sizeof(prompt),
+		         "wpanctl> "
+		         );
+	} else {
+		snprintf(prompt,
+		         sizeof(prompt),
+		         "wpanctl:%s> ",
+		         gInterfaceName
+		         );
+	}
+	return prompt;
+}
+
+void
+process_input_readline(char *l)
+{
+	process_input_line(l);
+	if (istty) {
+#if HAVE_RL_SET_PROMPT
+		if (gRet == ERRORCODE_QUIT)
+			rl_set_prompt("");
+		else
+#endif
+		rl_callback_handler_install(
+		    get_current_prompt(), &process_input_readline);
+	}
+}
+#endif // HAVE_LIBREADLINE
+
+#pragma mark -
+
+#if HAVE_LIBREADLINE
+
+static int
+initialize_readline()
+{
+	int ret = 0;
+
+	require_action(NULL != readline, bail, ret = ERRORCODE_NOREADLINE);
+	rl_initialize();
+
+	rl_readline_name = "wpanctl";
+	//rl_completer_word_break_characters = " \t\n\"\\'`@$><|&{("; // Removed '=' ';'
+
+	using_history();
+	read_history(getenv("WPANCTL_HISTORY_FILE"));
+	rl_instream = stdin;
+
+	rl_callback_handler_install(get_current_prompt(), &process_input_readline);
+
+	rl_attempted_completion_function = wpanctl_completion;
+
+bail:
+	return ret;
+}
+#endif
+
+static void
+print_version()
+{
+	printf("wpanctl " PACKAGE_VERSION );
+	if ((internal_build_source_version[0] == 0) || strequal(SOURCE_VERSION, internal_build_source_version)) {
+		if (strequal(PACKAGE_VERSION, SOURCE_VERSION)) {
+			printf(" (%s)\n", internal_build_date);
+		} else {
+			printf(" (" SOURCE_VERSION "; %s)\n", internal_build_date);
+		}
+	} else {
+		if (strequal(SOURCE_VERSION, PACKAGE_VERSION) || strequal(PACKAGE_VERSION, internal_build_source_version)) {
+			printf(" (%s; %s)\n", internal_build_source_version, internal_build_date);
+		} else {
+			printf(" (" SOURCE_VERSION "/%s; %s)\n", internal_build_source_version, internal_build_date);
+		}
+	}
+}
+
+static int
+wpan_dbus_version_check(DBusConnection* connection)
+{
+	int timeout = 5 * 1000; // Five second timeout
+	int ret = ERRORCODE_BADVERSION;
+	DBusMessage *message = NULL;
+	DBusMessage *reply = NULL;
+	uint32_t version = 0;
+	DBusError error;
+
+	dbus_error_init(&error);
+
+	message = dbus_message_new_method_call(
+	    getenv("WPANCTL_DBUS_NAME"),
+	    WPAN_TUNNEL_DBUS_PATH,
+	    WPAN_TUNNEL_DBUS_INTERFACE,
+	    WPAN_TUNNEL_CMD_GET_VERSION
+	    );
+
+	if (!message) {
+		fprintf(stderr, "error: Unable to allocate dbus message\n");
+		ret = ERRORCODE_ALLOC;
+		goto bail;
+	}
+
+	reply = dbus_connection_send_with_reply_and_block(
+	    connection,
+	    message,
+	    timeout,
+	    &error
+	    );
+
+	if (!reply) {
+		fprintf(stderr, "error: %s\n", error.message);
+		ret = ERRORCODE_TIMEOUT;
+		goto bail;
+	}
+
+	dbus_message_get_args(
+	    reply, NULL,
+	    DBUS_TYPE_UINT32, &version,
+	    DBUS_TYPE_INVALID
+	);
+
+	if (gDebugMode >= 1) {
+		fprintf(stderr, "DEBUG: Version check, wpanctl=%d, wpantund=%d\n", WPAN_TUNNEL_DBUS_VERSION, version);
+	}
+
+	if(version != WPAN_TUNNEL_DBUS_VERSION) {
+		fprintf(stderr, "error: `wpantund` version (%d) doesn't match `wpanctl` version (%d).\n", version, WPAN_TUNNEL_DBUS_VERSION);
+		goto bail;
+	}
+
+	ret = 0;
+
+bail:
+	if (message)
+		dbus_message_unref(message);
+
+	if (reply)
+		dbus_message_unref(reply);
+
+	dbus_error_free(&error);
+
+	return ret;
+}
+
+#pragma mark -
+
+
+int main(int argc, char * argv[])
+{
+	int c;
+	bool ignore_driver_version_mismatch = false;
+	DBusError error;
+	DBusConnection* connection;
+
+	dbus_error_init(&error);
+
+	srandom(time(NULL));
+
+	while (1) {
+		static struct option long_options[] = {
+			{"help", no_argument, 0, 'h'},
+			{"version", no_argument, 0, 'v'},
+			{"ignore-mismatch", no_argument, 0, 'i'},
+			{"debug", no_argument, 0, 'd'},
+			{"interface", required_argument, 0, 'I'},
+			{"file", required_argument, 0, 'f'},
+			{0, 0, 0, 0}
+		};
+
+		int option_index = 0;
+
+		if ((optind < argc) && (find_cmd(argv[optind]) != NULL)) {
+			// This is where the wpanctl command starts; skip
+			// parsing the flags since they may belong to the command
+			break;
+	}
+
+		c = getopt_long(argc, argv, "hvidI:f:", long_options,
+				&option_index);
+
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+		print_version();
+		print_arg_list_help(option_list,
+		                    argv[0],
+		                    "[options] <sub-command> [args]");
+		print_commands();
+		gRet = ERRORCODE_HELP;
+		goto bail;
+
+		case 'v':
+			print_version();
+			gRet = 0;
+			goto bail;
+
+		case 'd':
+			gDebugMode++;
+			break;
+
+		case 'I':
+			snprintf(gInterfaceName, sizeof(gInterfaceName),
+				 "%s", optarg);
+			break;
+
+		case 'i':
+			ignore_driver_version_mismatch = true;
+			break;
+
+		case 'f':
+#if HAVE_LIBREADLINE
+			stdin = fopen(optarg, "r");
+		if (!stdin) {
+			fprintf(stderr,
+			        "%s: error: Unable to open file \"%s\".\n",
+					argv[0], optarg);
+			return ERRORCODE_BADARG;
+		}
+#else
+			fprintf(stderr,
+				"%s: Cannot read from file \"%s\" : Missing readline library.\n",
+				argv[0], optarg);
+			return ERRORCODE_BADARG;
+#endif
+		default:
+		break;
+	}
+	}
+
+	istty = isatty(fileno(stdin));
+
+	if (gDebugMode >= 1) {
+		fprintf(stderr, "DEBUG: isatty(fileno(stdin)) = %d\n", istty);
+	}
+
+	if (gDebugMode >= 1) {
+		fprintf(stderr, "DEBUG: Will use interface '%s'.\n", gInterfaceName);
+	}
+
+	if (getenv("WPANCTL_DBUS_NAME") && gDebugMode>=1)
+		fprintf(stderr, "DEBUG: Using dbus \"%s\"\n", getenv("WPANCTL_DBUS_NAME"));
+
+	setenv("WPANCTL_DBUS_NAME", WPAN_TUNNEL_DBUS_NAME, 0);
+
+	if (gDebugMode >= 1) {
+		fprintf(stderr, "DEBUG: Getting DBusConnection via dbus_bus_get(DBUS_BUS_STARTER). . .\n");
+	}
+
+	connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
+
+	if (!connection) {
+		if (gDebugMode >= 1) {
+			fprintf(stderr, "DEBUG: dbus_bus_get(DBUS_BUS_STARTER) didn't work, trying dbus_bus_get(DBUS_BUS_SYSTEM). . .\n");
+		}
+		dbus_error_free(&error);
+		dbus_error_init(&error);
+		connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+	}
+
+	require_string(connection != NULL, bail, error.message);
+
+	if (gDebugMode >= 1) {
+		fprintf(stderr, "DEBUG: DBusConnection: %p\n", connection);
+	}
+
+	if (gDebugMode >= 1) {
+		fprintf(stderr, "DEBUG: Registering DBusConnection. . .\n");
+	}
+
+	dbus_bus_register(connection, &error);
+	require_string(error.name == NULL, bail, error.message);
+
+	if (gDebugMode >= 1) {
+		fprintf(stderr, "DEBUG: DBusConnection registered.\n");
+	}
+
+	if (gDebugMode >= 1) {
+		fprintf(stderr, "DEBUG: Requesting DBus name \"%s\". . .\n",WPAN_TUNNEL_DBUS_NAME ".wpanctl");
+	}
+
+	dbus_bus_request_name(connection,
+	                      WPAN_TUNNEL_DBUS_NAME ".wpanctl",
+	                      0,
+	                      &error);
+
+	if (gDebugMode >= 1) {
+		if (error.name != NULL) {
+			fprintf(stderr, "DEBUG: Requesting DBus name \"%s\" failed (no biggie): %s\n",WPAN_TUNNEL_DBUS_NAME ".wpanctl", error.name);
+		} else {
+			fprintf(stderr, "DEBUG: Requesting DBus name \"%s\" succeded.\n",WPAN_TUNNEL_DBUS_NAME ".wpanctl");
+		}
+	}
+
+	// Don't fail if we can't get the name. It isn't a big deal.
+	//require_string(error.name == NULL, bail, error.message);
+
+	if (gDebugMode >= 1) {
+		fprintf(stderr, "DEBUG: Performing wpantund version check. . .\n");
+	}
+
+	// Make sure that we are compatible with the copy of wpantund
+	// that is currently running.
+	gRet = wpan_dbus_version_check(connection);
+
+	if (gRet != 0) {
+		fprintf(stderr,
+		        "%s: error: `wpantund` is either not running, locked up, or incompatible with this version of `wpanctl`.\n",
+		        argv[0]
+		        );
+		if (!ignore_driver_version_mismatch)
+			goto bail;
+	} else {
+		if (gDebugMode >= 1) {
+			fprintf(stderr,
+				"DEBUG: wpantund version check succeded.\n");
+		}
+	}
+
+	if (optind < argc) {
+			if (gDebugMode >= 1) {
+			fprintf(stderr, "DEBUG: Executing command '%s'. . .\n",
+				argv[optind]);
+		}
+
+		argc -= optind;
+		argv += optind;
+
+		optind = 0;
+		gRet = exec_command(argc, argv);
+		goto bail;
+	}
+
+	if (istty) {
+#if !HAVE_LIBREADLINE
+		fprintf(stderr,
+		        "%s: error: Interactive mode disabled: Compiled without libeditline or libreadline support.\n",
+		        argv[0]
+		        );
+		print_arg_list_help(option_list,
+		                    argv[0],
+		                    "[options] <sub-command> [args]");
+		print_commands();
+		gRet = ERRORCODE_NOCOMMAND;
+		goto bail;
+#else   // HAVE_LIBREADLINE
+		setenv("WPANCTL_HISTORY_FILE", tilde_expand("~/.wpanctl_history"), 0);
+
+		gRet = initialize_readline();
+		if(gRet) {
+			fprintf(stderr,
+			        "%s: error: Failed to initialize readline: %d\n",
+			        argv[0], gRet
+			        );
+			goto bail;
+		}
+#endif  // HAVE_LIBREADLINE
+	}
+
+	// Command mode.
+	while ((gRet != ERRORCODE_QUIT) && !feof(stdin)) {
+		optind = 0;
+#if HAVE_LIBREADLINE
+		if (istty) {
+			int dbus_fd = -1;
+
+			dbus_connection_get_unix_fd(connection, &dbus_fd);
+
+			struct pollfd polltable[2] = {
+				{ fileno(stdin), POLLIN | POLLHUP,               0                         },
+				{ dbus_fd,               POLLIN | POLLHUP,               0                         },
+			};
+
+			if (poll(
+			        polltable,
+			        (dbus_fd >= 0) ? 2 : 1,
+			        1000
+			        ) < 0
+			    ) {
+				if (errno == EINTR) {
+					// We just caught a signal.
+					// Do nothing.
+				} else {
+					break;
+				}
+			}
+
+			if (polltable[0].revents)
+				rl_callback_read_char();
+		} else
+#endif  // HAVE_LIBREADLINE
+		{
+			char linebuffer[200];
+			process_input_line(fgets(linebuffer, sizeof(linebuffer), stdin));
+		}
+
+		dbus_connection_read_write_dispatch(connection, 0);
+	}
+	printf("\n");
+
+bail:
+#if HAVE_LIBREADLINE
+	rl_callback_handler_remove();
+#endif  // HAVE_LIBREADLINE
+	if (gRet == ERRORCODE_QUIT)
+		gRet = 0;
+
+	return gRet;
+}
diff --git a/src/wpantund/FirmwareUpgrade.cpp b/src/wpantund/FirmwareUpgrade.cpp
new file mode 100644
index 0000000..89f0c8e
--- /dev/null
+++ b/src/wpantund/FirmwareUpgrade.cpp
@@ -0,0 +1,424 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      Firmware Upgrade Manager
+ *
+ */
+
+/*
+
+This class does what may, at first, appear to be somewhat
+reminiscent of a "Rube Goldberg" contraption: When we
+set the check and upgrade commands, we end up forking and
+spinning off entirely new processes which we communicate
+with via a socket/pipe. Why on earth would I do this?
+
+The answer is security. While wpantund doesn't drop
+privileges yet, it will have that capability one day.
+This setup allows the upgrade and check scripts to run
+in a privileged fashion while the rest of wpantund runs
+with lower privileges. The idea is that wpantund would be
+run initially in a privileged context and then, after
+setting up this object, drop privileges.
+
+There are other places where we will likely want to have
+this sort of behavior (meaning that this doesn't yield any
+immediate increase in security), but this is a good start
+in that direction.
+
+-- RQ 2015-10-23
+
+*/
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "assert-macros.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include "FirmwareUpgrade.h"
+#include "socket-utils.h"
+#include <errno.h>
+#include <syslog.h>
+#include "fgetln.h"
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+using namespace nl::wpantund;
+
+FirmwareUpgrade::FirmwareUpgrade(): mUpgradeStatus(0), mFirmwareCheckFD(-1), mFirmwareUpgradeFD(-1)
+{
+}
+
+FirmwareUpgrade::~FirmwareUpgrade()
+{
+	close_check_fd();
+	close_upgrade_fd();
+}
+
+void
+FirmwareUpgrade::close_check_fd(void)
+{
+	if (mFirmwareCheckFD >= 0) {
+		IGNORE_RETURN_VALUE(write(mFirmwareCheckFD, "X\n", 1));
+		close(mFirmwareCheckFD);
+		mFirmwareCheckFD = -1;
+	}
+}
+
+void
+FirmwareUpgrade::close_upgrade_fd(void)
+{
+	if (mFirmwareUpgradeFD >= 0) {
+		IGNORE_RETURN_VALUE(write(mFirmwareUpgradeFD, "X", 1));
+		close(mFirmwareUpgradeFD);
+		mFirmwareUpgradeFD = -1;
+	}
+}
+
+bool
+FirmwareUpgrade::is_firmware_upgrade_required(const std::string& version)
+{
+	bool ret = false;
+	uint8_t c = 1;
+
+	require(!version.empty(), bail);
+
+	require_quiet(mFirmwareCheckFD >= 0, bail);
+
+	require_string(write(mFirmwareCheckFD, version.c_str(), version.size()) >= 0, bail, strerror(errno));
+
+	require_string(write(mFirmwareCheckFD, "\n", 1) == 1, bail, strerror(errno));
+
+	require_string(read(mFirmwareCheckFD, &c, 1) == 1, bail, strerror(errno));
+
+	ret = (c == 0);
+
+bail:
+
+	// If this check determined that a firmware upgrade was not required,
+	// go ahead and close out our check process so that we don't
+	// waste resources.
+	if (ret == false) {
+		close_check_fd();
+		close_upgrade_fd();
+	}
+
+	return ret;
+}
+
+void
+FirmwareUpgrade::upgrade_firmware(void)
+{
+	require(mUpgradeStatus != EINPROGRESS, bail);
+
+	mUpgradeStatus = EINVAL;
+
+	require(mFirmwareUpgradeFD >= 0, bail);
+
+	require_string(write(mFirmwareUpgradeFD, "1", 1) == 1, bail, strerror(errno));
+
+	mUpgradeStatus = EINPROGRESS;
+
+bail:
+	return;
+}
+
+void
+FirmwareUpgrade::set_firmware_upgrade_command(const std::string& command)
+{
+	int status = -1;
+	pid_t pid = -1;
+
+	if (mFirmwareUpgradeFD >= 0) {
+		close(mFirmwareUpgradeFD);
+		mFirmwareUpgradeFD = -1;
+	}
+
+	pid = fork_unixdomain_socket(&mFirmwareUpgradeFD);
+
+	if (pid < 0) {
+		return;
+	}
+
+	if (pid == 0) {
+		int stdout_copy = dup(STDOUT_FILENO);
+		int stdin_copy = dup(STDIN_FILENO);
+
+		dup2(STDERR_FILENO,STDOUT_FILENO);
+		close(STDIN_FILENO);
+
+		stdin = fdopen(stdin_copy, "r");
+		stdout = fdopen(stdout_copy, "w");
+
+		// Double fork to avoid leaking zombie processes.
+		pid = fork();
+		if (pid < 0) {
+			syslog(LOG_ERR, "Call to fork() failed: %s (%d)", strerror(errno), errno);
+
+			_exit(errno);
+		}
+
+		if (0 == pid)
+		{
+			// Set the shell environment variable if it isn't set already.
+			setenv("SHELL","/bin/sh",0);
+
+			while ((ferror(stdin) == 0) && (feof(stdin) == 0)) {
+				int c;
+				int ret;
+
+				c = fgetc(stdin);
+
+				switch (c) {
+				case '1':
+					// Go ahead and leave the parent's process group
+					setsid();
+
+					// Execute the requested command.
+					ret = system(command.c_str());
+
+					// Inform our parent process of the return value for the command.
+					fputc(WEXITSTATUS(ret), stdout);
+					fflush(stdout);
+					break;
+
+				case 'X':
+					fputc(0, stdout);
+					fflush(stdout);
+					_exit(EXIT_SUCCESS);
+					break;
+
+				default:
+					fputc(1, stdout);
+					fflush(stdout);
+					_exit(EXIT_FAILURE);
+					break;
+				}
+			}
+
+			_exit(EXIT_FAILURE);
+		}
+
+		_exit(EXIT_SUCCESS);
+	}
+
+	// Wait for the first fork to return, and place the return value in errno
+	if (waitpid(pid, &status, 0) < 0) {
+		syslog(LOG_ERR, "Call to waitpid() failed: %s (%d)", strerror(errno), errno);
+	}
+
+	if (0 != WEXITSTATUS(status)) {
+		// If this has happened then the double fork failed. Clean up
+		// and pass this status along to the caller as errno.
+
+		syslog(LOG_ERR, "Child process failed: %s (%d)", strerror(WEXITSTATUS(status)), WEXITSTATUS(status));
+
+		close(mFirmwareUpgradeFD);
+		mFirmwareUpgradeFD = -1;
+
+		errno = WEXITSTATUS(status);
+
+	} else {
+		int saved_flags = fcntl(mFirmwareUpgradeFD, F_GETFL, 0);
+		fcntl(mFirmwareUpgradeFD, F_SETFL, saved_flags | O_NONBLOCK);
+	}
+}
+
+void
+FirmwareUpgrade::set_firmware_check_command(const std::string& command)
+{
+	int status = -1;
+	pid_t pid = -1;
+
+	if (mFirmwareCheckFD >= 0) {
+		close(mFirmwareCheckFD);
+		mFirmwareCheckFD = -1;
+	}
+
+	pid = fork_unixdomain_socket(&mFirmwareCheckFD);
+
+	if (pid < 0) {
+		return;
+	}
+
+	if (pid == 0) {
+		int stdout_copy = dup(STDOUT_FILENO);
+		int stdin_copy = dup(STDIN_FILENO);
+
+		dup2(STDERR_FILENO,STDOUT_FILENO);
+
+		if (stdin_copy >=0 ) {
+			close(STDIN_FILENO);
+			stdin = fdopen(stdin_copy, "r");
+		}
+
+		if (stdout_copy >=0 ) {
+			stdout = fdopen(stdout_copy, "w");
+		}
+
+		// Double fork to avoid leaking zombie processes.
+		pid = fork();
+		if (pid < 0) {
+			syslog(LOG_ERR, "Call to fork() failed: %s (%d)", strerror(errno), errno);
+
+			_exit(errno);
+		}
+
+		if (0 == pid)
+		{
+			// Set the shell environment variable if it isn't set already.
+			setenv("SHELL","/bin/sh",0);
+
+			while ((ferror(stdin) == 0) && (feof(stdin) == 0)) {
+				int ret;
+				const char* line = NULL;
+				size_t line_len = 0;
+				std::string escaped_line;
+
+				line = fgetln(stdin, &line_len);
+
+				if (!line || (line_len == 0)) {
+					continue;
+				}
+
+				if ((line[0] == 'X') && (line[1] == 0)) {
+					fputc(0, stdout);
+					fflush(stdout);
+					_exit(EXIT_SUCCESS);
+				}
+
+				// Open quotation
+				escaped_line = " '";
+
+				// Sanitize and escape the string
+				for(;line_len != 0;line_len--, line++) {
+					char c = *line;
+					if ((c < 32) && (c != '\t') && (c != '\n') && (c != '\r')) {
+						// Control characters aren't allowed.
+						syslog(LOG_ERR, "FirmwareCheck: Prohibited character (%d) in version string", c);
+						fputc('E', stdout);
+						fflush(stdout);
+						_exit(EXIT_FAILURE);
+					}
+					switch (c) {
+					case '\'':
+						escaped_line += "'\''";
+						break;
+					case '\n':
+					case '\r':
+						break;
+					default:
+						escaped_line += c;
+						break;
+					}
+				}
+
+				// Close quotation
+				escaped_line += "'";
+
+				ret = system((command + escaped_line).c_str());
+
+				fputc(WEXITSTATUS(ret), stdout);
+				fflush(stdout);
+			}
+
+			_exit(EXIT_FAILURE);
+		}
+
+		_exit(EXIT_SUCCESS);
+	}
+
+	// Wait for the first fork to return, and place the return value in errno
+	if (waitpid(pid, &status, 0) < 0) {
+		syslog(LOG_ERR, "Call to waitpid() failed: %s (%d)", strerror(errno), errno);
+	}
+
+	if (0 != WEXITSTATUS(status)) {
+		// If this has happened then the double fork failed. Clean up
+		// and pass this status along to the caller as errno.
+
+		syslog(LOG_ERR, "Child process failed: %s (%d)", strerror(WEXITSTATUS(status)), WEXITSTATUS(status));
+
+		close(mFirmwareCheckFD);
+		mFirmwareCheckFD = -1;
+
+		errno = WEXITSTATUS(status);
+	}
+}
+
+bool
+FirmwareUpgrade::can_upgrade_firmware(void)
+{
+	return (mFirmwareUpgradeFD >= 0);
+}
+
+int
+FirmwareUpgrade::get_upgrade_status(void)
+{
+	return mUpgradeStatus;
+}
+
+int
+FirmwareUpgrade::update_fd_set(fd_set *read_fd_set, fd_set *write_fd_set, fd_set *error_fd_set, int *max_fd, cms_t *timeout)
+{
+	if ((mUpgradeStatus == EINPROGRESS) && (mFirmwareUpgradeFD >= 0)) {
+		if (read_fd_set != NULL) {
+			FD_SET(mFirmwareUpgradeFD, read_fd_set);
+		}
+		if (error_fd_set != NULL) {
+			FD_SET(mFirmwareUpgradeFD, error_fd_set);
+		}
+		if (max_fd != NULL) {
+			*max_fd = std::max(*max_fd, mFirmwareUpgradeFD);
+		}
+	}
+
+	return 0;
+}
+
+void
+FirmwareUpgrade::process(void)
+{
+	if (mUpgradeStatus == EINPROGRESS) {
+		ssize_t bytes_read;
+		uint8_t value;
+
+		bytes_read = read(mFirmwareUpgradeFD, &value, 1);
+
+		if ((bytes_read < 0) && (errno != EWOULDBLOCK)) {
+			mUpgradeStatus = errno;
+		} else if (bytes_read == 1) {
+			mUpgradeStatus = value;
+
+			// If the upgrade status was successful, go ahead and close
+			// down both checks so that we don't waste resources. This
+			// also has the added benefit of preventing upgrade loops.
+			if (mUpgradeStatus == 0) {
+				close_check_fd();
+				close_upgrade_fd();
+			}
+		}
+	}
+}
diff --git a/src/wpantund/FirmwareUpgrade.h b/src/wpantund/FirmwareUpgrade.h
new file mode 100644
index 0000000..33d1e83
--- /dev/null
+++ b/src/wpantund/FirmwareUpgrade.h
@@ -0,0 +1,69 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      Firmware Upgrade Manager
+ *
+ */
+
+#ifndef __wpantund__FirmwareUpgrade__
+#define __wpantund__FirmwareUpgrade__
+
+#include <stdio.h>
+#include <sys/select.h>
+#include <string>
+#include "time-utils.h"
+
+namespace nl {
+namespace wpantund {
+
+class FirmwareUpgrade {
+public:
+	FirmwareUpgrade();
+	~FirmwareUpgrade();
+
+	bool is_firmware_upgrade_required(const std::string& version);
+	void upgrade_firmware(void);
+
+	void set_firmware_upgrade_command(const std::string& command);
+	void set_firmware_check_command(const std::string& command);
+
+	// May return:
+	//  * `0`: An upgrade was not started or the upgrade completed successfully.
+	//	* `EINPROGRESS`: An upgrade is currently in process.
+	//  * Any Other Value: An error occured when attempting to upgrade.
+	int get_upgrade_status(void);
+
+	int update_fd_set(fd_set *read_fd_set, fd_set *write_fd_set, fd_set *error_fd_set, int *max_fd, cms_t *timeout);
+	void process(void);
+
+	bool can_upgrade_firmware(void);
+
+private:
+	void close_check_fd(void);
+	void close_upgrade_fd(void);
+
+private:
+	int mUpgradeStatus;
+	int mFirmwareCheckFD;
+	int mFirmwareUpgradeFD;
+};
+
+}; // namespace wpantund
+}; // namespace nl
+
+#endif /* defined(__wpantund__FirmwareUpgrade__) */
diff --git a/src/wpantund/IPCServer.h b/src/wpantund/IPCServer.h
new file mode 100644
index 0000000..21f4572
--- /dev/null
+++ b/src/wpantund/IPCServer.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef wpantund_ipc_server_h
+#define wpantund_ipc_server_h
+
+#include "NCPControlInterface.h"
+#include <sys/select.h>
+#include <unistd.h>
+
+namespace nl {
+namespace wpantund {
+
+class IPCServer {
+public:
+	virtual ~IPCServer() {}
+	virtual cms_t get_ms_to_next_event(void) = 0;
+	virtual void process(void) = 0;
+
+	virtual int update_fd_set(fd_set *read_fd_set, fd_set *write_fd_set, fd_set *error_fd_set, int *max_fd, cms_t *timeout) = 0;
+
+	virtual int add_interface(NCPControlInterface* instance) = 0;
+};
+
+};
+};
+
+#endif
diff --git a/src/wpantund/Makefile.am b/src/wpantund/Makefile.am
new file mode 100644
index 0000000..ca8339a
--- /dev/null
+++ b/src/wpantund/Makefile.am
@@ -0,0 +1,113 @@
+#
+# Copyright (c) 2016 Nest Labs, Inc.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src \
+	-I$(top_srcdir)/src/util \
+	-I$(top_srcdir)/src/ipc-dbus \
+	-I$(top_srcdir)/third_party/fgetln \
+	-I$(top_srcdir)/third_party/pt \
+	-I$(top_srcdir)/third_party/assert-macros \
+	$(NULL)
+
+DISTCLEANFILES = \
+	.deps \
+	Makefile \
+	$(NULL)
+
+EXTRA_DIST = \
+	wpantund.conf \
+	$(NULL)
+
+pkginclude_HEADERS = \
+	wpan-properties.h \
+	wpan-error.h \
+	$(NULL)
+
+sysconf_DATA = wpantund.conf
+
+sbin_PROGRAMS = wpantund
+
+wpantund_SOURCES = \
+	wpantund.cpp \
+	wpantund.h \
+	IPCServer.h \
+	NCPConstants.h \
+	NCPControlInterface.cpp \
+	NCPControlInterface.h \
+	NCPInstance.cpp \
+	NCPInstance.h \
+	NCPInstanceBase.cpp \
+	NCPInstanceBase.h \
+	NCPMfgInterface.h \
+	NetworkInstance.h \
+	NCPConstants.h \
+	FirmwareUpgrade.h \
+	FirmwareUpgrade.cpp \
+	StatCollector.h \
+	StatCollector.cpp \
+	RunawayResetBackoffManager.cpp \
+	RunawayResetBackoffManager.h \
+	NCPInstanceBase-NetInterface.cpp \
+	NCPInstanceBase-Addresses.cpp \
+	NCPInstanceBase-AsyncIO.cpp \
+	NCPTypes.h \
+	NCPTypes.cpp \
+	../util/IPv6PacketMatcher.cpp \
+	../util/tunnel.c \
+	../util/config-file.c \
+	../util/socket-utils.c \
+	../util/any-to.cpp \
+	../util/string-utils.c \
+	../util/time-utils.c \
+	../util/nlpt-select.c \
+	../util/Data.cpp \
+	../util/SocketWrapper.cpp \
+	../util/SocketAdapter.cpp \
+	../util/UnixSocket.cpp \
+	../util/SuperSocket.cpp \
+	../util/EventHandler.cpp \
+	../util/TunnelIPv6Interface.cpp \
+	../util/ValueMap.cpp \
+	../util/Timer.cpp \
+	$(NULL)
+
+wpantund_LDADD =  $(DBUS_LIBS)
+wpantund_LDADD += ../ipc-dbus/libwpantund-dbus.la
+
+if STATIC_LINK_NCP_PLUGIN
+wpantund_LDADD += ../ncp-@default_ncp_plugin@/libncp-@default_ncp_plugin@.la
+else
+wpantund_LDADD += $(LIBDL_LIBS)
+wpantund_LDFLAGS = @EXPORT_DYNAMIC_LDFLAGS@
+endif
+
+wpantund_CPPFLAGS = $(AM_CPPFLAGS) $(DBUS_CFLAGS)
+wpantund_CXXFLAGS = $(BOOST_CXXFLAGS)
+
+SOURCE_VERSION=$(shell                                            \
+	git describe --dirty --always --match "[0-9].*" 2> /dev/null  \
+)
+
+BUILT_SOURCES  = $(top_builddir)/$(subdir)/version.c
+CLEANFILES     = $(top_builddir)/$(subdir)/version.c
+.INTERMEDIATE:   $(top_builddir)/$(subdir)/version.c wpantund-version.$(OBJEXT)
+
+$(top_builddir)/$(subdir)/version.c: ../version.c.in Makefile
+	sed 's/SOURCE_VERSION/"$(SOURCE_VERSION)"/' < $< > $@
+
+wpantund_SOURCES += $(top_builddir)/$(subdir)/version.c
diff --git a/src/wpantund/NCPConstants.h b/src/wpantund/NCPConstants.h
new file mode 100644
index 0000000..9f9e2ef
--- /dev/null
+++ b/src/wpantund/NCPConstants.h
@@ -0,0 +1,33 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef wpantund_NCPConstants_h
+#define wpantund_NCPConstants_h
+
+#define NCP_DEFAULT_COMMAND_RESPONSE_TIMEOUT    5  // seconds
+#define NCP_DEFAULT_COMMAND_SEND_TIMEOUT        5  // seconds
+#define NCP_TICKLE_TIMEOUT                      60 // seconds
+#define NCP_DEEP_SLEEP_TICKLE_TIMEOUT          (60*70) // Seventy minutes
+#define NCP_FORM_TIMEOUT						60 // seconds
+#define NCP_JOIN_TIMEOUT						30 // seconds
+
+#define BUSY_DEBOUNCE_TIME_IN_MS         200
+#define MAX_INSOMNIA_TIME_IN_MS                 (MSEC_PER_SEC * 60 * 3)
+
+#endif
diff --git a/src/wpantund/NCPControlInterface.cpp b/src/wpantund/NCPControlInterface.cpp
new file mode 100644
index 0000000..19176be
--- /dev/null
+++ b/src/wpantund/NCPControlInterface.cpp
@@ -0,0 +1,409 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *		Abstract base class for NCP implementations.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include "NCPControlInterface.h"
+#include "NCPInstance.h"
+#include "tunnel.h"
+#include <syslog.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <boost/bind.hpp>
+#include "version.h"
+#include "any-to.h"
+#include "wpan-error.h"
+
+#ifndef SOURCE_VERSION
+#define SOURCE_VERSION		PACKAGE_VERSION
+#endif
+
+using namespace nl;
+using namespace wpantund;
+
+std::string
+NCPControlInterface::external_route_priority_to_string(ExternalRoutePriority route_priority)
+{
+	switch(route_priority) {
+		case ROUTE_LOW_PREFRENCE:
+			return std::string("low preference");
+
+		case ROUTE_MEDIUM_PREFERENCE:
+			return std::string("Medium (normal) preference");
+
+		case ROUTE_HIGH_PREFERENCE:
+			return std::string("High preference");
+	}
+	return std::string("Unknown route preference");
+}
+
+NCPControlInterface::~NCPControlInterface() {
+}
+
+struct GetPropertyHelper {
+	boost::any *dest;
+	bool *          didFire;
+
+	void operator()(
+	    int status, const boost::any& value
+	    ) const
+	{
+		if (dest) {
+			if (status == 0)
+				*dest = value;
+			*didFire = true;
+		}
+		delete this;
+	}
+};
+
+boost::any
+NCPControlInterface::get_property(const std::string& key)
+{
+	// In this function, we want to immediately return the value
+	// for the given key. Since the actual get_property() function
+	// returns the value as a callback, we may not actually
+	// be able to do this. What we do here is give a callback
+	// object that contains a pointer to our return value.
+	// After we call get_property(), we then zero out that
+	// pointer. For properties which get updated immediately, our
+	// return value will be updated automatically. For properties
+	// which can't be fetched immediately, we return an empty value.
+	// The "GetPropertyHelper" class makes sure that when the
+	// callback eventually does fire that it doesn't break anything.
+
+	boost::any ret;
+	bool didFire = false;
+	struct GetPropertyHelper *helper = new GetPropertyHelper;
+
+	helper->dest = &ret;
+	helper->didFire = &didFire;
+	get_property(key, boost::bind(&GetPropertyHelper::operator(),
+	                              helper,
+	                              _1,
+	                              _2));
+	if (!didFire) {
+		helper->dest = 0;
+		helper->didFire = 0;
+	}
+	return ret;
+}
+
+std::string
+NCPControlInterface::get_name() {
+	return boost::any_cast<std::string>(get_property(kWPANTUNDProperty_ConfigTUNInterfaceName));
+}
+
+
+struct SetPropertyHelper {
+	int* dest;
+	bool* didFire;
+
+	void operator()(int status) const
+	{
+		if (dest) {
+			*dest = status;
+			*didFire = true;
+		}
+		delete this;
+	}
+};
+
+#define kWPANTUNDPropertyNCPSocketName             "NCPSocketName"
+#define kWPANTUNDPropertyNCPSocketBaud             "NCPSocketBaud"
+#define kWPANTUNDPropertyNCPDriverName             "NCPDriverName"
+#define kWPANTUNDPropertyNCPHardResetPath          "NCPHardResetPath"
+#define kWPANTUNDPropertyNCPPowerPath              "NCPPowerPath"
+#define kWPANTUNDPropertyWPANInterfaceName         "WPANInterfaceName"
+#define kWPANTUNDPropertyPIDFile                   "PIDFile"
+#define kWPANTUNDPropertyFirmwareCheckCommand      "FirmwareCheckCommand"
+#define kWPANTUNDPropertyFirmwareUpgradeCommand    "FirmwareUpgradeCommand"
+#define kWPANTUNDPropertyTerminateOnFault          "TerminateOnFault"
+#define kWPANTUNDPropertyPrivDropToUser            "PrivDropToUser"
+#define kWPANTUNDPropertyChroot                    "Chroot"
+#define kWPANTUNDPropertyNCPReliabilityLayer       "NCPReliabilityLayer"
+
+// Version Properties
+#define kWPANTUNDPropertyNCPVersion                "NCPVersion"
+#define kWPANTUNDPropertyDriverVersion             "DriverVersion"
+
+// Driver State Properties
+#define kWPANTUNDPropertyAssociationState          "AssociationState"    // [RO]
+#define kWPANTUNDPropertyEnabled                   "Enabled"             // [RW]
+#define kWPANTUNDPropertyAutoresume                "AutoResume"          // [RW]
+#define kWPANTUNDPropertyAutoUpdateFirmware        "AutoUpdateFirmware"  // [RW]
+
+// PHY-layer parameters
+#define kWPANTUNDPropertyHWAddr                    "HWAddr"
+#define kWPANTUNDPropertyChannel                   "Channel"
+#define kWPANTUNDPropertyTXPower                   "TXPower"
+#define kWPANTUNDPropertyNCPTXPowerLimit           "NCPTXPowerLimit"
+#define kWPANTUNDPropertyCCAThreshold              "CCAThreshold"
+#define kWPANTUNDPropertyDefaultChannelMask        "DefaultChannelMask"
+
+// MAC-layer (and higher) parameters
+#define kWPANTUNDPropertyNetworkName               "NetworkName"         // [RO]
+#define kWPANTUNDPropertyXPANID                    "XPANID"              // [RO]
+#define kWPANTUNDPropertyPANID                     "PANID"               // [RO]
+#define kWPANTUNDPropertyNodeType                  "NodeType"            // [RW]
+#define kWPANTUNDPropertyNetworkKey                "NetworkKey"          // [RW]
+#define kWPANTUNDPropertyNetworkKeyIndex           "NetworkKeyIndex"     // [RW]
+#define kWPANTUNDPropertyMeshLocalPrefix           "MeshLocalPrefix"     // [RO]
+#define kWPANTUNDPropertyAllowingJoin              "AllowingJoin"        // [RO]
+#define kWPANTUNDPropertyIsAssociated              "IsAssociated"        // [RO]
+
+// Power Management Properties
+#define kWPANTUNDPropertyIsOKToSleep               "IsOKToSleep"
+#define kWPANTUNDPropertyUseDeepSleepOnLowPower    "UseDeepSleepOnLowPower"
+#define kWPANTUNDPropertyAlwaysResetToWake         "AlwaysResetToWake"
+#define kWPANTUNDPropertyAutoDeepSleep             "AutoDeepSleep"
+#define kWPANTUNDPropertySleepPollInterval         "SleepPollInterval"
+
+// Debugging and logging
+#define kWPANTUNDPropertySyslogMask                "SyslogMask"
+#define kWPANTUNDPropertyNCPDebug                  "NCPDebug"
+
+// Properties related to manufacturing test commands
+#define kWPANTUNDPropertyMfgTestMode               "MfgTestMode"
+#define kWPANTUNDPropertyMfgSYNOffset              "MfgSYNOffset"
+#define kWPANTUNDPropertyMfgRepeatRandomTXInterval "MfgRepeatRandomTXInterval"
+#define kWPANTUNDPropertyMfgRepeatRandomTXLen      "MfgRepeatRandomTXLen"
+#define kWPANTUNDPropertyMfgFirstPacketRSSI        "MfgFirstPacketRSSI"
+#define kWPANTUNDPropertyMfgFirstPacketLQI         "MfgFirstPacketLQI"
+
+
+// Nest-Specific Properties
+#define kWPANTUNDPropertyPassthruPort              "PassthruPort"
+#define kWPANTUNDPropertyTransmitHookActive        "TransmitHookActive"
+#define kWPANTUNDPropertyUseLegacyChannel          "UseLegacyChannel"
+#define kWPANTUNDPropertyLegacyPrefix              "LegacyPrefix"
+#define kWPANTUNDPropertyNetWakeData               "NetWakeData"
+#define kWPANTUNDPropertyNetWakeRemaining          "NetWakeRemaining"
+#define kWPANTUNDPropertyActiveWakeupBlacklist     "ActiveWakeupBlacklist"
+#define kWPANTUNDPropertyLegacyInterfaceEnabled    "LegacyInterfaceEnabled"
+#define kWPANTUNDPropertyPrefix                    "Prefix"
+
+#define kWPANTUNDPropertyGlobalIPAddresses         "GlobalIPAddresses"
+#define kWPANTUNDPropertyGlobalIPAddressList       "GlobalIPAddressList"
+
+int
+NCPControlInterface::set_property(const std::string& key, const boost::any& value)
+{
+	// In this function, we want to immediately return the status
+	// of the set operation. Since the actual set_property() function
+	// returns the status as a callback, we may not actually
+	// be able to do this. What we do here is give a callback
+	// object that contains a pointer to our return value.
+	// After we call set_property(), we then zero out that
+	// pointer. For properties which get updated immediately, our
+	// return value will be updated automatically. For properties
+	// which can't be fetched immediately, we return -EINPROGRESS.
+	// The "SetPropertyHelper" class makes sure that when the
+	// callback eventually does fire that it doesn't break anything.
+
+	int ret = kWPANTUNDStatus_InProgress;
+	bool didFire = false;
+	struct SetPropertyHelper *helper = new SetPropertyHelper;
+
+	helper->dest = &ret;
+	helper->didFire = &didFire;
+	set_property(key, value, boost::bind(&SetPropertyHelper::operator(),
+	                              helper,
+	                              _1));
+	if (!didFire) {
+		helper->dest = 0;
+		helper->didFire = 0;
+	}
+	return ret;
+}
+
+
+bool
+NCPControlInterface::translate_deprecated_property(std::string& key, boost::any& value)
+{
+	bool ret = false;
+	if (strcaseequal(key.c_str(), kWPANTUNDPropertyPrefix)) {
+		key = kWPANTUNDProperty_IPv6MeshLocalPrefix;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyNCPSocketName )) {
+		key =  kWPANTUNDProperty_ConfigNCPSocketPath ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyNCPSocketBaud )) {
+		key =  kWPANTUNDProperty_ConfigNCPSocketBaud ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyNCPDriverName )) {
+		key =  kWPANTUNDProperty_ConfigNCPDriverName ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyNCPHardResetPath )) {
+		key =  kWPANTUNDProperty_ConfigNCPHardResetPath ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyNCPPowerPath )) {
+		key =  kWPANTUNDProperty_ConfigNCPPowerPath ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyWPANInterfaceName )) {
+		key =  kWPANTUNDProperty_ConfigTUNInterfaceName ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyPIDFile )) {
+		key =  kWPANTUNDProperty_ConfigDaemonPIDFile ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyFirmwareCheckCommand )) {
+		key =  kWPANTUNDProperty_ConfigNCPFirmwareCheckCommand ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyFirmwareUpgradeCommand )) {
+		key =  kWPANTUNDProperty_ConfigNCPFirmwareUpgradeCommand ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyTerminateOnFault )) {
+		key =  kWPANTUNDProperty_DaemonTerminateOnFault ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyPrivDropToUser )) {
+		key =  kWPANTUNDProperty_ConfigDaemonPrivDropToUser ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyChroot )) {
+		key =  kWPANTUNDProperty_ConfigDaemonChroot ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyNCPReliabilityLayer )) {
+		key =  kWPANTUNDProperty_ConfigNCPReliabilityLayer ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyNCPVersion )) {
+		key =  kWPANTUNDProperty_NCPVersion ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyDriverVersion )) {
+		key =  kWPANTUNDProperty_DaemonVersion ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyAssociationState )) {
+		key =  kWPANTUNDProperty_NCPState ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyEnabled )) {
+		key =  kWPANTUNDProperty_DaemonEnabled ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyAutoresume )) {
+		key =  kWPANTUNDProperty_DaemonAutoAssociateAfterReset ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyAutoUpdateFirmware )) {
+		key =  kWPANTUNDProperty_DaemonAutoFirmwareUpdate ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyHWAddr )) {
+		key =  kWPANTUNDProperty_NCPHardwareAddress ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyChannel )) {
+		key =  kWPANTUNDProperty_NCPChannel ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyTXPower )) {
+		key =  kWPANTUNDProperty_NCPTXPower ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyNCPTXPowerLimit )) {
+		key =  kWPANTUNDProperty_NCPTXPowerLimit ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyCCAThreshold )) {
+		key =  kWPANTUNDProperty_NCPCCAThreshold ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyDefaultChannelMask )) {
+		key =  kWPANTUNDProperty_NCPChannelMask ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyNetworkName )) {
+		key =  kWPANTUNDProperty_NetworkName ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyXPANID )) {
+		key =  kWPANTUNDProperty_NetworkXPANID ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyPANID )) {
+		key =  kWPANTUNDProperty_NetworkPANID ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyNodeType )) {
+		key =  kWPANTUNDProperty_NetworkNodeType ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyNetworkKey )) {
+		key =  kWPANTUNDProperty_NetworkKey ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyNetworkKeyIndex )) {
+		key =  kWPANTUNDProperty_NetworkKeyIndex ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyMeshLocalPrefix )) {
+		key =  kWPANTUNDProperty_IPv6MeshLocalPrefix ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyAllowingJoin )) {
+		key =  kWPANTUNDProperty_NestLabs_NetworkAllowingJoin ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyIsAssociated )) {
+		key =  kWPANTUNDProperty_NetworkIsCommissioned ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyIsOKToSleep )) {
+		key =  kWPANTUNDProperty_DaemonReadyForHostSleep ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyUseDeepSleepOnLowPower )) {
+		key =  kWPANTUNDProperty_NestLabs_HackUseDeepSleepOnLowPower ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyAlwaysResetToWake )) {
+		key =  kWPANTUNDProperty_NestLabs_HackAlwaysResetToWake ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyAutoDeepSleep )) {
+		key =  kWPANTUNDProperty_DaemonAutoDeepSleep ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertySleepPollInterval )) {
+		key =  kWPANTUNDProperty_NCPSleepyPollInterval ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertySyslogMask )) {
+		key =  kWPANTUNDProperty_DaemonSyslogMask ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyPassthruPort )) {
+		key =  kWPANTUNDProperty_NestLabs_NetworkPassthruPort ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyTransmitHookActive )) {
+		key =  kWPANTUNDProperty_NestLabs_NCPTransmitHookActive ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyLegacyPrefix )) {
+		key =  kWPANTUNDProperty_NestLabs_LegacyMeshLocalPrefix ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyNetWakeData )) {
+		key =  kWPANTUNDProperty_NestLabs_NetworkWakeData ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyNetWakeRemaining )) {
+		key =  kWPANTUNDProperty_NestLabs_NetworkWakeRemaining ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyActiveWakeupBlacklist ) || strcaseequal(key.c_str(), "ActiveWakeupMask")) {
+		key =  kWPANTUNDProperty_NestLabs_NetworkWakeBlacklist ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyLegacyInterfaceEnabled )) {
+		key =  kWPANTUNDProperty_NestLabs_LegacyEnabled ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyUseLegacyChannel )) {
+		key =  kWPANTUNDProperty_NestLabs_LegacyPreferInterface ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyGlobalIPAddresses )) {
+		key =  kWPANTUNDProperty_IPv6AllAddresses ;
+		ret = true;
+	} else if (strcaseequal(key.c_str(),  kWPANTUNDPropertyGlobalIPAddressList )) {
+		key =  kWPANTUNDProperty_DebugIPv6GlobalIPAddressList ;
+		ret = true;
+	}
+	return ret;
+}
+
+bool
+NCPControlInterface::translate_deprecated_property(std::string& key)
+{
+	boost::any unused;
+	return translate_deprecated_property(key, unused);
+}
diff --git a/src/wpantund/NCPControlInterface.h b/src/wpantund/NCPControlInterface.h
new file mode 100644
index 0000000..6bdc2ff
--- /dev/null
+++ b/src/wpantund/NCPControlInterface.h
@@ -0,0 +1,230 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef wpantund_WPAN_NCPControlInterface_h
+#define wpantund_WPAN_NCPControlInterface_h
+
+#ifndef __STDC_CONSTANT_MACROS
+#define __STDC_CONSTANT_MACROS 1
+#endif
+
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+#include <boost/signals2/signal.hpp>
+#include <boost/any.hpp>
+#include <list>
+#include <arpa/inet.h>
+#include "time-utils.h"
+
+#include "NetworkInstance.h"
+#include <cstring>
+#include "IPv6PacketMatcher.h"
+#include "Data.h"
+#include "NilReturn.h"
+#include "NCPConstants.h"
+#include "Callbacks.h"
+#include "wpan-properties.h"
+#include "ValueMap.h"
+
+namespace nl {
+namespace wpantund {
+
+#define USE_DEFAULT_TX_POWER               INT32_MIN
+#define USE_DEFAULT_CCA_THRESHOLD          INT32_MIN
+#define USE_DEFAULT_TX_POWER_MODE          INT32_MIN
+#define USE_DEFAULT_TRANSMIT_HOOK_ACTIVE   INT32_MIN
+
+typedef std::set<int> IntegerSet;
+
+class NCPInstance;
+
+class NCPControlInterface {
+public:
+
+	typedef uint32_t ChannelMask;
+
+	enum ExternalRoutePriority {
+		ROUTE_LOW_PREFRENCE = -1,
+		ROUTE_MEDIUM_PREFERENCE = 0,
+		ROUTE_HIGH_PREFERENCE = 1,
+	};
+
+public:
+	// ========================================================================
+	// Static Functions
+
+
+	static std::string external_route_priority_to_string(ExternalRoutePriority route_priority);
+
+public:
+	// ========================================================================
+	// Public Virtual Member Functions
+
+	//! Returns the network instance currently associated with the NCP.
+	virtual const WPAN::NetworkInstance& get_current_network_instance(void)const = 0;
+
+public:
+	// ========================================================================
+	// NCP Command Virtual Member Functions
+
+	virtual void join(
+		const ValueMap& options,
+	    CallbackWithStatus cb = NilReturn()
+	) = 0;
+
+	virtual void form(
+		const ValueMap& options,
+	    CallbackWithStatus cb = NilReturn()
+	) = 0;
+
+	virtual void leave(CallbackWithStatus cb = NilReturn()) = 0;
+
+	virtual void attach(CallbackWithStatus cb = NilReturn()) = 0;
+
+	virtual void reset(CallbackWithStatus cb = NilReturn()) = 0;
+
+	virtual void refresh_state(CallbackWithStatus cb = NilReturn()) = 0;
+
+	virtual void get_property(
+	    const std::string& key,
+	    CallbackWithStatusArg1 cb
+	) = 0;
+
+	virtual void set_property(
+	    const std::string& key,
+	    const boost::any& value,
+	    CallbackWithStatus cb
+	) = 0;
+
+	virtual void config_gateway(
+		bool defaultRoute,
+		const uint8_t prefix[8],
+		uint32_t preferredLifetime,
+		uint32_t validLifetime,
+		CallbackWithStatus cb = NilReturn()
+	) = 0;
+
+	virtual void add_external_route(
+		const uint8_t route[],
+		int route_prefix_len,
+		int domain_id,
+		ExternalRoutePriority priority,
+		CallbackWithStatus cb = NilReturn()
+	) = 0;
+
+	virtual void remove_external_route(
+		const uint8_t route[],
+		int route_prefix_len,
+		int domain_id,
+		CallbackWithStatus cb = NilReturn()
+	) = 0;
+
+public:
+	// ========================================================================
+	// Scan-related Member Functions
+
+	virtual void netscan_start(const ValueMap& options, CallbackWithStatus cb = NilReturn()) = 0;
+
+	virtual void netscan_stop(CallbackWithStatus cb = NilReturn()) = 0;
+
+	boost::signals2::signal<void(const WPAN::NetworkInstance&)> mOnNetScanBeacon;
+
+
+public:
+	// ========================================================================
+	// Power-related Member Functions
+
+	virtual void begin_low_power(CallbackWithStatus cb = NilReturn()) = 0;
+
+	virtual void host_did_wake(CallbackWithStatus cb = NilReturn()) = 0;
+
+	virtual void data_poll(CallbackWithStatus cb = NilReturn()) = 0;
+
+
+public:
+	// ========================================================================
+	// Nest-Specific Member Functions
+
+	virtual void begin_net_wake(
+		uint8_t data = 0,
+		uint32_t flags = 0,
+		CallbackWithStatus cb = NilReturn()
+	) = 0;
+
+	virtual void permit_join(
+	    int seconds = 15 * 60,
+	    uint8_t commissioning_traffic_type = 0xFF,
+	    in_port_t commissioning_traffic_port = 0,
+	    bool network_wide = false,
+	    CallbackWithStatus cb = NilReturn()
+	) = 0;
+
+
+public:
+	// ========================================================================
+	// Convenience methods
+
+	boost::any get_property(const std::string& key);
+
+	int set_property(const std::string& key, const boost::any& value);
+
+	virtual std::string get_name();
+
+public:
+	// ========================================================================
+	// Other
+
+	static bool translate_deprecated_property(std::string& key, boost::any& value);
+
+	static bool translate_deprecated_property(std::string& key);
+
+
+public:
+	// ========================================================================
+	// Signals
+
+	//! Fires whenever value of certain properties changed (e.g. NodeType).
+	boost::signals2::signal<void(const std::string& key, const boost::any& value)> mOnPropertyChanged;
+
+public:
+	// ========================================================================
+	// Nest-Specific Signals
+
+	//! Fires when the network wake state has changed or been updated.
+	boost::signals2::signal<void(uint8_t data, cms_t ms_remaining)> mOnNetWake;
+
+protected:
+	// ========================================================================
+	// Protected Virtual Member Functions
+
+	virtual ~NCPControlInterface();
+
+	//! Returns the associated NCP instance.
+	virtual NCPInstance& get_ncp_instance(void) = 0;
+};
+
+}; // namespace wpantund
+}; // namespace nl
+
+#endif
diff --git a/src/wpantund/NCPInstance.cpp b/src/wpantund/NCPInstance.cpp
new file mode 100644
index 0000000..1b198cc
--- /dev/null
+++ b/src/wpantund/NCPInstance.cpp
@@ -0,0 +1,149 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "assert-macros.h"
+#include "NCPInstance.h"
+#include "tunnel.h"
+#include <syslog.h>
+#include <errno.h>
+
+#ifndef HAVE_DLFCN_H
+#define HAVE_DLFCN_H	defined(__APPLE__)
+#endif
+
+#ifndef PKGLIBEXECDIR
+#define PKGLIBEXECDIR	"/usr/local/libexec/wpantund"
+#endif
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#ifndef RTLD_DEFAULT
+#define RTLD_DEFAULT	((void*)0)
+#endif
+#ifndef RTLD_NEXT
+#define RTLD_NEXT		((void*)-1l)
+#endif
+#endif
+
+#if WPANTUND_PLUGIN_STATICLY_LINKED
+#undef HAVE_DLFCN_H
+#endif
+
+#ifndef WPANTUND_DEFAULT_NCP_PLUGIN
+#if WPANTUND_PLUGIN_STATICLY_LINKED
+#define WPANTUND_DEFAULT_NCP_PLUGIN "default"
+#else
+#define WPANTUND_DEFAULT_NCP_PLUGIN "spinel"
+#endif
+#endif
+
+using namespace nl;
+using namespace wpantund;
+
+typedef nl::wpantund::NCPInstance* (*NCPInstanceAllocatorType)(const nl::wpantund::NCPInstance::Settings& settings);
+
+NCPInstance*
+NCPInstance::alloc(const Settings& settings)
+{
+	NCPInstance* ret = NULL;
+	std::string ncp_driver_name = WPANTUND_DEFAULT_NCP_PLUGIN;
+	std::string symbol_name = "wpantund_ncpinstance_default_alloc";
+	NCPInstanceAllocatorType allocator = NULL;
+
+	if (settings.count(kWPANTUNDProperty_ConfigNCPDriverName)) {
+		ncp_driver_name = settings.find(kWPANTUNDProperty_ConfigNCPDriverName)->second;
+	}
+
+	if (ncp_driver_name == "default") {
+		ncp_driver_name = WPANTUND_DEFAULT_NCP_PLUGIN;
+	}
+
+#if WPANTUND_PLUGIN_STATICLY_LINKED
+	// Statically-linked plugin
+	if ((allocator == NULL) && (ncp_driver_name == WPANTUND_DEFAULT_NCP_PLUGIN)) {
+		allocator = &wpantund_ncpinstance_default_alloc;
+	}
+#else
+	// Dynamically-loaded plugin
+	std::string plugin_path;
+
+	if ((ncp_driver_name.find('/') != std::string::npos)
+	 || (ncp_driver_name.find('.') != std::string::npos)
+	) {
+		plugin_path = ncp_driver_name;
+	} else {
+		plugin_path = std::string("ncp-") + ncp_driver_name + ".so";
+		symbol_name = std::string("wpantund_ncpinstance_") +ncp_driver_name + "_alloc";
+
+		if( access( (std::string(PKGLIBEXECDIR "/") + plugin_path).c_str(), X_OK ) != -1 ) {
+			plugin_path = std::string(PKGLIBEXECDIR "/") + plugin_path;
+		}
+
+		allocator = (NCPInstanceAllocatorType)dlsym(RTLD_DEFAULT, symbol_name.c_str());
+	}
+
+	if (allocator == NULL) {
+		// We failed to immediately look up the allocator function,
+		// so lets see if we can find a plug-in that we can open,
+		// and then try again.
+
+		void* dl_handle = NULL;
+
+		dl_handle = dlopen(plugin_path.c_str(), RTLD_NOW|RTLD_LOCAL);
+
+		if (dl_handle == NULL) {
+			syslog(LOG_ERR, "Couldn't open plugin \"%s\", %s", plugin_path.c_str(), dlerror());
+		} else {
+			allocator = (NCPInstanceAllocatorType)dlsym(dl_handle, symbol_name.c_str());
+		}
+	}
+
+#endif
+
+	require_string(allocator != NULL, bail, "Unknown NCP Driver");
+
+	ret = (*allocator)(settings);
+
+bail:
+	if (ret == NULL) {
+		syslog(LOG_ERR, "Unable to load NCP driver \"%s\".", ncp_driver_name.c_str());
+	}
+
+	return ret;
+}
+
+NCPInstance::~NCPInstance()
+{
+}
+
+void
+NCPInstance::signal_fatal_error(int err)
+{
+	if (err == ERRORCODE_ERRNO) {
+		syslog(LOG_CRIT, "NCPInstance: errno %d \"%s\"\n", errno, strerror(
+				   errno));
+	} else {
+		syslog(LOG_CRIT, "NCPInstance: error %d\n", err);
+	}
+	mOnFatalError(err);
+}
diff --git a/src/wpantund/NCPInstance.h b/src/wpantund/NCPInstance.h
new file mode 100644
index 0000000..62a363b
--- /dev/null
+++ b/src/wpantund/NCPInstance.h
@@ -0,0 +1,122 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __wpantund__NCPInstance__
+#define __wpantund__NCPInstance__
+
+#define BOOST_SIGNALS_NO_DEPRECATION_WARNING 1
+
+#ifndef __STDC_CONSTANT_MACROS
+#define __STDC_CONSTANT_MACROS 1
+#endif
+
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+#include <boost/signals2/signal.hpp>
+#include <boost/any.hpp>
+#include <list>
+#include <arpa/inet.h>
+
+#include "NetworkInstance.h"
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <cstring>
+#include "IPv6PacketMatcher.h"
+#include "Data.h"
+#include "NilReturn.h"
+#include "NCPControlInterface.h"
+#include "tunnel.h"
+#include "SocketAdapter.h"
+#include "TunnelIPv6Interface.h"
+#include "NCPConstants.h"
+#include "wpan-error.h"
+#include "StatCollector.h"
+
+#define ERRORCODE_OK            (0)
+#define ERRORCODE_HELP          (1)
+#define ERRORCODE_BADARG        (2)
+#define ERRORCODE_NOCOMMAND     (3)
+#define ERRORCODE_UNKNOWN       (4)
+#define ERRORCODE_BADCOMMAND    (5)
+#define ERRORCODE_NOREADLINE    (6)
+#define ERRORCODE_QUIT          (7)
+#define ERRORCODE_BADCONFIG     (8)
+#define ERRORCODE_ERRNO         (9)
+
+#define ERRORCODE_INTERRUPT     (128 + SIGINT)
+#define ERRORCODE_SIGHUP        (128 + SIGHUP)
+
+#define WTD_WEAK                __attribute__((weak))
+
+#define WPANTUND_DECLARE_NCPINSTANCE_PLUGIN(short_name, class_name) \
+	extern "C" nl::wpantund::NCPInstance* wpantund_ncpinstance_ ## short_name ## _alloc(const nl::wpantund::NCPInstance::Settings& settings)
+
+#define WPANTUND_DEFINE_NCPINSTANCE_PLUGIN(short_name, class_name) \
+	nl::wpantund::NCPInstance* \
+	wpantund_ncpinstance_ ## short_name ## _alloc(const nl::wpantund::NCPInstance::Settings& settings) { \
+		return new class_name(settings); \
+	} \
+	nl::wpantund::NCPInstance* \
+	wpantund_ncpinstance_default_alloc(const nl::wpantund::NCPInstance::Settings& settings) { \
+		return new class_name(settings); \
+	}
+
+namespace nl {
+
+namespace wpantund {
+class NCPControlInterface;
+
+class NCPInstance {
+public:
+	typedef std::map<std::string, std::string> Settings;
+
+public:
+	static NCPInstance* alloc(
+		const Settings& settings = Settings()
+	);
+
+	virtual ~NCPInstance();
+
+	virtual const std::string &get_name(void) = 0;
+	virtual NCPControlInterface& get_control_interface(void) = 0;
+	virtual StatCollector& get_stat_collector(void) = 0;
+
+	virtual void set_socket_adapter(const boost::shared_ptr<SocketAdapter> &adapter) = 0;
+
+	virtual cms_t get_ms_to_next_event(void) = 0;
+	virtual void process(void) = 0;
+	virtual int update_fd_set(fd_set *read_fd_set, fd_set *write_fd_set, fd_set *error_fd_set, int *max_fd, cms_t *timeout) = 0;
+
+public:
+	void signal_fatal_error(int err);
+	SignalWithStatus mOnFatalError;
+}; // class NCPInstance
+
+}; // namespace wpantund
+}; // namespace nl
+
+extern "C" nl::wpantund::NCPInstance* wpantund_ncpinstance_default_alloc(const nl::wpantund::NCPInstance::Settings& settings);
+
+#endif /* defined(__wpantund__NCPInstance__) */
diff --git a/src/wpantund/NCPInstanceBase-Addresses.cpp b/src/wpantund/NCPInstanceBase-Addresses.cpp
new file mode 100644
index 0000000..38350c0
--- /dev/null
+++ b/src/wpantund/NCPInstanceBase-Addresses.cpp
@@ -0,0 +1,209 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "assert-macros.h"
+#include "NCPInstanceBase.h"
+#include "tunnel.h"
+#include <syslog.h>
+#include <errno.h>
+#include "nlpt.h"
+#include <algorithm>
+#include "socket-utils.h"
+#include "SuperSocket.h"
+
+using namespace nl;
+using namespace wpantund;
+
+
+
+void
+NCPInstanceBase::refresh_global_addresses()
+{
+	// Here is where we would do any periodic global address bookkeeping,
+	// which doesn't appear to be necessary yet but may become necessary
+	// in the future.
+}
+
+void
+NCPInstanceBase::clear_nonpermanent_global_addresses()
+{
+	std::map<struct in6_addr, GlobalAddressEntry>::iterator iter;
+
+	// We want to remove all of the addresses that were
+	// not user-added.
+	//
+	// This loop looks a little weird because we are mutating
+	// the container as we are iterating through it. Whenever
+	// we mutate the container we have to start over.
+	do {
+		for (iter = mGlobalAddresses.begin(); iter != mGlobalAddresses.end(); ++iter) {
+			// Skip the removal of user-added addresses.
+			if (iter->second.mUserAdded) {
+				continue;
+			}
+
+			mPrimaryInterface->remove_address(&iter->first);
+			mGlobalAddresses.erase(iter);
+
+			// The following assignment is needed to avoid
+			// an invalid iterator comparison in the outer loop.
+			iter = mGlobalAddresses.begin();
+
+			// Break out of the inner loop so that we start over.
+			break;
+		}
+	} while(iter != mGlobalAddresses.end());
+}
+
+void
+NCPInstanceBase::restore_global_addresses()
+{
+	std::map<struct in6_addr, GlobalAddressEntry>::const_iterator iter;
+	std::map<struct in6_addr, GlobalAddressEntry> global_addresses(mGlobalAddresses);
+
+	mGlobalAddresses.clear();
+
+	for (iter = global_addresses.begin(); iter!= global_addresses.end(); ++iter) {
+		if (iter->second.mUserAdded) {
+			address_was_added(iter->first, 64);
+		}
+		mGlobalAddresses.insert(*iter);
+		mPrimaryInterface->add_address(&iter->first);
+	}
+}
+
+void
+NCPInstanceBase::update_global_address(const struct in6_addr& address, uint32_t valid_lifetime, uint32_t preferred_lifetime, uint8_t flags)
+{
+	if (0 == memcmp(address.s6_addr, mNCPV6Prefix, sizeof(mNCPV6Prefix))) {
+		// wut. This address is with our mesh local prefix. Ignore it.
+
+	} else {
+		if (valid_lifetime == 0) {
+			syslog(LOG_INFO, "Removing Global IPv6 Address...");
+			mGlobalAddresses.erase(address);
+			mPrimaryInterface->remove_address(&address);
+
+		} else {
+
+			GlobalAddressEntry entry = GlobalAddressEntry();
+
+			if (mGlobalAddresses.count(address)) {
+				syslog(LOG_INFO, "Updating Global IPv6 Address...");
+				entry = mGlobalAddresses[address];
+			} else {
+				syslog(LOG_INFO, "Adding Global IPv6 Address...");
+				mPrimaryInterface->add_address(&address);
+			}
+
+			entry.mValidLifetime = valid_lifetime;
+			entry.mPreferredLifetime = preferred_lifetime;
+			entry.mFlags = flags;
+			entry.mValidLifetimeExpiration = time_get_monotonic() + entry.mValidLifetime;
+			entry.mPreferredLifetimeExpiration = time_get_monotonic() + entry.mPreferredLifetime;
+
+			mGlobalAddresses[address] = entry;
+		}
+	}
+}
+
+bool
+NCPInstanceBase::is_address_known(const struct in6_addr &address)
+{
+	bool ret(mGlobalAddresses.count(address) != 0);
+
+	if (!ret) {
+		ret = (mPrimaryInterface->get_realm_local_address() == address);
+	}
+
+	return ret;
+}
+
+bool
+NCPInstanceBase::lookup_address_for_prefix(struct in6_addr *address, const struct in6_addr &prefix, int prefix_len_in_bits)
+{
+	struct in6_addr masked_prefix(prefix);
+
+	in6_addr_apply_mask(masked_prefix, prefix_len_in_bits);
+
+	std::map<struct in6_addr, GlobalAddressEntry>::const_iterator iter;
+	for (iter = mGlobalAddresses.begin(); iter != mGlobalAddresses.end(); ++iter) {
+		struct in6_addr iter_prefix(iter->first);
+		in6_addr_apply_mask(iter_prefix, prefix_len_in_bits);
+
+		if (iter_prefix == masked_prefix) {
+			if (address != NULL) {
+				*address = iter->first;
+			}
+			return true;
+		}
+	}
+	return false;
+}
+
+void
+NCPInstanceBase::address_was_added(const struct in6_addr& addr, int prefix_len)
+{
+	char addr_cstr[INET6_ADDRSTRLEN] = "::";
+	inet_ntop(
+		AF_INET6,
+		&addr,
+		addr_cstr,
+		sizeof(addr_cstr)
+	);
+
+	syslog(LOG_NOTICE, "\"%s\" was added to \"%s\"", addr_cstr, mPrimaryInterface->get_interface_name().c_str());
+
+	if (mGlobalAddresses.count(addr) == 0) {
+		const GlobalAddressEntry entry = {
+			UINT32_MAX,          // .mValidLifetime
+			TIME_DISTANT_FUTURE, // .mValidLifetimeExpiration
+			UINT32_MAX,          // .mPreferredLifetime
+			TIME_DISTANT_FUTURE, // .mPreferredLifetimeExpiration
+			0,                   // .mFlags
+			1,                   // .mUserAdded
+		};
+
+		mGlobalAddresses[addr] = entry;
+	}
+}
+
+void
+NCPInstanceBase::address_was_removed(const struct in6_addr& addr, int prefix_len)
+{
+	char addr_cstr[INET6_ADDRSTRLEN] = "::";
+	inet_ntop(
+		AF_INET6,
+		&addr,
+		addr_cstr,
+		sizeof(addr_cstr)
+	);
+
+	if ((mGlobalAddresses.count(addr) != 0)
+	 && (mPrimaryInterface->is_online() || !mGlobalAddresses[addr].mUserAdded)
+	) {
+		mGlobalAddresses.erase(addr);
+	}
+
+	syslog(LOG_NOTICE, "\"%s\" was removed from \"%s\"", addr_cstr, mPrimaryInterface->get_interface_name().c_str());
+}
diff --git a/src/wpantund/NCPInstanceBase-AsyncIO.cpp b/src/wpantund/NCPInstanceBase-AsyncIO.cpp
new file mode 100644
index 0000000..5be8a0b
--- /dev/null
+++ b/src/wpantund/NCPInstanceBase-AsyncIO.cpp
@@ -0,0 +1,228 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "assert-macros.h"
+#include "NCPInstanceBase.h"
+#include "tunnel.h"
+#include <syslog.h>
+#include <errno.h>
+#include "nlpt.h"
+#include <algorithm>
+#include "socket-utils.h"
+#include "SuperSocket.h"
+
+using namespace nl;
+using namespace wpantund;
+
+bool
+NCPInstanceBase::can_set_ncp_power(void)
+{
+	return mPowerFD >= 0;
+}
+
+int
+NCPInstanceBase::set_ncp_power(bool power)
+{
+	ssize_t ret = -1;
+
+	if (mPowerFD >= 0) {
+		// Since controlling the power such a low-level
+		// operation, we break with our usual "no blocking calls"
+		// rule here for code clarity and to avoid
+		// unnecessary complexity.
+
+		IGNORE_RETURN_VALUE( lseek(mPowerFD, 0, SEEK_SET));
+
+		if (power) {
+			ret = write(mPowerFD, static_cast<const void*>(&mPowerFD_PowerOn), 1);
+		} else {
+			ret = write(mPowerFD, static_cast<const void*>(&mPowerFD_PowerOff), 1);
+		}
+
+		require_string(ret >= 0, bail, strerror(errno));
+
+		// We assign the return value of `write()` here
+		// to `ret` because we don't care if writing the `\n`
+		// fails. This happens when writing directly to GPIO
+		// files. We write the "\n" anyway to make it easier
+		// for non-GPIO sockets to parse.
+		IGNORE_RETURN_VALUE( write(mPowerFD, static_cast<const void*>("\n"), 1) );
+	}
+
+	if (ret > 0) {
+		ret = 0;
+	}
+
+bail:
+
+	return static_cast<int>(ret);
+}
+
+void
+NCPInstanceBase::hard_reset_ncp(void)
+{
+	NLPT_INIT(&mDriverToNCPPumpPT);
+	NLPT_INIT(&mNCPToDriverPumpPT);
+
+	if (mResetFD >= 0) {
+		// Since hardware resets are such a low-level
+		// operation, we break with our usual "no blocking calls"
+		// rule here for code clarity and to avoid
+		// unnecessary complexity.
+
+		ssize_t wret = -1;
+
+		IGNORE_RETURN_VALUE( lseek(mResetFD, 0, SEEK_SET) );
+
+		wret = write(mResetFD, static_cast<const void*>(&mResetFD_BeginReset), 1);
+
+		check_string(wret != -1, strerror(errno));
+
+		// We don't assign the return value of `write()` here
+		// to `ret` because we don't care if writing the `\n`
+		// fails. This happens when writing directly to GPIO
+		// files. We write the "\n" anyway to make it easier
+		// for non-GPIO sockets to parse.
+		IGNORE_RETURN_VALUE( write(mResetFD, static_cast<const void*>("\n"), 1) );
+
+		usleep(20 * USEC_PER_MSEC);
+
+		IGNORE_RETURN_VALUE( lseek(mResetFD, 0, SEEK_SET) );
+
+		wret = write(mResetFD, static_cast<const void*>(&mResetFD_EndReset), 1);
+
+		check_string(wret != -1, strerror(errno));
+
+		IGNORE_RETURN_VALUE( write(mResetFD, static_cast<const void*>("\n"), 1) );
+	} else {
+		mSerialAdapter->reset();
+	}
+}
+
+void
+NCPInstanceBase::set_socket_adapter(const boost::shared_ptr<SocketAdapter> &adapter)
+{
+	if(adapter) {
+		adapter->set_parent(mRawSerialAdapter);
+		mSerialAdapter = adapter;
+	} else {
+		mSerialAdapter = mRawSerialAdapter;
+	}
+}
+
+// ----------------------------------------------------------------------------
+// MARK: -
+
+cms_t
+NCPInstanceBase::get_ms_to_next_event(void)
+{
+	cms_t ret(EventHandler::get_ms_to_next_event());
+
+	mSerialAdapter->update_fd_set(NULL, NULL, NULL, NULL, &ret);
+	mPrimaryInterface->update_fd_set(NULL, NULL, NULL, NULL, &ret);
+	mFirmwareUpgrade.update_fd_set(NULL, NULL, NULL, NULL, &ret);
+
+	if (mWasBusy && (mLastChangedBusy != 0)) {
+		cms_t temp_cms(MAX_INSOMNIA_TIME_IN_MS - (time_ms() - mLastChangedBusy));
+		if (temp_cms < ret) {
+			ret = temp_cms;
+		}
+	}
+
+	if (ret < 0) {
+		ret = 0;
+	}
+
+	return ret;
+}
+
+int
+NCPInstanceBase::update_fd_set(fd_set *read_fd_set, fd_set *write_fd_set, fd_set *error_fd_set, int *max_fd, cms_t *timeout)
+{
+	int ret = -1;
+
+	if (timeout != NULL) {
+		*timeout = std::min(*timeout, get_ms_to_next_event());
+	}
+
+	ret = mFirmwareUpgrade.update_fd_set(read_fd_set, write_fd_set, error_fd_set, max_fd, timeout);
+
+	require_noerr(ret, bail);
+
+	if (!ncp_state_is_detached_from_ncp(get_ncp_state())) {
+		nlpt_select_update_fd_set(&mDriverToNCPPumpPT, read_fd_set, write_fd_set, error_fd_set, max_fd);
+		nlpt_select_update_fd_set(&mNCPToDriverPumpPT, read_fd_set, write_fd_set, error_fd_set, max_fd);
+
+		ret = mPrimaryInterface->update_fd_set(read_fd_set, write_fd_set, error_fd_set, max_fd, timeout);
+		require_noerr(ret, bail);
+
+		if (is_legacy_interface_enabled()) {
+			ret = mLegacyInterface->update_fd_set(read_fd_set, write_fd_set, error_fd_set, max_fd, timeout);
+			require_noerr(ret, bail);
+		}
+
+		ret = mSerialAdapter->update_fd_set(read_fd_set, write_fd_set, error_fd_set, max_fd, timeout);
+		require_noerr(ret, bail);
+	}
+
+bail:
+	return ret;
+}
+
+void
+NCPInstanceBase::process(void)
+{
+	int ret = 0;
+
+	mRunawayResetBackoffManager.update();
+
+	mFirmwareUpgrade.process();
+
+	if (get_upgrade_status() != EINPROGRESS) {
+		refresh_global_addresses();
+
+		require_noerr(ret = mPrimaryInterface->process(), socket_failure);
+
+		if (is_legacy_interface_enabled()) {
+			mLegacyInterface->process();
+		}
+
+		require_noerr(ret = mSerialAdapter->process(), socket_failure);
+
+		ncp_to_driver_pump();
+	}
+
+	EventHandler::process_event(EVENT_IDLE);
+
+	if (get_upgrade_status() != EINPROGRESS) {
+		driver_to_ncp_pump();
+	}
+
+    update_busy_indication();
+
+	return;
+
+socket_failure:
+	signal_fatal_error(ret);
+	return;
+}
diff --git a/src/wpantund/NCPInstanceBase-NetInterface.cpp b/src/wpantund/NCPInstanceBase-NetInterface.cpp
new file mode 100644
index 0000000..0a42eb6
--- /dev/null
+++ b/src/wpantund/NCPInstanceBase-NetInterface.cpp
@@ -0,0 +1,423 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "assert-macros.h"
+#include "NCPInstanceBase.h"
+#include "tunnel.h"
+#include <syslog.h>
+#include <errno.h>
+#include "nlpt.h"
+#include <algorithm>
+#include "socket-utils.h"
+#include "SuperSocket.h"
+
+using namespace nl;
+using namespace wpantund;
+
+
+int
+NCPInstanceBase::set_online(bool x)
+{
+	int ret;
+
+	ret = mPrimaryInterface->set_online(x);
+
+	restore_global_addresses();
+
+	if ((ret == 0) && static_cast<bool>(mLegacyInterface)) {
+		ret = mLegacyInterface->set_online(x && mNodeTypeSupportsLegacy);
+	}
+
+	return ret;
+}
+
+int
+NCPInstanceBase::set_hardware_address(const uint8_t x[8])
+{
+	int ret;
+
+	memcpy(mNCPHardwareAddress, x, sizeof(mNCPHardwareAddress));
+
+	syslog(
+		LOG_INFO,
+		"NCP Status: HardwareAddr:      %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",
+		mNCPHardwareAddress[0],mNCPHardwareAddress[1],mNCPHardwareAddress[2],mNCPHardwareAddress[3],
+		mNCPHardwareAddress[4],mNCPHardwareAddress[5],mNCPHardwareAddress[6],mNCPHardwareAddress[7]
+	);
+
+	if ((x[0] & 1) == 1) {
+		syslog(LOG_ERR,"HARDWARE ADDRESS IS INVALID, MULTICAST BIT IS SET!");
+	}
+
+	signal_property_changed(kWPANTUNDProperty_NCPHardwareAddress, Data(mNCPHardwareAddress, sizeof(mNCPHardwareAddress)));
+
+	ret = mPrimaryInterface->set_hardware_address(x);
+
+	if (static_cast<bool>(mLegacyInterface)) {
+		mLegacyInterface->set_hardware_address(x);
+	}
+
+	return ret;
+}
+
+void
+NCPInstanceBase::reset_interface(void)
+{
+	mPrimaryInterface->reset();
+
+	if (static_cast<bool>(mLegacyInterface)) {
+		mLegacyInterface->reset();
+	}
+}
+
+void
+NCPInstanceBase::enable_legacy_interface(void)
+{
+	if (!static_cast<bool>(mLegacyInterface)) {
+		mLegacyInterface = boost::shared_ptr<TunnelIPv6Interface>(new TunnelIPv6Interface(mPrimaryInterface->get_interface_name()+"-L"));
+		mLegacyInterface->set_hardware_address(mPrimaryInterface->get_hardware_address());
+	}
+}
+
+bool
+NCPInstanceBase::is_legacy_interface_enabled(void)
+{
+	if (mNodeTypeSupportsLegacy) {
+		return static_cast<bool>(mLegacyInterface);
+	}
+	return false;
+}
+
+
+
+int
+NCPInstanceBase::join_multicast_group(const std::string &group_name)
+{
+	int ret = -1;
+	struct ipv6_mreq imreq;
+	unsigned int value = 1;
+	struct hostent *tmp = gethostbyname2(group_name.c_str(), AF_INET6);
+	memset(&imreq, 0, sizeof(imreq));
+
+	if (mMCFD < 0) {
+		mMCFD = socket(AF_INET6, SOCK_DGRAM, 0);
+	}
+
+	require(mMCFD >= 0, skip_mcast);
+
+	require(!h_errno && tmp, skip_mcast);
+	require(tmp->h_length > 1, skip_mcast);
+
+	memcpy(&imreq.ipv6mr_multiaddr.s6_addr, tmp->h_addr_list[0], 16);
+
+	value = 1;
+	ret = setsockopt(
+		mMCFD,
+		IPPROTO_IPV6,
+		IPV6_MULTICAST_LOOP,
+		&value,
+		sizeof(value)
+	);
+	require_noerr(ret, skip_mcast);
+
+	imreq.ipv6mr_interface = if_nametoindex(mPrimaryInterface->get_interface_name().c_str());
+
+	ret = setsockopt(
+		mMCFD,
+		IPPROTO_IPV6,
+		IPV6_JOIN_GROUP,
+		&imreq,
+		sizeof(imreq)
+	);
+	require_noerr(ret, skip_mcast);
+
+skip_mcast:
+
+	if (ret) {
+		syslog(LOG_WARNING, "Failed to join multicast group \"%s\"", group_name.c_str());
+	}
+
+	return ret;
+}
+
+
+int
+NCPInstanceBase::set_commissioniner(
+    int seconds, uint8_t traffic_type, in_port_t traffic_port
+    )
+{
+	mCommissioningRule.clear();
+
+	if (seconds > 0 && traffic_port) {
+		mCommissioningExpiration = time_get_monotonic() + seconds;
+
+		mCommissioningRule.type = traffic_type;
+		mCommissioningRule.local_port = traffic_port;
+		mCommissioningRule.local_port_match = true;
+		mCommissioningRule.local_address.s6_addr[0] = 0xFE;
+		mCommissioningRule.local_address.s6_addr[1] = 0x80;
+		mCommissioningRule.local_match_mask = 10;
+	} else {
+		mCommissioningExpiration = 0;
+		mInsecureFirewall.clear();
+	}
+
+	return 0;
+}
+
+
+
+void
+NCPInstanceBase::handle_normal_ipv6_from_ncp(const uint8_t* ip_packet, size_t packet_length)
+{
+	if (mPrimaryInterface->write(ip_packet, packet_length) != packet_length) {
+		syslog(LOG_INFO, "[NCP->] IPv6 packet refused by host stack!");
+	}
+}
+
+void
+NCPInstanceBase::handle_alt_ipv6_from_ncp(const uint8_t* ip_packet, size_t packet_length)
+{
+	if (mLegacyInterface->write(ip_packet, packet_length)) {
+		syslog(LOG_INFO, "[NCP->] IPv6 packet refused by host stack!");
+	}
+}
+
+#define FRAME_TYPE_TO_CSTR(x) \
+			       (((x) == FRAME_TYPE_INSECURE_DATA) \
+					? "INSECURE" \
+					: ((x) == FRAME_TYPE_LEGACY_DATA) \
+						? "LEGACY" \
+						: "SECURE")
+
+// This function does a little more than it's name might imply.
+// It will also mutate the NCP frame to reflect the appropriate
+// interface if the firewall rules indicate that it should be
+// handled differently (i.e., a insecure packet that matches
+// the appropriate firewall rules will be re-tagged as a normal
+// packet, etc.)
+bool
+NCPInstanceBase::should_forward_hostbound_frame(uint8_t* type, const uint8_t* ip_packet, size_t packet_length)
+{
+	bool packet_should_be_dropped = false;
+	IPv6PacketMatcherRule rule;
+
+	rule.update_from_inbound_packet(ip_packet);
+
+	// Handle special considerations for packets received
+	// from the insecure data channel.
+	if (*type == FRAME_TYPE_INSECURE_DATA) {
+		// If the packet is from the insecure channel, we
+		// mark the packed as "to be dropped" by default.
+		// We perform some additional checks below which
+		// may switch this back to `false`.
+		packet_should_be_dropped = true;
+
+		if (ncp_state_is_joining(get_ncp_state())) {
+			// Don't drop data from insecure channel if we
+			// aren't joined yet.
+			packet_should_be_dropped = false;
+
+		} else if (mCommissioningExpiration != 0) {
+			if (mCommissioningExpiration > time_get_monotonic()) {
+				// We are in the middle of commissioning and we
+				// haven't expired yet.
+
+				if (mInsecureFirewall.count(rule)) {
+					syslog(LOG_INFO,
+						   "[NCP->] Routing insecure commissioning traffic.");
+					packet_should_be_dropped = false;
+				} else if (mCommissioningRule.match_inbound(ip_packet)) {
+					rule.subtype = IPv6PacketMatcherRule::SUBTYPE_ALL;
+					mInsecureFirewall.insert(rule);
+					packet_should_be_dropped = false;
+					syslog(LOG_INFO,
+						   "[NCP->] Tracking *NEW* insecure commissioning connection.");
+				} else if (rule.type == IPv6PacketMatcherRule::TYPE_ICMP) {
+					mInsecureFirewall.insert(rule);
+					packet_should_be_dropped = false;
+					syslog(LOG_INFO,
+						   "[NCP->] Tracking *NEW* ICMP ping during commissioning.");
+				} else {
+					syslog(LOG_INFO,
+						   "[NCP->] Non-matching insecure traffic while joinable, ignoring");
+				}
+			} else {
+				// Commissioning has ended. Clean up.
+				syslog(LOG_NOTICE, "Commissioning period has ended");
+				mCommissioningExpiration = 0;
+				mInsecureFirewall.clear();
+			}
+		}
+	} else if (
+		(   (*type == FRAME_TYPE_DATA)
+			|| (*type == FRAME_TYPE_LEGACY_DATA)
+		)
+		&&	(mInsecureFirewall.size()>0)
+	) {
+		// In this case we want to make sure that if we receive
+		// a packet on a secure channel that we were previously
+		// routing over the insecure channel that we remove the
+		// matching entry so that we don't route those packets
+		// over the insecure channel any more.
+
+		if (mInsecureFirewall.count(rule)) {
+			syslog(LOG_NOTICE, "Secure packet matched rule on insecure firewall, removing rule.");
+			mInsecureFirewall.erase(rule);
+
+			if (*type == FRAME_TYPE_LEGACY_DATA) {
+				// The first packet to match the rule on the insecure firewall
+				// was from the legacy interface. To ensure the continuity
+				// of the connection, we need to ensure that the packets
+				// for this session continue to come out of the non-legacy
+				// interface. We do that by adding them to this packet matcher.
+				mLegacyCommissioningMatcher.insert(rule);
+			}
+		}
+	}
+
+	// If our legacy interface isn't enabled, drop all legacy traffic.
+	if (*type == FRAME_TYPE_LEGACY_DATA) {
+		packet_should_be_dropped |= !is_legacy_interface_enabled();
+	}
+
+	// Debug logging.
+	dump_inbound_ipv6_packet(
+		ip_packet,
+		packet_length,
+		FRAME_TYPE_TO_CSTR(*type),
+		packet_should_be_dropped
+	);
+
+	// Make sure the interface is up.
+	if (!ncp_state_is_interface_up(get_ncp_state())) {
+		// The interface is down, so we don't send the packet.
+		packet_should_be_dropped = true;
+
+		// Check to see if the NCP is supposed to be asleep:
+		if (ncp_state_is_sleeping(get_ncp_state())) {
+			syslog(LOG_ERR, "Got IPv6 traffic when we should be asleep! (%s)",ncp_state_to_string(get_ncp_state()).c_str());
+			ncp_is_misbehaving();
+		} else {
+			syslog(LOG_WARNING, "Ignoring IPv6 traffic while in %s state.", ncp_state_to_string(get_ncp_state()).c_str());
+		}
+	}
+
+	if ((*type == FRAME_TYPE_LEGACY_DATA) && mLegacyCommissioningMatcher.count(rule)) {
+		// This is for ensuring that the commissioning TCP connection survives
+		// the transition to joining the network. In order for this to occur,
+		// we need to ensure that the packets for this particular connection
+		// continue to flow to and from the normal IPv6 data interface.
+		*type = FRAME_TYPE_DATA;
+	}
+
+	if (!packet_should_be_dropped) {
+		// Inform the statistic collector about the inbound IP packet
+		get_stat_collector().record_inbound_packet(ip_packet);
+	} else {
+		syslog(LOG_DEBUG, "Dropping host-bound IPv6 packet.");
+	}
+
+	return !packet_should_be_dropped;
+}
+
+bool
+NCPInstanceBase::should_forward_ncpbound_frame(uint8_t* type, const uint8_t* ip_packet, size_t packet_length)
+{
+	bool should_forward = true;
+	IPv6PacketMatcherRule rule;
+
+	if (!ncp_state_is_interface_up(get_ncp_state())) {
+		syslog(LOG_DEBUG, "Dropping IPv6 packet, NCP not ready yet!");
+		should_forward = false;
+		goto bail;
+	}
+
+	// Skip non-IPv6 packets
+	if (!is_valid_ipv6_packet(ip_packet, packet_length)) {
+		syslog(LOG_DEBUG,
+			   "Dropping non-IPv6 outbound packet (first byte was 0x%02X)",
+			   ip_packet[0]);
+		should_forward = false;
+		goto bail;
+	}
+
+	rule.update_from_outbound_packet(ip_packet);
+
+	if (mDropFirewall.match_outbound(ip_packet) != mDropFirewall.end()) {
+		syslog(LOG_INFO, "[->NCP] Dropping matched packet.");
+		should_forward = false;
+		goto bail;
+	}
+
+	if (mLegacyCommissioningMatcher.count(rule)) {
+		if (*type == FRAME_TYPE_LEGACY_DATA) {
+			// This is for ensuring that the commissioning TCP connection survives
+			// the transition to joining the network. In order for this to occur,
+			// we need to ensure that the packets for this particular connection
+			// continue to flow to and from the normal IPv6 data interface.
+			*type = FRAME_TYPE_DATA;
+		} else {
+			mLegacyCommissioningMatcher.erase(rule);
+		}
+	}
+
+	rule.subtype = IPv6PacketMatcherRule::SUBTYPE_ALL;
+
+	if (mInsecureFirewall.count(rule)) {
+		// We use `count` instead of `match_outbound` in the
+		// check above because exact matches are faster.
+		syslog(LOG_INFO, "[->NCP] Routing insecure commissioning traffic.");
+		*type = FRAME_TYPE_INSECURE_DATA;
+	}
+
+	if (ncp_state_is_joining(get_ncp_state())) {
+		// When we are joining, all outbound traffic is insecure.
+		*type = FRAME_TYPE_INSECURE_DATA;
+
+	} else if ((mCommissioningExpiration != 0)
+		&& (mCommissioningExpiration < time_get_monotonic())
+	) {
+		syslog(LOG_NOTICE, "Commissioning period has ended");
+		mCommissioningExpiration = 0;
+		mInsecureFirewall.clear();
+	}
+
+	// Inform the statistics collector about the outbound IPv6 packet
+	if (should_forward) {
+		get_stat_collector().record_outbound_packet(ip_packet);
+	} else {
+		syslog(LOG_DEBUG, "Dropping NCP-bound IPv6 packet.");
+	}
+
+	// Debug logging
+	dump_outbound_ipv6_packet(
+		ip_packet,
+		packet_length,
+		FRAME_TYPE_TO_CSTR(*type)
+	);
+
+bail:
+
+	return should_forward;
+}
diff --git a/src/wpantund/NCPInstanceBase.cpp b/src/wpantund/NCPInstanceBase.cpp
new file mode 100644
index 0000000..11b0a00
--- /dev/null
+++ b/src/wpantund/NCPInstanceBase.cpp
@@ -0,0 +1,824 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "assert-macros.h"
+#include "NCPInstanceBase.h"
+#include "tunnel.h"
+#include <syslog.h>
+#include <errno.h>
+#include "nlpt.h"
+#include <algorithm>
+#include "socket-utils.h"
+#include "SuperSocket.h"
+#include "wpantund.h"
+#include "any-to.h"
+
+using namespace nl;
+using namespace wpantund;
+
+
+
+// ----------------------------------------------------------------------------
+// MARK: -
+// MARK: Constructors/Destructors
+
+NCPInstanceBase::NCPInstanceBase(const Settings& settings):
+	mCommissioningRule(),
+	mCommissioningExpiration(0)
+{
+	std::string wpan_interface_name = "wpan0";
+
+	mResetFD = -1;
+	mResetFD_BeginReset = '0';
+	mResetFD_EndReset = '1';
+
+	mPowerFD = -1;
+	mPowerFD_PowerOff = '0';
+	mPowerFD_PowerOn = '1';
+
+	mMCFD = -1;
+
+	NLPT_INIT(&mNCPToDriverPumpPT);
+	NLPT_INIT(&mDriverToNCPPumpPT);
+
+	mCommissioningExpiration = 0;
+	mEnabled = true;
+	mTerminateOnFault = false;
+	mAutoUpdateFirmware = false;
+	mAutoResume = true;
+	mAutoDeepSleep = false;
+	mNCPState = UNINITIALIZED;
+	mNodeType = UNKNOWN;
+	mFailureCount = 0;
+	mFailureThreshold = 3;
+	mAutoDeepSleepTimeout = 10;
+
+	memset(mNCPMeshLocalAddress.s6_addr, 0, sizeof(mNCPMeshLocalAddress));
+	memset(mNCPLinkLocalAddress.s6_addr, 0, sizeof(mNCPLinkLocalAddress));
+	memset(mNCPV6LegacyPrefix, 0, sizeof(mNCPV6LegacyPrefix));
+	memset(mNCPHardwareAddress, 0, sizeof(mNCPHardwareAddress));
+
+	if (!settings.empty()) {
+		Settings::const_iterator iter;
+
+		for(iter = settings.begin(); iter != settings.end(); iter++) {
+			if (strcaseequal(iter->first.c_str(), kWPANTUNDProperty_ConfigNCPHardResetPath)) {
+				mResetFD = open_serial_socket(iter->second.c_str());
+
+			} else if (strcaseequal(iter->first.c_str(), kWPANTUNDProperty_ConfigNCPPowerPath)) {
+				mPowerFD = open_serial_socket(iter->second.c_str());
+
+			} else if (strcaseequal(iter->first.c_str(), kWPANTUNDProperty_ConfigNCPSocketPath)) {
+				mRawSerialAdapter = SuperSocket::create(iter->second);
+
+			} else if (strcaseequal(iter->first.c_str(), kWPANTUNDProperty_ConfigTUNInterfaceName)) {
+				wpan_interface_name = iter->second;
+
+			} else if (strcaseequal(iter->first.c_str(), kWPANTUNDProperty_ConfigNCPFirmwareCheckCommand)) {
+				mFirmwareUpgrade.set_firmware_check_command(iter->second);
+
+			} else if (strcaseequal(iter->first.c_str(), kWPANTUNDProperty_ConfigNCPFirmwareUpgradeCommand)) {
+				mFirmwareUpgrade.set_firmware_upgrade_command(iter->second);
+			}
+		}
+	}
+
+	if (!mRawSerialAdapter) {
+		syslog(LOG_WARNING, kWPANTUNDProperty_ConfigNCPSocketPath" was not specified. Using \"/dev/null\" instead.");
+		mRawSerialAdapter = SuperSocket::create("/dev/null");
+	}
+
+	if (mRawSerialAdapter) {
+		mRawSerialAdapter->set_log_level(LOG_DEBUG);
+	}
+
+	mSerialAdapter = mRawSerialAdapter;
+
+	mPrimaryInterface = boost::shared_ptr<TunnelIPv6Interface>(new TunnelIPv6Interface(wpan_interface_name));
+	mPrimaryInterface->mAddressWasAdded.connect(boost::bind(&NCPInstanceBase::address_was_added, this, _1, _2));
+	mPrimaryInterface->mAddressWasRemoved.connect(boost::bind(&NCPInstanceBase::address_was_removed, this, _1, _2));
+
+	set_ncp_power(true);
+
+	// Go ahead and start listening on ff03::1
+	join_multicast_group("ff03::1");
+
+
+	{
+		IPv6PacketMatcherRule rule;
+
+		// --------------------------------------------------------------------
+		// Packet Drop rules
+
+		rule.clear();
+		// OS X seems to generate these packets when bringing up the interface.
+		// Honey badger don't care.
+		rule.type = IPv6PacketMatcherRule::TYPE_HOP_BY_HOP;
+		rule.remote_address.s6_addr[0x0] = 0xFF;
+		rule.remote_address.s6_addr[0x1] = 0x02;
+		rule.remote_address.s6_addr[0xF] = 0x16;
+		rule.remote_match_mask = 128;
+		mDropFirewall.insert(rule);
+
+		rule.clear();
+		// Don't forward router advertisement or router solicitation
+		// traffic.
+		rule.type = IPv6PacketMatcherRule::TYPE_ICMP;
+		rule.remote_address.s6_addr[0x0] = 0xFF;
+		rule.remote_address.s6_addr[0x1] = 0x02;
+		rule.remote_address.s6_addr[0xF] = 0x02;
+		rule.remote_match_mask = 128;
+		rule.subtype = IPv6PacketMatcherRule::SUBTYPE_ICMP_ROUTER_ADV;
+		mDropFirewall.insert(rule);
+		rule.subtype = IPv6PacketMatcherRule::SUBTYPE_ICMP_ROUTER_SOL;
+		mDropFirewall.insert(rule);
+
+		rule.clear();
+		// Don't forward neighbor advertisement or neighbor solicitation
+		// traffic.
+		rule.type = IPv6PacketMatcherRule::TYPE_ICMP;
+		rule.subtype = IPv6PacketMatcherRule::SUBTYPE_ICMP_NEIGHBOR_ADV;
+		mDropFirewall.insert(rule);
+		rule.subtype = IPv6PacketMatcherRule::SUBTYPE_ICMP_NEIGHBOR_SOL;
+		mDropFirewall.insert(rule);
+	}
+}
+
+bool
+NCPInstanceBase::setup_property_supported_by_class(const std::string& prop_name)
+{
+	return strcaseequal(prop_name.c_str(), kWPANTUNDProperty_ConfigNCPHardResetPath)
+		|| strcaseequal(prop_name.c_str(), kWPANTUNDProperty_ConfigNCPPowerPath)
+		|| strcaseequal(prop_name.c_str(), kWPANTUNDProperty_ConfigNCPSocketPath)
+		|| strcaseequal(prop_name.c_str(), kWPANTUNDProperty_ConfigTUNInterfaceName)
+		|| strcaseequal(prop_name.c_str(), kWPANTUNDProperty_ConfigNCPDriverName)
+		|| strcaseequal(prop_name.c_str(), kWPANTUNDProperty_ConfigNCPFirmwareCheckCommand)
+		|| strcaseequal(prop_name.c_str(), kWPANTUNDProperty_ConfigNCPFirmwareUpgradeCommand);
+}
+
+NCPInstanceBase::~NCPInstanceBase()
+{
+	close(mMCFD);
+	close(mPowerFD);
+	close(mResetFD);
+}
+
+
+
+const std::string &
+NCPInstanceBase::get_name()
+{
+	return mPrimaryInterface->get_interface_name();
+}
+
+const WPAN::NetworkInstance&
+NCPInstanceBase::get_current_network_instance(void)const
+{
+	return mCurrentNetworkInstance;
+}
+
+// Helpful for use with callbacks.
+int
+NCPInstanceBase::process_event_helper(int event)
+{
+	return EventHandler::process_event(event);
+}
+
+// ----------------------------------------------------------------------------
+// MARK: -
+
+void
+NCPInstanceBase::set_ncp_version_string(const std::string& version_string)
+{
+	if (version_string != mNCPVersionString) {
+		if (!mNCPVersionString.empty()) {
+			// The previous version string isn't empty!
+			syslog(LOG_ERR, "Illegal NCP version change! (Previously \"%s\")", mNCPVersionString.c_str());
+			ncp_is_misbehaving();
+		} else {
+			mNCPVersionString = version_string;
+
+			syslog(LOG_NOTICE, "NCP is running \"%s\"", mNCPVersionString.c_str());
+			syslog(LOG_NOTICE, "Driver is running \"%s\"", nl::wpantund::get_wpantund_version_string().c_str());
+
+			if (mAutoUpdateFirmware && is_firmware_upgrade_required(version_string)) {
+				syslog(LOG_NOTICE, "NCP FIRMWARE UPGRADE IS REQUIRED");
+				upgrade_firmware();
+			}
+		}
+	}
+}
+
+std::set<std::string>
+NCPInstanceBase::get_supported_property_keys() const
+{
+	std::set<std::string> properties;
+
+	properties.insert(kWPANTUNDProperty_NetworkName);
+	properties.insert(kWPANTUNDProperty_NetworkPANID);
+	properties.insert(kWPANTUNDProperty_NetworkXPANID);
+	properties.insert(kWPANTUNDProperty_NetworkKey);
+	properties.insert(kWPANTUNDProperty_NetworkKeyIndex);
+	properties.insert(kWPANTUNDProperty_NetworkNodeType);
+
+	properties.insert(kWPANTUNDProperty_NCPState);
+
+	properties.insert(kWPANTUNDProperty_IPv6MeshLocalPrefix);
+	properties.insert(kWPANTUNDProperty_IPv6MeshLocalAddress);
+	properties.insert(kWPANTUNDProperty_IPv6LinkLocalAddress);
+	properties.insert(kWPANTUNDProperty_IPv6AllAddresses);
+
+	properties.insert(kWPANTUNDProperty_DaemonAutoAssociateAfterReset);
+	properties.insert(kWPANTUNDProperty_DaemonAutoDeepSleep);
+	properties.insert(kWPANTUNDProperty_DaemonEnabled);
+	properties.insert(kWPANTUNDProperty_DaemonReadyForHostSleep);
+	properties.insert(kWPANTUNDProperty_DaemonTerminateOnFault);
+
+	properties.insert(kWPANTUNDProperty_NestLabs_NetworkAllowingJoin);
+
+	properties.insert(kWPANTUNDProperty_DaemonVersion);
+	properties.insert(kWPANTUNDProperty_DaemonTerminateOnFault);
+
+	properties.insert(kWPANTUNDProperty_NCPChannel);
+	properties.insert(kWPANTUNDProperty_NCPVersion);
+	properties.insert(kWPANTUNDProperty_NCPHardwareAddress);
+	properties.insert(kWPANTUNDProperty_NCPCCAThreshold);
+	properties.insert(kWPANTUNDProperty_NCPTXPower);
+
+	properties.insert(kWPANTUNDProperty_NetworkIsCommissioned);
+
+	properties.insert(kWPANTUNDProperty_ConfigTUNInterfaceName);
+
+	if (mLegacyInterfaceEnabled || mNodeTypeSupportsLegacy || buffer_is_nonzero(mNCPV6LegacyPrefix, sizeof(mNCPV6LegacyPrefix))) {
+		properties.insert(kWPANTUNDProperty_NestLabs_LegacyMeshLocalAddress);
+		properties.insert(kWPANTUNDProperty_NestLabs_LegacyMeshLocalPrefix);
+	}
+
+	return properties;
+}
+
+void
+NCPInstanceBase::get_property(
+	const std::string& in_key,
+	CallbackWithStatusArg1 cb
+) {
+	std::string key = in_key;
+
+
+	if (key.empty()) {
+		/* This key is used to get the list of available properties */
+		cb(0, get_supported_property_keys());
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ConfigTUNInterfaceName)) {
+		cb(0, get_name());
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_DaemonEnabled)) {
+		cb(0, boost::any(mEnabled));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_DaemonReadyForHostSleep)) {
+		cb(0, boost::any(!is_busy()));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ConfigTUNInterfaceName)) {
+		cb(0, get_name());
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NCPVersion)) {
+		cb(0, boost::any(mNCPVersionString));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NetworkName)) {
+		cb(0, boost::any(get_current_network_instance().name));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NetworkIsCommissioned)) {
+		NCPState ncp_state = get_ncp_state();
+		if (ncp_state_is_commissioned(ncp_state)) {
+			cb(0, boost::any(true));
+		} else  if (ncp_state == OFFLINE || ncp_state == DEEP_SLEEP) {
+			cb(0, boost::any(false));
+		} else {
+			cb(kWPANTUNDStatus_TryAgainLater, boost::any(std::string("Unable to determine association state at this time")));
+		}
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NestLabs_LegacyEnabled)) {
+		cb(0, boost::any(mLegacyInterfaceEnabled));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NestLabs_NetworkAllowingJoin)) {
+		cb(0, boost::any(get_current_network_instance().joinable));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NetworkPANID)) {
+		cb(0, boost::any(get_current_network_instance().panid));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NetworkXPANID)) {
+		cb(0, boost::any(get_current_network_instance().get_xpanid_as_uint64()));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NCPChannel)) {
+		cb(0, boost::any((int)get_current_network_instance().channel));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_DaemonVersion)) {
+		cb(0, boost::any(nl::wpantund::get_wpantund_version_string()));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_DaemonAutoAssociateAfterReset)) {
+		cb(0, boost::any(static_cast<bool>(mAutoResume)));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_DaemonAutoDeepSleep)) {
+		cb(0, boost::any(mAutoDeepSleep));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_DaemonAutoFirmwareUpdate)) {
+		cb(0, boost::any(mAutoUpdateFirmware));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_DaemonTerminateOnFault)) {
+		cb(0, boost::any(mTerminateOnFault));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NCPHardwareAddress)) {
+		cb(0, boost::any(nl::Data(mNCPHardwareAddress, sizeof(mNCPHardwareAddress))));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_IPv6MeshLocalPrefix)) {
+		if (buffer_is_nonzero(mNCPV6Prefix, sizeof(mNCPV6Prefix))) {
+			struct in6_addr addr (mNCPMeshLocalAddress);
+			// Zero out the lower 64 bits.
+			memset(addr.s6_addr+8, 0, 8);
+			cb(0, boost::any(in6_addr_to_string(addr)+"/64"));
+		} else {
+			cb(kWPANTUNDStatus_FeatureNotSupported, std::string("Property is unavailable"));
+		}
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_IPv6MeshLocalAddress)) {
+		if (buffer_is_nonzero(mNCPMeshLocalAddress.s6_addr, sizeof(mNCPMeshLocalAddress))) {
+			cb(0, boost::any(in6_addr_to_string(mNCPMeshLocalAddress)));
+		} else {
+			cb(kWPANTUNDStatus_FeatureNotSupported, std::string("Property is unavailable"));
+		}
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_IPv6LinkLocalAddress)) {
+		if (buffer_is_nonzero(mNCPLinkLocalAddress.s6_addr, sizeof(mNCPLinkLocalAddress))) {
+			cb(0, boost::any(in6_addr_to_string(mNCPLinkLocalAddress)));
+		} else {
+			cb(kWPANTUNDStatus_FeatureNotSupported, std::string("Property is unavailable"));
+		}
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NestLabs_LegacyMeshLocalPrefix)) {
+		if (mLegacyInterfaceEnabled || mNodeTypeSupportsLegacy || buffer_is_nonzero(mNCPV6LegacyPrefix, sizeof(mNCPV6LegacyPrefix))) {
+			cb(0, boost::any(nl::Data(mNCPV6LegacyPrefix, sizeof(mNCPV6LegacyPrefix))));
+		} else {
+			cb(kWPANTUNDStatus_FeatureNotSupported, std::string("Property is unavailable"));
+		}
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NestLabs_LegacyMeshLocalAddress)) {
+		struct in6_addr legacy_addr;
+
+		if ((mLegacyInterfaceEnabled || mNodeTypeSupportsLegacy) && buffer_is_nonzero(mNCPV6LegacyPrefix, sizeof(mNCPV6LegacyPrefix))) {
+			memcpy(&legacy_addr, mNCPV6LegacyPrefix, sizeof(mNCPV6LegacyPrefix));
+			memcpy(&legacy_addr.s6_addr[8], mNCPHardwareAddress, sizeof(mNCPHardwareAddress));
+			legacy_addr.s6_addr[8] ^= 0x02; // Flip the private-use bit on the hardware address.
+			cb(0, boost::any(in6_addr_to_string(legacy_addr)));
+		} else {
+			cb(kWPANTUNDStatus_FeatureNotSupported, std::string("Property is unavailable"));
+		}
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NCPState)) {
+		cb(0, boost::any(ncp_state_to_string(get_ncp_state())));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NetworkNodeType)) {
+		cb(0, boost::any(node_type_to_string(mNodeType)));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_IPv6AllAddresses) || strcaseequal(key.c_str(), kWPANTUNDProperty_DebugIPv6GlobalIPAddressList))  {
+		std::list<std::string> result;
+		std::map<struct in6_addr, GlobalAddressEntry>::const_iterator it;
+		char address_string[INET6_ADDRSTRLEN];
+		for ( it = mGlobalAddresses.begin();
+			  it != mGlobalAddresses.end();
+			  it++ ) {
+			inet_ntop(AF_INET6,	&it->first,	address_string, sizeof(address_string));
+			result.push_back(std::string(address_string)+ "  " + it->second.get_description());
+		}
+		cb(0, boost::any(result));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_DaemonSyslogMask)) {
+		std::string mask_string;
+		int logmask;
+
+		setlogmask(logmask = setlogmask(0));
+
+		if (LOG_FAC(logmask) == LOG_DAEMON) {
+			mask_string += "daemon ";
+		}
+		if (LOG_FAC(logmask) == LOG_USER) {
+			mask_string += "user ";
+		}
+		if (logmask & LOG_MASK(LOG_EMERG)) {
+			mask_string += "emerg ";
+		}
+		if (logmask & LOG_MASK(LOG_ALERT)) {
+			mask_string += "alert ";
+		}
+		if (logmask & LOG_MASK(LOG_CRIT)) {
+			mask_string += "crit ";
+		}
+		if (logmask & LOG_MASK(LOG_ERR)) {
+			mask_string += "err ";
+		}
+		if (logmask & LOG_MASK(LOG_WARNING)) {
+			mask_string += "warning ";
+		}
+		if (logmask & LOG_MASK(LOG_NOTICE)) {
+			mask_string += "notice ";
+		}
+		if (logmask & LOG_MASK(LOG_INFO)) {
+			mask_string += "info ";
+		}
+		if (logmask & LOG_MASK(LOG_DEBUG)) {
+			mask_string += "debug ";
+		}
+
+		cb(0, mask_string);
+
+	} else if (StatCollector::is_a_stat_property(key)) {
+		get_stat_collector().get_property(key, cb);
+
+	} else {
+		cb(kWPANTUNDStatus_PropertyNotFound, boost::any(std::string("Property Not Found")));
+	}
+}
+
+void
+NCPInstanceBase::set_property(
+	const std::string& key,
+	const boost::any& value,
+	CallbackWithStatus cb
+) {
+	// If we are disabled, then the only property we
+	// are allowed to set is kWPANTUNDProperty_DaemonEnabled.
+	if (!mEnabled && !strcaseequal(key.c_str(), kWPANTUNDProperty_DaemonEnabled)) {
+		cb(kWPANTUNDStatus_InvalidWhenDisabled);
+		return;
+	}
+
+	try {
+		if (strcaseequal(key.c_str(), kWPANTUNDProperty_DaemonEnabled)) {
+			mEnabled = any_to_bool(value);
+			cb(0);
+
+		} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_DaemonAutoAssociateAfterReset)) {
+			mAutoResume = any_to_bool(value);
+			cb(0);
+
+		} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_DaemonAutoFirmwareUpdate)) {
+			bool value_bool = any_to_bool(value);
+
+			if (value_bool && !mAutoUpdateFirmware) {
+				if (get_ncp_state() == FAULT) {
+					syslog(LOG_ALERT, "The NCP is misbehaving: Attempting a firmware update");
+					upgrade_firmware();
+				} else if (get_ncp_state() != UNINITIALIZED) {
+					if (is_firmware_upgrade_required(mNCPVersionString)) {
+						syslog(LOG_NOTICE, "NCP FIRMWARE UPGRADE IS REQUIRED");
+						upgrade_firmware();
+					}
+				}
+			}
+
+			mAutoUpdateFirmware = value_bool;
+
+			cb(0);
+
+		} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_DaemonTerminateOnFault)) {
+			mTerminateOnFault = any_to_bool(value);
+			cb(0);
+			if (mTerminateOnFault && (get_ncp_state() == FAULT)) {
+				reinitialize_ncp();
+			}
+
+		} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_IPv6MeshLocalPrefix) || strcaseequal(key.c_str(), kWPANTUNDProperty_IPv6MeshLocalAddress)) {
+			if (get_ncp_state() <= OFFLINE) {
+				nl::Data prefix;
+
+				if (value.type() == typeid(std::string)) {
+					uint8_t ula_bytes[16] = {};
+					const std::string ip_string(any_to_string(value));
+
+					// Address-style
+					int bits = inet_pton(AF_INET6,ip_string.c_str(),ula_bytes);
+					if (bits <= 0) {
+						// Prefix is the wrong length.
+						cb(kWPANTUNDStatus_InvalidArgument);
+						return;
+					}
+
+					prefix = nl::Data(ula_bytes, 8);
+				} else {
+					prefix = any_to_data(value);
+				}
+
+				if (prefix.size() < sizeof(mNCPV6Prefix)) {
+					// Prefix is the wrong length.
+					cb(kWPANTUNDStatus_InvalidArgument);
+				}
+				memcpy(mNCPV6Prefix, prefix.data(), sizeof(mNCPV6Prefix));
+				cb(0);
+			} else {
+				cb(kWPANTUNDStatus_InvalidForCurrentState);
+			}
+
+		} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_DaemonAutoDeepSleep)) {
+			mAutoDeepSleep = any_to_bool(value);
+
+			if (mAutoDeepSleep == false
+			    && mNCPState == DEEP_SLEEP
+				&& mEnabled
+			) {
+				// Wake us up if we are asleep and deep sleep was turned off.
+				get_control_interface().refresh_state(boost::bind(cb,0));
+			} else {
+				cb(0);
+			}
+
+		} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_DaemonSyslogMask)) {
+			setlogmask(strtologmask(any_to_string(value).c_str(), setlogmask(0)));
+			cb(0);
+
+		} else if (StatCollector::is_a_stat_property(key)) {
+			get_stat_collector().set_property(key, value, cb);
+
+		} else {
+			syslog(LOG_ERR,"set_property: Unsupported property \"%s\"", key.c_str());
+			cb(kWPANTUNDStatus_PropertyNotFound);
+		}
+
+	} catch (const boost::bad_any_cast &x) {
+		// We will get a bad_any_cast exception if the property is of
+		// the wrong type.
+		syslog(LOG_ERR,"set_property: Bad type for property \"%s\" (%s)", key.c_str(), x.what());
+		cb(kWPANTUNDStatus_InvalidArgument);
+
+	} catch (const std::invalid_argument &x) {
+		// We will get a bad_any_cast exception if the property is of
+		// the wrong type.
+		syslog(LOG_ERR,"set_property: Invalid argument for property \"%s\" (%s)", key.c_str(), x.what());
+		cb(kWPANTUNDStatus_InvalidArgument);
+	}
+}
+
+void
+NCPInstanceBase::signal_property_changed(
+	const std::string& key,
+	const boost::any& value
+) {
+	get_control_interface().mOnPropertyChanged(key, value);
+}
+
+// ----------------------------------------------------------------------------
+// MARK: -
+
+NCPState
+NCPInstanceBase::get_ncp_state()const
+{
+	return mNCPState;
+}
+
+void
+NCPInstanceBase::change_ncp_state(NCPState new_ncp_state)
+{
+	NCPState old_ncp_state = mNCPState;
+	mNCPState = new_ncp_state;
+
+	if (old_ncp_state != mNCPState) {
+		syslog(
+			LOG_NOTICE,
+		   "State change: \"%s\" -> \"%s\"",
+		   ncp_state_to_string(old_ncp_state).c_str(),
+		   ncp_state_to_string(mNCPState).c_str()
+		);
+
+		// Sanity checks for invalid state transitions.
+		__ASSERT_MACROS_check(!(ncp_state_is_commissioned(old_ncp_state) && ncp_state_is_joining(mNCPState)));
+
+		handle_ncp_state_change(new_ncp_state, old_ncp_state);
+	}
+}
+
+void
+NCPInstanceBase::handle_ncp_state_change(NCPState new_ncp_state, NCPState old_ncp_state)
+{
+	// Detached NCP -> Online NCP
+	if (ncp_state_is_detached_from_ncp(old_ncp_state)
+	 && !ncp_state_is_detached_from_ncp(new_ncp_state)
+	) {
+		__ASSERT_MACROS_check(new_ncp_state == UNINITIALIZED);
+
+		// We are transitioning out of a state where we are disconnected
+		// from the NCP. This requires a hard reset.
+		set_ncp_power(true);
+		hard_reset_ncp();
+		PT_INIT(&mControlPT);
+	}
+
+	// Online NCP -> Detached NCP
+	else if (!ncp_state_is_detached_from_ncp(old_ncp_state)
+	 && ncp_state_is_detached_from_ncp(new_ncp_state)
+	) {
+		// We are transitioning into a state where we need to be disconnected
+		// from the NCP. For this we use the hibernate command.
+		mSerialAdapter->hibernate();
+		PT_INIT(&mControlPT);
+		mFailureCount = 0;
+
+		if (new_ncp_state == FAULT) {
+			// When we enter the fault state, attempt to
+			// ensure that we are using as little power as
+			// possible by physically turning off the NCP
+			// (if a method of doing so has been specified
+			// in our configuration)
+			set_ncp_power(false);
+
+			if (mTerminateOnFault) {
+				signal_fatal_error(kWPANTUNDStatus_Failure);
+			}
+		}
+		return;
+	}
+
+	// Interface Down -> Interface Up
+	if (!ncp_state_is_interface_up(old_ncp_state)
+	 && ncp_state_is_interface_up(new_ncp_state)
+	) {
+		set_online(true);
+
+
+	// InterfaceUp -> COMMISSIONED
+	// (Special case of InterfaceUp -> InterfaceDown)
+	} else if (ncp_state_is_interface_up(old_ncp_state)
+				&& (new_ncp_state == COMMISSIONED)
+				&& mAutoResume
+	) {
+		// We don't bother going further if autoresume is on.
+		return;
+
+
+	// Commissioned -> InterfaceDown
+	// (Special case of InterfaceUp -> InterfaceDown)
+	} else if (ncp_state_is_commissioned(old_ncp_state)
+		&& !ncp_state_is_commissioned(new_ncp_state)
+		&& !ncp_state_is_sleeping(new_ncp_state)
+	) {
+		syslog(LOG_NOTICE, "Resetting interface(s). . .");
+		mCurrentNetworkInstance.joinable = false;
+		set_commissioniner(0, 0, 0);
+		reset_interface();
+
+
+	// InterfaceUp -> InterfaceDown (General Case)
+	} else if (ncp_state_is_interface_up(old_ncp_state)
+		&& !ncp_state_is_interface_up(new_ncp_state)
+		&& new_ncp_state != NET_WAKE_WAKING
+	) {
+		// Take the interface offline.
+		syslog(LOG_NOTICE, "Taking interface(s) down. . .");
+
+		mCurrentNetworkInstance.joinable = false;
+		set_commissioniner(0, 0, 0);
+		set_online(false);
+	}
+
+
+	// We don't announce transitions to the "UNITIALIZED" state.
+	if (UNINITIALIZED != new_ncp_state) {
+		signal_property_changed(kWPANTUNDProperty_NCPState, ncp_state_to_string(new_ncp_state));
+	}
+}
+
+void
+NCPInstanceBase::reinitialize_ncp(void)
+{
+	PT_INIT(&mControlPT);
+	change_ncp_state(UNINITIALIZED);
+}
+
+void
+NCPInstanceBase::reset_tasks(wpantund_status_t status)
+{
+}
+
+
+// ----------------------------------------------------------------------------
+// MARK: -
+
+
+bool
+NCPInstanceBase::is_busy(void)
+{
+	const NCPState ncp_state = get_ncp_state();
+	bool is_busy = ncp_state_is_busy(ncp_state);
+
+	if (ncp_state == FAULT) {
+		return false;
+	}
+
+	if (get_upgrade_status() == EINPROGRESS) {
+		is_busy = true;
+	}
+
+	return is_busy;
+}
+
+StatCollector&
+NCPInstanceBase::get_stat_collector(void)
+{
+	return mStatCollector;
+}
+
+void
+NCPInstanceBase::update_busy_indication(void)
+{
+    cms_t current_time = time_ms();
+
+	if (mWasBusy != is_busy()) {
+		if (!mWasBusy
+			|| (mLastChangedBusy == 0)
+			|| (current_time - mLastChangedBusy >= BUSY_DEBOUNCE_TIME_IN_MS)
+			|| (current_time - mLastChangedBusy < 0)
+		) {
+			mWasBusy = !mWasBusy;
+			if(!mWasBusy) {
+				if (mLastChangedBusy == 0) {
+					syslog(LOG_INFO, "NCP is no longer busy, host sleep is permitted.");
+				} else {
+					syslog(LOG_INFO, "NCP is no longer busy, host sleep is permitted. (Was busy for %dms)",(int)(current_time - mLastChangedBusy));
+				}
+				signal_property_changed(kWPANTUNDProperty_DaemonReadyForHostSleep, true);
+			} else {
+				syslog(LOG_INFO, "NCP is now BUSY.");
+				signal_property_changed(kWPANTUNDProperty_DaemonReadyForHostSleep, false);
+			}
+			mLastChangedBusy = current_time;
+		}
+	} else if (mWasBusy
+		&& (mLastChangedBusy != 0)
+		&& (current_time - mLastChangedBusy > MAX_INSOMNIA_TIME_IN_MS)
+	) {
+		syslog(LOG_ERR, "Experiencing extended insomnia. Resetting internal state.");
+
+		mLastChangedBusy = current_time;
+
+		ncp_is_misbehaving();
+	}
+}
+
+void
+NCPInstanceBase::ncp_is_misbehaving()
+{
+	mFailureCount++;
+	hard_reset_ncp();
+	reinitialize_ncp();
+	reset_tasks();
+
+	if (mFailureCount >= mFailureThreshold) {
+		change_ncp_state(FAULT);
+	}
+}
+
+// ----------------------------------------------------------------------------
+// MARK: -
+
+bool
+NCPInstanceBase::is_firmware_upgrade_required(const std::string& version)
+{
+	return mFirmwareUpgrade.is_firmware_upgrade_required(version);
+}
+
+void
+NCPInstanceBase::upgrade_firmware(void)
+{
+	change_ncp_state(UPGRADING);
+
+	set_ncp_power(true);
+
+	return mFirmwareUpgrade.upgrade_firmware();
+}
+
+int
+NCPInstanceBase::get_upgrade_status(void)
+{
+	return mFirmwareUpgrade.get_upgrade_status();
+}
+
+bool
+NCPInstanceBase::can_upgrade_firmware(void)
+{
+	return mFirmwareUpgrade.can_upgrade_firmware();
+}
diff --git a/src/wpantund/NCPInstanceBase.h b/src/wpantund/NCPInstanceBase.h
new file mode 100644
index 0000000..9dff629
--- /dev/null
+++ b/src/wpantund/NCPInstanceBase.h
@@ -0,0 +1,329 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __wpantund__NCPInstanceBase__
+#define __wpantund__NCPInstanceBase__
+
+#include "NCPInstance.h"
+#include <map>
+#include <string>
+#include "FirmwareUpgrade.h"
+#include "EventHandler.h"
+#include "NCPTypes.h"
+#include "StatCollector.h"
+#include "RunawayResetBackoffManager.h"
+
+namespace nl {
+namespace wpantund {
+
+class NCPInstanceBase : public NCPInstance, public EventHandler{
+public:
+
+	enum {
+		FRAME_TYPE_DATA = 2,
+		FRAME_TYPE_INSECURE_DATA = 3,
+		FRAME_TYPE_LEGACY_DATA = 4
+	};
+
+protected:
+	NCPInstanceBase(const Settings& settings = Settings());
+
+public:
+	virtual ~NCPInstanceBase();
+
+	virtual const std::string &get_name();
+
+	virtual void set_socket_adapter(const boost::shared_ptr<SocketAdapter> &adapter);
+
+public:
+	// ========================================================================
+	// Static Functions
+
+	static bool setup_property_supported_by_class(const std::string& prop_name);
+
+
+public:
+	// ========================================================================
+	// MARK: ASync I/O
+
+	virtual cms_t get_ms_to_next_event(void);
+
+	virtual int update_fd_set(
+		fd_set *read_fd_set,
+		fd_set *write_fd_set,
+		fd_set *error_fd_set,
+		int *max_fd,
+		cms_t *timeout
+	);
+
+	virtual void process(void);
+
+	// Helpful for use with callbacks.
+	int process_event_helper(int event);
+
+	virtual StatCollector& get_stat_collector(void);
+
+protected:
+	virtual char ncp_to_driver_pump() = 0;
+	virtual char driver_to_ncp_pump() = 0;
+
+public:
+	// ========================================================================
+	// MARK: NCP Behavior
+
+	virtual void hard_reset_ncp(void);
+
+	virtual int set_ncp_power(bool power);
+
+	virtual bool can_set_ncp_power(void);
+
+public:
+	// ========================================================================
+	// MARK: Other
+
+	virtual void reinitialize_ncp(void);
+
+	virtual void reset_tasks(wpantund_status_t status = kWPANTUNDStatus_Canceled);
+
+	NCPState get_ncp_state()const;
+
+	//! Handles transitioning from state-to-state.
+	/*! This is the ONLY WAY to change mNCPState. */
+	void change_ncp_state(NCPState new_ncp_state);
+
+	virtual void handle_ncp_state_change(NCPState new_ncp_state, NCPState old_ncp_state);
+
+	virtual void ncp_is_misbehaving();
+
+public:
+	// ========================================================================
+	// MARK: Network Interface Methods
+
+	int set_online(bool x);
+
+	int set_hardware_address(const uint8_t addr[8]);
+
+	void reset_interface(void);
+
+	const WPAN::NetworkInstance& get_current_network_instance(void)const;
+
+public:
+	// ========================================================================
+	// MARK: Global Address Management
+
+	void update_global_address(const struct in6_addr &address, uint32_t valid_lifetime, uint32_t preferred_lifetime, uint8_t flags);
+
+	void refresh_global_addresses();
+
+	//! Removes all nonpermanent global address entries
+	void clear_nonpermanent_global_addresses();
+
+	void restore_global_addresses();
+
+	bool is_address_known(const struct in6_addr &address);
+
+	bool lookup_address_for_prefix(struct in6_addr *address, const struct in6_addr &prefix, int prefix_len_in_bits = 64);
+
+	int join_multicast_group(const std::string &group_name);
+
+public:
+	// ========================================================================
+	// MARK: Subclass Hooks
+
+	virtual void address_was_added(const struct in6_addr& addr, int prefix_len);
+
+	virtual void address_was_removed(const struct in6_addr& addr, int prefix_len);
+
+public:
+	// ========================================================================
+	// MARK: Firware Upgrade
+
+	virtual bool is_firmware_upgrade_required(const std::string& version);
+
+	virtual void upgrade_firmware(void);
+
+	virtual int get_upgrade_status(void);
+
+	virtual bool can_upgrade_firmware(void);
+
+public:
+	// ========================================================================
+	// MARK: Busy/OkToSleep
+
+	virtual bool is_busy(void);
+
+	virtual void update_busy_indication(void);
+
+public:
+	// ========================================================================
+	// MARK: IPv6 data path helpers
+
+	bool should_forward_hostbound_frame(uint8_t* type, const uint8_t* packet, size_t packet_length);
+
+	bool should_forward_ncpbound_frame(uint8_t* type, const uint8_t* packet, size_t packet_length);
+
+	void handle_normal_ipv6_from_ncp(const uint8_t* packet, size_t packet_length);
+
+	int set_commissioniner(int seconds, uint8_t traffic_type, in_port_t traffic_port);
+
+public:
+	// ========================================================================
+	// MARK: Legacy Interface Methods
+
+	void enable_legacy_interface(void);
+
+	bool is_legacy_interface_enabled(void);
+
+	void handle_alt_ipv6_from_ncp(const uint8_t* packet, size_t packet_length);
+
+public:
+
+	virtual std::set<std::string> get_supported_property_keys()const;
+
+	virtual void get_property(
+	    const std::string& key,
+	    CallbackWithStatusArg1 cb
+	);
+
+	virtual void set_property(
+	    const std::string& key,
+	    const boost::any& value,
+	    CallbackWithStatus cb
+	);
+
+	virtual void signal_property_changed(
+	    const std::string& key,
+	    const boost::any& value = boost::any()
+	);
+
+	void set_ncp_version_string(const std::string& version_string);
+
+protected:
+	// ========================================================================
+	// MARK: Protected Data
+
+	boost::shared_ptr<TunnelIPv6Interface> mPrimaryInterface;
+
+	boost::shared_ptr<SocketWrapper> mRawSerialAdapter;
+	boost::shared_ptr<SocketWrapper> mSerialAdapter;
+
+	struct nlpt mNCPToDriverPumpPT;
+	struct nlpt mDriverToNCPPumpPT;
+
+	std::map<struct in6_addr, GlobalAddressEntry> mGlobalAddresses;
+
+	IPv6PacketMatcherRule mCommissioningRule;
+	IPv6PacketMatcher mInsecureFirewall;
+	IPv6PacketMatcher mDropFirewall;
+
+	time_t mCommissioningExpiration;
+
+	std::string mNCPVersionString;
+
+	bool mEnabled;
+	bool mTerminateOnFault;
+	bool mAutoUpdateFirmware;
+	bool mAutoResume;
+	bool mAutoDeepSleep;
+	int mAutoDeepSleepTimeout; // In seconds
+
+private:
+	NCPState mNCPState;
+
+protected:
+	uint8_t mNCPHardwareAddress[8];
+	union {
+		uint8_t mNCPV6Prefix[8];
+		struct in6_addr mNCPMeshLocalAddress;
+	};
+	struct in6_addr mNCPLinkLocalAddress;
+
+	WPAN::NetworkInstance mCurrentNetworkInstance;
+
+	NodeType mNodeType;
+
+	int mFailureCount;
+	int mFailureThreshold;
+
+	RunawayResetBackoffManager mRunawayResetBackoffManager;
+
+protected:
+	// ========================================================================
+	// MARK: Legacy Interface Support
+
+	boost::shared_ptr<TunnelIPv6Interface> mLegacyInterface;
+	IPv6PacketMatcher mLegacyCommissioningMatcher;
+	uint8_t mNCPV6LegacyPrefix[8];
+	bool mLegacyInterfaceEnabled;
+	bool mNodeTypeSupportsLegacy;
+
+
+private:
+	// ========================================================================
+	// MARK: Private Data
+
+	int mResetFD; //!^ File descriptor for resetting NCP.
+	char mResetFD_BeginReset; //!^ Value for entering reset
+	char mResetFD_EndReset; //!^ Value for leaving reset
+
+	int mPowerFD; //!^ File descriptor for controlling NCP power.
+	char mPowerFD_PowerOn; //!^ Value for the power being on.
+	char mPowerFD_PowerOff; //!^ Value for the power being off.
+
+	int mMCFD; //!^ File descriptor for multicast stuff.
+
+	bool mWasBusy;
+	cms_t mLastChangedBusy;
+
+	FirmwareUpgrade mFirmwareUpgrade;
+
+	StatCollector mStatCollector;  // Statistic collector
+}; // class NCPInstance
+
+}; // namespace wpantund
+
+}; // namespace nl
+
+// This callback is not sent from the NCP. It is a fake NCP
+// callback sent from the processing thread to indicate that
+// the NCP is in deep sleep.
+#define EVENT_NCP_DISABLED                 0x78C9
+
+#define EVENT_NCP_CONN_RESET               0x78CB
+
+// Extracts a pointer and length from argument list and
+// returns a `nl::Data` object.
+static inline nl::Data
+va_arg_as_Data(va_list args)
+{
+	const uint8_t* data = NULL;
+	size_t data_len = 0;
+
+	data = va_arg(args, const uint8_t*);
+	data_len = va_arg(args, size_t);
+
+	// Sanity check
+	assert(data_len < 1024*1024);
+
+	return nl::Data(data, data_len);
+}
+
+#define va_arg_small(args, type)		static_cast<type>(va_arg(args, int))
+
+#endif /* defined(__wpantund__NCPInstanceBase__) */
diff --git a/src/wpantund/NCPMfgInterface.h b/src/wpantund/NCPMfgInterface.h
new file mode 100644
index 0000000..ec72674
--- /dev/null
+++ b/src/wpantund/NCPMfgInterface.h
@@ -0,0 +1,50 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef wpantund_NCPMfgInterface_h
+#define wpantund_NCPMfgInterface_h
+
+#include "NCPControlInterface.h"
+
+namespace nl {
+namespace wpantund {
+
+class NCPMfgInterface {
+public:
+	virtual void mfg_start(CallbackWithStatus cb) { }
+
+
+	virtual void mfg_finish(CallbackWithStatusArg1 cb) = 0;
+	virtual void mfg_begin_test(uint8_t type, CallbackWithStatus cb) = 0;
+	virtual void mfg_end_test(uint8_t type, CallbackWithStatus cb) = 0;
+	virtual void mfg_tx_packet(const Data& packet, int16_t repeat, CallbackWithStatus cb) = 0;
+
+	virtual void mfg_clockmon(bool enable, uint32_t timerId, CallbackWithStatus cb) = 0;
+	virtual void mfg_gpio_set(uint8_t port_pin, uint8_t config, uint8_t value, CallbackWithStatus cb) = 0;
+	virtual void mfg_gpio_get(uint8_t port_pin, CallbackWithStatusArg1 cb) = 0;
+	virtual void mfg_channelcal(uint8_t channel, uint32_t duration,  CallbackWithStatus cb) = 0;
+	virtual void mfg_channelcal_get(uint8_t channel, CallbackWithStatusArg1 cb) = 0;
+
+	boost::signals2::signal<void(Data, uint8_t, int8_t)> mOnMfgRXPacket;
+};
+
+}; // namespace wpantund
+}; // namespace nl
+
+#endif
diff --git a/src/wpantund/NCPTypes.cpp b/src/wpantund/NCPTypes.cpp
new file mode 100644
index 0000000..eeb1937
--- /dev/null
+++ b/src/wpantund/NCPTypes.cpp
@@ -0,0 +1,347 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "NCPTypes.h"
+#include "string-utils.h"
+#include <string>
+#include "wpan-properties.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+using namespace nl;
+using namespace wpantund;
+
+
+// ----------------------------------------------------------------------------
+// MARK: -
+// MARK: Static Methods
+
+bool
+nl::wpantund::ncp_state_is_sleeping(NCPState x)
+{
+	switch(x) {
+	case DEEP_SLEEP:
+	case NET_WAKE_ASLEEP:
+		return true;
+	default:
+		return false;
+	}
+}
+
+bool
+nl::wpantund::ncp_state_has_joined(NCPState x)
+{
+	switch(x) {
+	case ASSOCIATED:
+	case ISOLATED:
+	case NET_WAKE_ASLEEP:
+	case NET_WAKE_WAKING:
+		return true;
+	default:
+		return false;
+	}
+}
+
+bool
+nl::wpantund::ncp_state_is_joining(NCPState x)
+{
+	switch(x) {
+	case ASSOCIATING:
+	case CREDENTIALS_NEEDED:
+		return true;
+	default:
+		return false;
+	}
+}
+
+bool
+nl::wpantund::ncp_state_is_interface_up(NCPState x)
+{
+	switch(x) {
+	case CREDENTIALS_NEEDED:
+	case ASSOCIATED:
+	case NET_WAKE_ASLEEP:
+		return true;
+	default:
+		return false;
+	}
+}
+
+bool
+nl::wpantund::ncp_state_is_commissioned(NCPState x)
+{
+	switch(x) {
+	case COMMISSIONED:
+	case ASSOCIATED:
+	case NET_WAKE_ASLEEP:
+	case ISOLATED:
+	case NET_WAKE_WAKING:
+		return true;
+	default:
+		return false;
+	}
+}
+
+bool
+nl::wpantund::ncp_state_is_initializing(NCPState x)
+{
+	switch(x) {
+	case UNINITIALIZED:
+	case UPGRADING:
+		return true;
+	default:
+		return false;
+	}
+}
+
+bool
+nl::wpantund::ncp_state_is_joining_or_joined(NCPState x)
+{
+	switch(x) {
+	case CREDENTIALS_NEEDED:
+	case ASSOCIATING:
+	case ASSOCIATED:
+	case ISOLATED:
+	case NET_WAKE_WAKING:
+	case NET_WAKE_ASLEEP:
+		return true;
+	default:
+		return false;
+	}
+}
+
+bool
+nl::wpantund::ncp_state_is_associated(NCPState x)
+{
+	switch(x) {
+	case ASSOCIATED:
+	case ISOLATED:
+	case NET_WAKE_WAKING:
+	case NET_WAKE_ASLEEP:
+		return true;
+	default:
+		return false;
+	}
+}
+
+bool
+nl::wpantund::ncp_state_is_detached_from_ncp(NCPState x)
+{
+	switch(x) {
+	case FAULT:
+	case UPGRADING:
+		return true;
+	default:
+		return false;
+	}
+}
+
+bool
+nl::wpantund::ncp_state_is_busy(NCPState x)
+{
+	switch(x) {
+	case DEEP_SLEEP:
+	case OFFLINE:
+	case NET_WAKE_ASLEEP:
+	case ISOLATED:
+	case ASSOCIATED:
+	case FAULT:
+		return false;
+	default:
+		return true;
+	}
+}
+
+std::string
+nl::wpantund::ncp_state_to_string(NCPState state)
+{
+	switch (state) {
+	case UNINITIALIZED:      return kWPANTUNDStateUninitialized;
+	case FAULT:              return kWPANTUNDStateFault;
+	case UPGRADING:          return kWPANTUNDStateUpgrading;
+	case DEEP_SLEEP:         return kWPANTUNDStateDeepSleep;
+	case OFFLINE:            return kWPANTUNDStateOffline;
+	case COMMISSIONED:       return kWPANTUNDStateCommissioned;
+	case ASSOCIATING:        return kWPANTUNDStateAssociating;
+	case CREDENTIALS_NEEDED: return kWPANTUNDStateCredentialsNeeded;
+	case ASSOCIATED:         return kWPANTUNDStateAssociated;
+	case ISOLATED:           return kWPANTUNDStateIsolated;
+	case NET_WAKE_ASLEEP:    return kWPANTUNDStateNetWake_Asleep;
+	case NET_WAKE_WAKING:    return kWPANTUNDStateNetWake_Waking;
+	}
+	return std::string("unknown-state");
+}
+
+std::string
+nl::wpantund::node_type_to_string(NodeType node_type)
+{
+	std::string ret;
+
+	switch (node_type) {
+	case UNKNOWN:
+		ret = kWPANTUNDNodeType_Unknown;
+		break;
+	case END_DEVICE:
+		ret = kWPANTUNDNodeType_EndDevice;
+		break;
+	case SLEEPY_END_DEVICE:
+		ret = kWPANTUNDNodeType_SleepyEndDevice;
+		break;
+	case ROUTER:
+		ret = kWPANTUNDNodeType_Router;
+		break;
+	case LURKER:
+		ret = kWPANTUNDNodeType_NestLurker;
+		break;
+	case LEADER:
+		ret = kWPANTUNDNodeType_Leader;
+		break;
+	case COMMISSIONER:
+		ret = kWPANTUNDNodeType_Commissioner;
+		break;
+	default:
+		{
+			char cstr[16];
+			snprintf(cstr, sizeof(cstr), "(node-type-%u)", (unsigned int)node_type);
+			ret = cstr;
+		}
+		break;
+	}
+
+	return ret;
+}
+
+nl::wpantund::NodeType
+nl::wpantund::string_to_node_type(const std::string& node_type_string)
+{
+	if (strcaseequal(node_type_string.c_str(), kWPANTUNDNodeType_EndDevice) || (node_type_string == "3")) {
+		return END_DEVICE;
+	} else if (strcaseequal(node_type_string.c_str(), kWPANTUNDNodeType_SleepyEndDevice) || (node_type_string == "4")) {
+		return SLEEPY_END_DEVICE;
+	} else if (strcaseequal(node_type_string.c_str(), kWPANTUNDNodeType_Router) || (node_type_string == "2")) {
+		return ROUTER;
+	} else if (strcaseequal(node_type_string.c_str(), kWPANTUNDNodeType_Leader)) {
+		return LEADER;
+	} else if (strcaseequal(node_type_string.c_str(), kWPANTUNDNodeType_NestLurker) || (node_type_string == "6") || strcaseequal(node_type_string.c_str(), "lurker")) {
+		return LURKER;
+	}
+	return UNKNOWN;
+}
+
+
+std::string
+nl::wpantund::address_flags_to_string(uint8_t flags)
+{
+	std::string ret;
+	int i;
+	for (i = 7; i >=0 ; --i) {
+		uint8_t mask = (1<<i);
+		if (i == 3) {
+			ret += ' ';
+		}
+		if (!(mask & flags)) {
+			ret += '-';
+			continue;
+		}
+		switch (mask) {
+		case GA_AM_GATEWAY:			//0
+			ret += 'G';
+			break;
+		case GA_AM_DHCP_SERVER:		//1
+			ret += 'D';
+			break;
+		case GA_AM_SLAAC_SERVER:	//2
+			ret += 'S';
+			break;
+		case GA_DHCP:				// 3
+			ret += 'd';
+			break;
+		case GA_SLAAC:				// 4
+			ret += 's';
+			break;
+		case GA_CONFIGURED:			// 5
+			ret += 'C';
+			break;
+		case GA_REQUEST_SENT:		//6
+			ret += 'R';
+			break;
+		case GA_REQUEST_FAILED:		//7
+			ret += 'F';
+			break;
+		default:
+			ret += '0' + i;
+			break;
+		}
+	}
+	return ret;
+}
+
+NCPState
+nl::wpantund::string_to_ncp_state(const std::string& state_string)
+{
+	if (state_string == kWPANTUNDStateFault) {
+		return FAULT;
+	} else if (state_string == kWPANTUNDStateUpgrading) {
+		return UPGRADING;
+	} else if (state_string == kWPANTUNDStateDeepSleep) {
+		return DEEP_SLEEP;
+	} else if (state_string == kWPANTUNDStateCommissioned) {
+		return COMMISSIONED;
+	} else if (state_string == kWPANTUNDStateCredentialsNeeded) {
+		return CREDENTIALS_NEEDED;
+	} else if (state_string == kWPANTUNDStateIsolated) {
+		return ISOLATED;
+	} else if (state_string == kWPANTUNDStateNetWake_Asleep) {
+		return NET_WAKE_ASLEEP;
+	} else if (state_string == kWPANTUNDStateNetWake_Waking) {
+		return NET_WAKE_WAKING;
+	}
+
+	if (strnequal(state_string.c_str(), kWPANTUNDStateUninitialized, sizeof(kWPANTUNDStateUninitialized)-1)) {
+		return UNINITIALIZED;
+	} else if (strnequal(state_string.c_str(), kWPANTUNDStateOffline, sizeof(kWPANTUNDStateOffline)-1)) {
+		return OFFLINE;
+	} else if (strnequal(state_string.c_str(), kWPANTUNDStateAssociating, sizeof(kWPANTUNDStateAssociating)-1)) {
+		return ASSOCIATING;
+	} else if (strnequal(state_string.c_str(), kWPANTUNDStateAssociated, sizeof(kWPANTUNDStateAssociated)-1)) {
+		return ASSOCIATED;
+	}
+
+	// Unknown
+	return UNINITIALIZED;
+}
+
+std::string
+GlobalAddressEntry::get_description() const
+{
+	char c_string[200];
+
+	snprintf(c_string, sizeof(c_string), "valid: %u  preferred: %u  flags: %s (0x%02X)",
+				mValidLifetime,
+				mPreferredLifetime,
+				address_flags_to_string(mFlags).c_str(),
+				mFlags);
+
+	return std::string(c_string);
+}
diff --git a/src/wpantund/NCPTypes.h b/src/wpantund/NCPTypes.h
new file mode 100644
index 0000000..c07da3c
--- /dev/null
+++ b/src/wpantund/NCPTypes.h
@@ -0,0 +1,110 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __wpantund__NCPTypes__
+#define __wpantund__NCPTypes__
+
+#include <stdint.h>
+#include "time-utils.h"
+#include <string>
+
+namespace nl {
+namespace wpantund {
+
+enum NCPState {
+	UNINITIALIZED,
+	FAULT,
+	UPGRADING,
+	DEEP_SLEEP,
+	OFFLINE,
+	COMMISSIONED,
+	ASSOCIATING,
+	CREDENTIALS_NEEDED,
+	ASSOCIATED,
+	ISOLATED,
+	NET_WAKE_WAKING,
+	NET_WAKE_ASLEEP,
+};
+
+enum NodeType {
+	UNKNOWN,
+	ROUTER,
+	END_DEVICE,
+	SLEEPY_END_DEVICE,
+	COMMISSIONER,
+	LURKER,
+	LEADER,
+};
+
+enum GlobalAddressFlags {
+	GA_AM_GATEWAY      = 0x01,
+	GA_AM_DHCP_SERVER  = 0x02,
+	GA_AM_SLAAC_SERVER = 0x04,
+	GA_DHCP            = 0x08,
+	GA_SLAAC           = 0x10,
+	GA_CONFIGURED      = 0x20,
+	GA_REQUEST_SENT    = 0x40,
+	GA_REQUEST_FAILED  = 0x80,
+};
+
+struct GlobalAddressEntry {
+	uint32_t mValidLifetime;
+	time_t mValidLifetimeExpiration;
+	uint32_t mPreferredLifetime;
+	time_t mPreferredLifetimeExpiration;
+	uint8_t mFlags;
+	uint8_t mUserAdded:1;
+
+	std::string get_description() const;
+};
+
+std::string address_flags_to_string(uint8_t flags);
+
+bool ncp_state_is_sleeping(NCPState x);
+
+bool ncp_state_has_joined(NCPState x);
+
+bool ncp_state_is_joining(NCPState x);
+
+bool ncp_state_is_commissioned(NCPState x);
+
+bool ncp_state_is_busy(NCPState x);
+
+bool ncp_state_is_joining_or_joined(NCPState x);
+
+bool ncp_state_is_interface_up(NCPState x);
+
+bool ncp_state_is_detached_from_ncp(NCPState x);
+
+bool ncp_state_is_initializing(NCPState x);
+
+bool ncp_state_is_associated(NCPState x);
+
+std::string ncp_state_to_string(NCPState state);
+
+NCPState string_to_ncp_state(const std::string& state_string);
+
+std::string node_type_to_string(NodeType node_type);
+
+NodeType string_to_node_type(const std::string& node_type_string);
+
+}; // namespace wpantund
+}; // namespace nl
+
+#endif  // defined(__wpantund__NCPTypes__)
diff --git a/src/wpantund/NetworkInstance.h b/src/wpantund/NetworkInstance.h
new file mode 100644
index 0000000..4fae679
--- /dev/null
+++ b/src/wpantund/NetworkInstance.h
@@ -0,0 +1,165 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef wpantund_NetworkInstance_h
+#define wpantund_NetworkInstance_h
+
+#include <stdint.h>
+#include <string>
+#include <cstring>
+#include "string-utils.h"
+
+namespace nl {
+
+
+namespace wpantund {
+namespace WPAN {
+struct NetworkId {
+	std::string name;
+	uint8_t xpanid[8];
+
+	uint64_t
+	get_xpanid_as_uint64() const
+	{
+		union {
+			uint64_t ret;
+			uint8_t data[8];
+		};
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+		memcpyrev(data, xpanid, 8);
+#endif
+		return ret;
+	}
+
+	void
+	set_xpanid_as_uint64(const uint64_t &_xpanid)
+	{
+		union {
+			uint64_t val;
+			uint8_t data[8];
+		} x;
+		x.val = _xpanid;
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+		memcpyrev(xpanid, x.data, 8);
+#endif
+	}
+
+	NetworkId(const std::string& _name = "")
+		: name(_name)
+	{
+	}
+
+	NetworkId(
+	    const std::string& _name, const uint8_t _xpanid[8]
+	    )
+		: name(_name)
+	{
+		if (_xpanid) {
+			memcpy(xpanid, _xpanid, sizeof(xpanid));
+		} else {
+			memset(xpanid, 0, sizeof(xpanid));
+		}
+	}
+
+	NetworkId(
+	    const std::string& _name, const uint64_t &_xpanid
+	    )
+		: name(_name)
+	{
+		set_xpanid_as_uint64(_xpanid);
+	}
+
+	bool operator==(const NetworkId& rhs) const
+	{
+		return (name == rhs.name) &&
+		       (0 == memcmp(xpanid, rhs.xpanid, sizeof(xpanid)));
+	}
+};
+
+struct NetworkInstance : public NetworkId {
+	uint16_t panid;
+	uint8_t channel;
+	bool joinable;
+	int8_t rssi;
+	uint8_t lqi;
+	uint8_t type;
+	uint8_t hwaddr[8];
+	uint16_t saddr;
+
+public:
+	NetworkInstance(
+	    const std::string& _name = "",
+	    const uint8_t _xpanid[8] = NULL,
+	    uint16_t _panid = 0xFFFF, int _channel = 0,
+	    bool _joinable = false
+	    ) :
+		NetworkId(_name, _xpanid),
+		panid(_panid),
+		channel(_channel),
+		joinable(_joinable),
+		rssi(-128),
+		type(0),
+		hwaddr()
+	{
+	}
+	NetworkInstance(
+	    const std::string& _name,
+	    const uint64_t _xpanid,
+	    uint16_t _panid = 0xFFFF, int _channel = 0,
+	    bool _joinable = false
+	    ) :
+		NetworkId(_name, _xpanid),
+		panid(_panid),
+		channel(_channel),
+		joinable(_joinable),
+		rssi(-128),
+		type(0),
+		hwaddr()
+	{
+	}
+
+	bool operator==(const NetworkInstance& rhs) const
+	{
+		return NetworkId::operator==(rhs)
+		       && (panid == rhs.panid)
+		       && (channel == rhs.channel)
+		       && (type == rhs.type)
+		       && (0 == memcmp(hwaddr, rhs.hwaddr, 8));
+	}
+
+	bool operator!=(const NetworkInstance& rhs) const
+	{
+		return !(*this == rhs);
+	}
+
+	uint64_t get_hwaddr_as_uint64() const
+	{
+		union {
+			uint64_t ret;
+			uint8_t data[8];
+		};
+		memcpyrev(data, hwaddr, 8);
+		return ret;
+	}
+};
+}; // namespace WPAN
+}; // namespace wpantund
+}; // namespace nl
+
+#endif
diff --git a/src/wpantund/RunawayResetBackoffManager.cpp b/src/wpantund/RunawayResetBackoffManager.cpp
new file mode 100644
index 0000000..27d0981
--- /dev/null
+++ b/src/wpantund/RunawayResetBackoffManager.cpp
@@ -0,0 +1,70 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "RunawayResetBackoffManager.h"
+
+#include <cstring>
+#include <syslog.h>
+#include <sys/time.h>
+
+using namespace nl;
+using namespace nl::wpantund;
+
+// -----------------------------
+
+const int RunawayResetBackoffManager::kDecayPeriod = 15;
+const int RunawayResetBackoffManager::kBackoffThreshold = 4;
+
+RunawayResetBackoffManager::RunawayResetBackoffManager():
+	mWindowedResetCount(0),
+	mDecrementAt(0)
+{
+}
+
+float
+RunawayResetBackoffManager::delay_for_unexpected_reset(void)
+{
+	float ret = 0;
+	if (mWindowedResetCount > kBackoffThreshold) {
+		int count = mWindowedResetCount - kBackoffThreshold;
+		ret = (count * count) / 2.0f;
+		syslog(LOG_ERR, "RunawayResetBackoffManager: mWindowedResetCount = %d, will delay for %f seconds", mWindowedResetCount, ret);
+	}
+	return ret;
+}
+
+void
+RunawayResetBackoffManager::count_unexpected_reset(void)
+{
+	mWindowedResetCount++;
+	mDecrementAt = time_get_monotonic() + kDecayPeriod;
+}
+
+void
+RunawayResetBackoffManager::update(void)
+{
+	if ((mWindowedResetCount > 0) && (mDecrementAt < time_get_monotonic())) {
+		mWindowedResetCount--;
+		mDecrementAt = time_get_monotonic() + kDecayPeriod;
+	}
+}
diff --git a/src/wpantund/RunawayResetBackoffManager.h b/src/wpantund/RunawayResetBackoffManager.h
new file mode 100644
index 0000000..cec5739
--- /dev/null
+++ b/src/wpantund/RunawayResetBackoffManager.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __WPANTUND_RUNAWAY_RESET_BACKOFF_MANAGER_H__
+#define __WPANTUND_RUNAWAY_RESET_BACKOFF_MANAGER_H__ 1
+
+#include "time-utils.h"
+
+namespace nl {
+namespace wpantund {
+
+class RunawayResetBackoffManager {
+public:
+	RunawayResetBackoffManager();
+
+	//! Returns the number of seconds that we should sleep after the next reset.
+	float delay_for_unexpected_reset(void);
+
+	//! Called when an unexpected reset occurs.
+	void count_unexpected_reset(void);
+
+	//! Called for every main loop to update the windowed reset count.
+	void update(void);
+
+private:
+	static const int kDecayPeriod;
+	static const int kBackoffThreshold;
+
+	int mWindowedResetCount;
+	time_t mDecrementAt;
+};
+
+}; // namespace wpantund
+}; // namespace nl
+
+#endif // __WPANTUND_RUNAWAY_RESET_BACKOFF_MANAGER_H__
diff --git a/src/wpantund/StatCollector.cpp b/src/wpantund/StatCollector.cpp
new file mode 100644
index 0000000..4c33cd7
--- /dev/null
+++ b/src/wpantund/StatCollector.cpp
@@ -0,0 +1,1691 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      Statistics collector module.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <syslog.h>
+#include <arpa/inet.h>
+#include <iterator>
+#include "StatCollector.h"
+#include "any-to.h"
+#include "wpan-error.h"
+
+using namespace nl;
+using namespace wpantund;
+
+// Enable additional debug logs only in this module
+#define ENABLE_MODULE_DEBUG  0
+#if ENABLE_MODULE_DEBUG
+#define DEBUG_LOG(msg, ...)  syslog(LOG_INFO, msg , ##__VA_ARGS__)
+#else
+#define DEBUG_LOG(msg, ...)  do { } while(false)
+#endif
+
+
+// Number of history items to show for short version of stat "Stat:Short"
+#define STAT_COLLECTOR_SHORT_HISTORY_COUNT        10
+
+// Number of history items to show for short version of stat "Stat:LinkQuality:Short"
+#define STAT_COLLECTOR_LINK_STAT_HISTORY_SIZE     8
+
+// Log level for adding logs to syslog when user/application requests it
+#define STAT_COLLECTOR_LOG_LEVEL_USER_REQUEST     LOG_INFO
+
+// Default log level for short auto logs (periodic logging)
+#define STAT_COLLECTOR_AUTO_LOG_DEFAULT_LOG_LEVEL LOG_INFO
+
+// Default period (in min) for automatically logging stat info
+#define STAT_COLLECTOR_AUTO_LOG_PERIOD_IN_MIN     30     // 30 min
+
+// Maximum allowed period for auto log (value is in min)
+#define STAT_COLLECTOR_AUTO_LOG_MAX_PERIOD		  (60 * 24 * 7 * 2) // Two weeks
+
+// Default period (in min) to check the link quality to peers
+#define STAT_COLLECTOR_LINK_STAT_PERIOD_IN_MIN    10     // 10 min
+
+// Time stamp constants
+#define TIMESTAMP_ONE_SEC_IN_MS           ((int32_t)1000)
+#define TIMESTAMP_ONE_MIN_IN_MS           (TIMESTAMP_ONE_SEC_IN_MS * 60)
+#define TIMESTAMP_ONE_HOUR_IN_MS          (TIMESTAMP_ONE_MIN_IN_MS * 60)
+#define TIMESTAMP_ONE_DAY_IN_MS           (TIMESTAMP_ONE_HOUR_IN_MS * 24)
+#define TIMESTAMP_UNINITIALIZED_VALUE     0
+
+// IPv6 types
+#define IPV6_TYPE_UDP                     0x11
+#define IPV6_TYPE_TCP                     0x06
+#define IPV6_TYPE_ICMP                    0x3A
+#define IPV6_ICMP_TYPE_ECHO_REQUEST       128
+#define IPV6_ICMP_TYPE_ECHO_REPLY         129
+
+// IPv6 Header Offset
+#define IPV6_HEADER_VERSION_OFFSET        0
+#define IPV6_HEADER_PAYLOAD_LEN_OFFSET    4
+#define IPV6_HEADER_TYPE_OFFSET           6
+#define IPV6_HEADER_SRC_ADDRESS_OFFSET    8
+#define IPV6_HEADER_DST_ADDRESS_OFFSET    24
+#define IPV6_UDP_HEADER_SRC_PORT_OFFSET   40
+#define IPV6_UDP_HEADER_DST_PORT_OFFSET   42
+#define IPV6_ICMP_HEADER_CODE_OFFSET      40
+
+#define IPV6_GET_UINT16(pkt,idx)   ( (static_cast<uint16_t>(pkt[idx]) << 8) + static_cast<uint16_t>(pkt[idx + 1] << 0) )
+
+//===================================================================
+
+static std::string
+string_printf(const char *fmt, ...)
+{
+	va_list args;
+	char c_str_buf[512];
+	va_start(args, fmt);
+	vsnprintf(c_str_buf, sizeof(c_str_buf), fmt, args);
+	return std::string(c_str_buf);
+}
+
+static std::string
+log_level_to_string(int log_level)
+{
+	switch(log_level) {
+		case LOG_EMERG:   return "emerg";
+		case LOG_ALERT:   return "alert";
+		case LOG_CRIT:    return "crit";
+		case LOG_ERR:     return "err";
+		case LOG_WARNING: return "warning";
+		case LOG_NOTICE:  return "notice";
+		case LOG_INFO:    return "info";
+		case LOG_DEBUG:   return "debug";
+	}
+	return string_printf("unknown(%d)", log_level);
+}
+
+// Converts a string to a log level, returns -1 if not a valid log level
+static int
+log_level_from_string(const char *log_string)
+{
+	int log_level = -1;
+
+	if (strcaseequal(log_string, "emerg")) {
+		log_level = LOG_EMERG;
+	} else 	if (strcaseequal(log_string, "alert")) {
+		log_level = LOG_ALERT;
+	} else 	if (strcaseequal(log_string, "crit")) {
+		log_level = LOG_CRIT;
+	} else 	if (strcaseequal(log_string, "err") || (strcaseequal(log_string, "error"))) {
+		log_level = LOG_ERR;
+	} else 	if (strcaseequal(log_string, "warning")) {
+		log_level = LOG_WARNING;
+	} else 	if (strcaseequal(log_string, "notice")) {
+		log_level = LOG_NOTICE;
+	} else 	if (strcaseequal(log_string, "info")) {
+		log_level = LOG_INFO;
+	} else 	if (strcaseequal(log_string, "debug")) {
+		log_level = LOG_DEBUG;
+	}
+
+	return log_level;
+}
+
+//-------------------------------------------------------------------
+// IPAddress
+
+void
+StatCollector::IPAddress::read_from(const uint8_t *arr)
+{
+	memcpy(static_cast<void *>(mAddressBuffer), arr, sizeof(mAddressBuffer));
+}
+
+std::string
+StatCollector::IPAddress::to_string(void) const
+{
+	char address_string[INET6_ADDRSTRLEN] = "::";
+	inet_ntop(AF_INET6, static_cast<const void *>(mAddressBuffer), address_string, sizeof(address_string));
+	return std::string(address_string);
+}
+
+bool
+StatCollector::IPAddress::operator==(const IPAddress& lhs) const
+{
+	// Since IPv6 addresses typically start with same prefix, we intentionally
+	// start the comparison from the end of address buffer
+	for(int indx = sizeof(mAddressBuffer)/sizeof(mAddressBuffer[0]) - 1; indx ; indx--) {
+		if (mAddressBuffer[indx] != lhs.mAddressBuffer[indx]) {
+			return false;
+		}
+	}
+
+	return true;
+}
+
+bool
+StatCollector::IPAddress::operator<(const IPAddress& lhs) const
+{
+	// Since IPv6 addresses typically start with same prefix, we intentionally
+	// start the comparison from the end of address buffer
+
+	for(int indx = sizeof(mAddressBuffer)/sizeof(mAddressBuffer[0]) - 1; indx ; indx--) {
+		if (mAddressBuffer[indx] < lhs.mAddressBuffer[indx]) {
+			return true;
+		}
+
+		if (mAddressBuffer[indx] > lhs.mAddressBuffer[indx]) {
+			return false;
+		}
+	}
+
+	// If all is equal, then return false.
+	return false;
+}
+
+//-------------------------------------------------------------------
+// EUI64Address
+
+void
+StatCollector::EUI64Address::read_from(const uint8_t *arr)
+{
+	mAddress[0] = arr[3] + (arr[2] << 8) + (arr[1] << 16) + (arr[0] << 24);
+	mAddress[1] = arr[7] + (arr[6] << 8) + (arr[5] << 16) + (arr[4] << 24);
+}
+
+std::string
+StatCollector::EUI64Address::to_string(void) const
+{
+	return string_printf("%08X%08X", mAddress[0], mAddress[1]);
+}
+
+bool
+StatCollector::EUI64Address::operator==(const EUI64Address& lhs) const
+{
+	return (mAddress[1] == lhs.mAddress[1]) && (mAddress[0] == lhs.mAddress[0]);
+}
+
+bool
+StatCollector::EUI64Address::operator<(const EUI64Address& lhs) const
+{
+	if (mAddress[1] < lhs.mAddress[1]) {
+		return true;
+	}
+
+	if (mAddress[1] > lhs.mAddress[1]) {
+		return false;
+	}
+
+	if (mAddress[0] < lhs.mAddress[0]) {
+		return true;
+	}
+
+	return false;
+}
+
+//-------------------------------------------------------------------
+// TimeStamp
+
+StatCollector::TimeStamp::TimeStamp()
+{
+	mTime = TIMESTAMP_UNINITIALIZED_VALUE;
+}
+
+void
+StatCollector::TimeStamp::set_to_now(void)
+{
+	mTime = time_ms();
+
+	if (mTime == TIMESTAMP_UNINITIALIZED_VALUE) {
+		mTime--;
+	}
+}
+
+void
+StatCollector::TimeStamp::clear(void)
+{
+	mTime = TIMESTAMP_UNINITIALIZED_VALUE;
+}
+
+cms_t
+StatCollector::TimeStamp::get_ms_till_now(void) const
+{
+	return CMS_SINCE(mTime);
+}
+
+bool
+StatCollector::TimeStamp::is_expired(void) const
+{
+	if (mTime == TIMESTAMP_UNINITIALIZED_VALUE) {
+		return true;
+	}
+
+	return (get_ms_till_now() < 0);
+}
+
+bool
+StatCollector::TimeStamp::is_uninitialized(void) const
+{
+	return (mTime == TIMESTAMP_UNINITIALIZED_VALUE);
+}
+
+bool
+StatCollector::TimeStamp::operator==(const TimeStamp &lhs)
+{
+	return mTime == lhs.mTime;
+}
+
+bool
+StatCollector::TimeStamp::operator<(const TimeStamp &lhs)
+{
+	return (mTime - lhs.mTime) < 0;
+}
+
+std::string
+StatCollector::TimeStamp::to_string(void) const
+{
+	int32_t days, hours, minutes, seconds, milliseconds;
+	cms_t ms_till_now = get_ms_till_now();
+
+	if (mTime == TIMESTAMP_UNINITIALIZED_VALUE)
+		return std::string("never");
+
+	if (ms_till_now < 0)
+		return std::string("long time (>24.86 days) ago");
+
+	days = ms_till_now / TIMESTAMP_ONE_DAY_IN_MS;
+	ms_till_now %= TIMESTAMP_ONE_DAY_IN_MS;
+	hours = ms_till_now / TIMESTAMP_ONE_HOUR_IN_MS;
+	ms_till_now %= TIMESTAMP_ONE_HOUR_IN_MS;
+	minutes = ms_till_now / TIMESTAMP_ONE_MIN_IN_MS;
+	ms_till_now %= TIMESTAMP_ONE_MIN_IN_MS;
+	seconds = ms_till_now / TIMESTAMP_ONE_SEC_IN_MS;
+	milliseconds = ms_till_now % TIMESTAMP_ONE_SEC_IN_MS;
+
+	if (days != 0) {
+		return string_printf("%2d day%s %02d:%02d:%02d.%03d ago", days, (days > 1)? "s" : "",
+		         hours, minutes, seconds, milliseconds);
+	}
+
+	return string_printf("%02d:%02d:%02d.%03d ago", hours, minutes, seconds, milliseconds);
+}
+
+int32_t
+StatCollector::TimeStamp::time_difference_in_ms(TimeStamp t1, TimeStamp t2)
+{
+	return (t2.mTime - t1.mTime);
+}
+
+//-------------------------------------------------------------------
+// BytesTotal
+
+StatCollector::BytesTotal::BytesTotal()
+{
+	clear();
+}
+
+void
+StatCollector::BytesTotal::clear()
+{
+	mBytes = 0;
+	mKiloBytes = 0;
+}
+
+void
+StatCollector::BytesTotal::add(uint16_t count)
+{
+	uint32_t num_bytes;
+
+	num_bytes = count;
+	num_bytes += mBytes;
+
+	mKiloBytes += (num_bytes >> 10);  // divide by 1024.
+	num_bytes &= 1023;                // reminder
+	mBytes = static_cast<uint16_t>(num_bytes);
+}
+
+std::string
+StatCollector::BytesTotal::to_string() const
+{
+	if (mKiloBytes == 0) {
+		return string_printf("%d bytes", mBytes);
+	}
+
+	if (mBytes == 0) {
+		return string_printf("%d Kbytes", mKiloBytes);
+	}
+
+	return string_printf("%d Kbytes & %d bytes", mKiloBytes, mBytes);
+}
+
+//-------------------------------------------------------------------
+// PacketInfo
+
+bool
+StatCollector::PacketInfo::update_from_packet(const uint8_t *packet)
+{
+	bool ret = false;
+
+	// Check the version in IPv6 header
+	if ((packet[IPV6_HEADER_VERSION_OFFSET] & 0xF0) == 0x60)  {
+
+		mTimeStamp.set_to_now();
+
+		mPayloadLen = IPV6_GET_UINT16(packet, IPV6_HEADER_PAYLOAD_LEN_OFFSET);
+
+		mType = packet[IPV6_HEADER_TYPE_OFFSET];
+
+		mSrcAddress.read_from(&packet[IPV6_HEADER_SRC_ADDRESS_OFFSET]);
+		mDstAddress.read_from(&packet[IPV6_HEADER_DST_ADDRESS_OFFSET]);
+
+		if (mType == IPV6_TYPE_ICMP) {
+			mSubtype = packet[IPV6_ICMP_HEADER_CODE_OFFSET];
+		} else {
+			mSubtype = 0;
+		}
+
+		if ((mType == IPV6_TYPE_UDP) || (mType == IPV6_TYPE_TCP)) {
+			mSrcPort = IPV6_GET_UINT16(packet, IPV6_UDP_HEADER_SRC_PORT_OFFSET);
+			mDstPort = IPV6_GET_UINT16(packet, IPV6_UDP_HEADER_DST_PORT_OFFSET);
+		} else {
+			mSrcPort = 0;
+			mDstPort = 0;
+		}
+
+		ret = true;
+	}
+
+	return ret;
+}
+
+std::string
+StatCollector::PacketInfo::to_string(void) const
+{
+	std::string type_str;
+	bool has_port;
+
+	has_port = false;
+	switch(mType) {
+		case IPV6_TYPE_TCP:
+			type_str = "TCP";
+			has_port = true;
+			break;
+
+		case IPV6_TYPE_UDP:
+			type_str = "UDP";
+			has_port = true;
+			break;
+
+		case IPV6_TYPE_ICMP:
+			switch(mSubtype) {
+				case IPV6_ICMP_TYPE_ECHO_REPLY:
+					type_str = "ICMP6(echo reply)";
+					break;
+				case IPV6_ICMP_TYPE_ECHO_REQUEST:
+					type_str = "ICMP6(echo request)";
+					break;
+				default:
+					type_str = string_printf("ICMP6(code:%d)", mSubtype);
+					break;
+			}
+			break;
+
+		default:
+			type_str = string_printf("0x%02x", mType);
+			break;
+	}
+
+	if (has_port) {
+		return string_printf(
+		        "%s -> type:%s len:%d from:[%s]:%d to:[%s]:%d",
+		        mTimeStamp.to_string().c_str(),
+		        type_str.c_str(),
+		        mPayloadLen,
+		        mSrcAddress.to_string().c_str(), mSrcPort,
+		        mDstAddress.to_string().c_str(), mDstPort
+		);
+	}
+
+	return string_printf(
+	        "%s -> type:%s len:%d from:[%s] to:[%s]",
+	        mTimeStamp.to_string().c_str(),
+	        type_str.c_str(),
+	        mPayloadLen,
+	        mSrcAddress.to_string().c_str(),
+	        mDstAddress.to_string().c_str()
+	);
+}
+
+//-------------------------------------------------------------------
+// NcpStateInfo
+
+void
+StatCollector::NcpStateInfo::update(NCPState new_state)
+{
+	mTimeStamp.set_to_now();
+	mNcpState = new_state;
+}
+
+std::string
+StatCollector::NcpStateInfo::to_string(void) const
+{
+	return
+		string_printf("%s -> %s",
+			mTimeStamp.to_string().c_str(),
+			ncp_state_to_string(mNcpState).c_str()
+		);
+}
+
+bool
+StatCollector::NcpStateInfo::is_expired(void) const
+{
+	return mTimeStamp.is_expired();
+}
+
+//------------------------------------------------------------------
+// ReadyForHostSleepState
+
+void
+StatCollector::ReadyForHostSleepState::update_with_blocking_sleep_time(TimeStamp blocking_sleep_time)
+{
+	mStartBlockingHostSleepTime = blocking_sleep_time;
+	mReadyForHostSleepTime.set_to_now();
+}
+
+std::string
+StatCollector::ReadyForHostSleepState::to_string(void) const
+{
+	if (mReadyForHostSleepTime.is_uninitialized() || mStartBlockingHostSleepTime.is_uninitialized()) {
+		return "Uninitialized";
+	}
+
+	return mStartBlockingHostSleepTime.to_string() + string_printf(" host sleep was blocked for %d ms",
+		TimeStamp::time_difference_in_ms(mStartBlockingHostSleepTime, mReadyForHostSleepTime));
+}
+
+//-------------------------------------------------------------------
+// Node Stat
+
+StatCollector::NodeStat::NodeStat():
+	mNodeInfoPool(), mNodeInfoMap()
+{
+	return;
+}
+
+void
+StatCollector::NodeStat::clear()
+{
+	mNodeInfoMap.clear();
+	mNodeInfoPool.free_all();
+}
+
+StatCollector::NodeStat::NodeInfo *
+StatCollector::NodeStat::find_node_info(const IPAddress& address)
+{
+	std::map<IPAddress, NodeInfo*>::iterator it = mNodeInfoMap.find(address);
+
+	if (it == mNodeInfoMap.end())
+		return NULL;
+
+	return it->second;
+}
+
+StatCollector::NodeStat::NodeInfo *
+StatCollector::NodeStat::create_new_node_info(const IPAddress& address)
+{
+	NodeInfo *node_info_ptr = NULL;
+
+	do {
+		node_info_ptr = mNodeInfoPool.alloc();
+
+		// If we can not allocate a new node info (all objects in the pool are used),
+		// we will remove oldest node info and try again.
+		if (!node_info_ptr) {
+			remove_oldest_node_info();
+		}
+	} while (!node_info_ptr);
+
+	node_info_ptr->clear();
+
+	mNodeInfoMap.insert(std::pair<IPAddress, NodeInfo*>(address, node_info_ptr));
+
+	return node_info_ptr;
+}
+
+void
+StatCollector::NodeStat::remove_oldest_node_info(void)
+{
+	std::map<IPAddress, NodeInfo*>::iterator cur_iter, oldest_iter;
+	TimeStamp ts, oldest_ts;
+
+	for (cur_iter = oldest_iter = mNodeInfoMap.begin(); cur_iter != mNodeInfoMap.end(); cur_iter++) {
+
+		ts = cur_iter->second->get_last_rx_or_tx_time();
+
+		if (oldest_ts.is_uninitialized()) {
+			oldest_ts = ts;
+			oldest_iter = cur_iter;
+		}
+
+		if (!ts.is_uninitialized()) {
+			if (ts < oldest_ts) {
+				oldest_ts = ts;
+				oldest_iter = cur_iter;
+			}
+		}
+	}
+
+	if (oldest_iter != mNodeInfoMap.end()) {
+		NodeInfo *node_info_ptr = oldest_iter->second;
+		mNodeInfoMap.erase(oldest_iter);
+		mNodeInfoPool.free(node_info_ptr);
+
+		syslog(LOG_INFO, "StatCollector: Out of NodeInfo objects --> Deleted the oldest NodeInfo");
+	}
+}
+
+void
+StatCollector::NodeStat::update_from_inbound_packet(const PacketInfo& packet_info)
+{
+	NodeInfo *node_info_ptr;
+
+	node_info_ptr = find_node_info(packet_info.mSrcAddress);
+	if (!node_info_ptr) {
+		node_info_ptr = create_new_node_info(packet_info.mSrcAddress);
+	}
+
+	if (node_info_ptr) {
+		node_info_ptr->mRxPacketsTotal++;
+		switch(packet_info.mType) {
+			case IPV6_TYPE_UDP: node_info_ptr->mRxPacketsUDP++; break;
+			case IPV6_TYPE_TCP: node_info_ptr->mRxPacketsTCP++; break;
+		}
+		node_info_ptr->mRxHistory.force_write(packet_info);
+	}
+}
+
+void
+StatCollector::NodeStat::update_from_outbound_packet(const PacketInfo& packet_info)
+{
+	NodeInfo *node_info_ptr;
+
+	node_info_ptr = find_node_info(packet_info.mDstAddress);
+	if (!node_info_ptr) {
+		node_info_ptr = create_new_node_info(packet_info.mDstAddress);
+	}
+
+	if (node_info_ptr) {
+		node_info_ptr->mTxPacketsTotal++;
+		switch(packet_info.mType) {
+			case IPV6_TYPE_UDP:  node_info_ptr->mTxPacketsUDP++;  break;
+			case IPV6_TYPE_TCP:  node_info_ptr->mTxPacketsTCP++;  break;
+		}
+		node_info_ptr->mTxHistory.force_write(packet_info);
+	}
+}
+
+void
+StatCollector::NodeStat::add_node_info_map_iter(StringList &output, const std::map<IPAddress, NodeInfo*>::const_iterator& it) const
+{
+	output.push_back("========================================================");
+	output.push_back("Address: " + it->first.to_string());
+	it->second->add_node_info(output);
+	output.push_back("");
+}
+
+void
+StatCollector::NodeStat::add_node_stat_history(StringList& output, std::string node_indicator) const
+{
+	std::map<IPAddress, NodeInfo*>::const_iterator it;
+
+	if (node_indicator.empty()) {
+		for (it = mNodeInfoMap.begin(); it != mNodeInfoMap.end(); it++) {
+			add_node_info_map_iter(output, it);
+		}
+	} else {
+		char c = node_indicator[0];
+		if (c == '@' || c == '[') { // Ip address mode
+			std::string ip_addr_str;
+			uint8_t ip_addr_buf[16];
+
+			if (c == '@') {
+				ip_addr_str = node_indicator.substr(1);
+			} else {
+				if (node_indicator[node_indicator.length() - 1] == ']') {
+					ip_addr_str = node_indicator.substr(1, node_indicator.length() - 2);
+				} else {
+					output.push_back(string_printf("Error : Missing \']\' in address format (\'%s\')", node_indicator.c_str()));
+					return;
+				}
+			}
+
+			if (inet_pton(AF_INET6, ip_addr_str.c_str(), ip_addr_buf) > 0) {
+				IPAddress ip_address;
+				ip_address.read_from(ip_addr_buf);
+				it = mNodeInfoMap.find(ip_address);
+				if (it != mNodeInfoMap.end()) {
+					add_node_info_map_iter(output, it);
+				} else {
+					output.push_back(string_printf("Error : Address does not exist (\'%s\')", node_indicator.c_str()));
+				}
+			} else {
+				output.push_back(string_printf("Error : Improper address format (\'%s\')", node_indicator.c_str()));
+			}
+		} else { // Index mode:
+			int index;
+			index = static_cast<int>(strtol(node_indicator.c_str(), NULL, 0));
+			if (index < mNodeInfoMap.size()) {
+				it = mNodeInfoMap.begin();
+				std::advance(it, index);
+				add_node_info_map_iter(output, it);
+			} else {
+				output.push_back(string_printf("Error: Out of bound index %d (\'%s\')", index, node_indicator.c_str()));
+			}
+		}
+	}
+}
+
+void
+StatCollector::NodeStat::add_node_stat(StringList& output) const
+{
+	std::map<IPAddress, NodeInfo*>::const_iterator it;
+
+	for (it = mNodeInfoMap.begin(); it != mNodeInfoMap.end(); it++) {
+		output.push_back("========================================================");
+		output.push_back("Address: " + it->first.to_string());
+		it->second->add_tx_stat(output);
+		it->second->add_rx_stat(output);
+		output.push_back("");
+	}
+}
+
+//-------------------------------------------------------------------
+// NodeStat::NodeInfo
+
+StatCollector::NodeStat::NodeInfo::NodeInfo()
+{
+	clear();
+}
+
+void
+StatCollector::NodeStat::NodeInfo::clear(void)
+{
+	mTxPacketsTotal = 0;
+	mTxPacketsUDP = 0;
+	mTxPacketsTCP = 0;
+
+	mRxPacketsTotal = 0;
+	mRxPacketsUDP = 0;
+	mRxPacketsTCP = 0;
+
+	mRxHistory.clear();
+	mTxHistory.clear();
+}
+
+StatCollector::TimeStamp
+StatCollector::NodeStat::NodeInfo::get_last_rx_time(void) const
+{
+	TimeStamp ts;
+	const PacketInfo *pkt_info_ptr;
+
+	pkt_info_ptr = mRxHistory.back();
+	if (pkt_info_ptr) {
+		ts = pkt_info_ptr->mTimeStamp;
+	}
+	return ts;
+}
+
+StatCollector::TimeStamp
+StatCollector::NodeStat::NodeInfo::get_last_tx_time(void) const
+{
+	TimeStamp ts;
+	const PacketInfo *pkt_info_ptr;
+
+	pkt_info_ptr = mTxHistory.back();
+	if (pkt_info_ptr) {
+		ts = pkt_info_ptr->mTimeStamp;
+	}
+	return ts;
+}
+
+StatCollector::TimeStamp
+StatCollector::NodeStat::NodeInfo::get_last_rx_or_tx_time(void) const
+{
+	TimeStamp rx_time = get_last_rx_time();
+	TimeStamp tx_time = get_last_tx_time();
+
+	// If either one is uninitialized, return the other one.
+	if (rx_time.is_uninitialized()) {
+		return tx_time;
+	}
+
+	if (tx_time.is_uninitialized()) {
+		return rx_time;
+	}
+
+	if (rx_time < tx_time) {
+		return tx_time;
+	}
+
+	return rx_time;
+}
+
+void
+StatCollector::NodeStat::NodeInfo::add_tx_stat(StringList& output, bool add_last_tx_time) const
+{
+	std::string str;
+
+	str = string_printf("%d packet%s (%d udp, %d tcp, %d other) %s sent to this address",
+		mTxPacketsTotal,
+		(mTxPacketsTotal == 1)? "" : "s",
+		mTxPacketsUDP,
+		mTxPacketsTCP,
+		mTxPacketsTotal - mTxPacketsUDP - mTxPacketsTCP,
+		(mTxPacketsTotal == 1)? "was" : "were"
+	);
+
+	if (add_last_tx_time) {
+		TimeStamp last_tx_time = get_last_tx_time();
+		if (!last_tx_time.is_uninitialized()) {
+			str += " - last tx happened " + last_tx_time.to_string();
+		}
+	}
+
+	output.push_back(str);
+}
+
+void
+StatCollector::NodeStat::NodeInfo::add_rx_stat(StringList& output, bool add_last_rx_time) const
+{
+	std::string str;
+
+	str = string_printf("%d packet%s (%d udp, %d tcp, %d other) %s received from this address",
+		mRxPacketsTotal,
+		(mRxPacketsTotal == 1)? "" : "s",
+		mRxPacketsUDP,
+		mRxPacketsTCP,
+		mRxPacketsTotal - mRxPacketsUDP - mRxPacketsTCP,
+		(mRxPacketsTotal == 1)? "was" : "were"
+	);
+
+	if (add_last_rx_time) {
+		TimeStamp last_rx_time = get_last_rx_time();
+		if (!last_rx_time.is_uninitialized()) {
+			str += " - last rx happened " + last_rx_time.to_string();
+		}
+	}
+
+	output.push_back(str);
+}
+
+void
+StatCollector::NodeStat::NodeInfo::add_node_info(StringList& output) const
+{
+	add_tx_stat(output, false);
+
+	if (!mTxHistory.empty()) {
+		RingBuffer<PacketInfo, STAT_COLLECTOR_PER_NODE_TX_HISTORY_SIZE>::ReverseIterator iter;
+
+		output.push_back(string_printf("\tLast %d tx packets", mTxHistory.size()));
+		for (iter = mTxHistory.rbegin(); iter != mTxHistory.rend(); ++iter) {
+			output.push_back("\t" + iter->to_string());
+		}
+	}
+	output.push_back("");
+
+	add_rx_stat(output, false);
+
+	if (!mRxHistory.empty()) {
+		RingBuffer<PacketInfo, STAT_COLLECTOR_PER_NODE_RX_HISTORY_SIZE>::ReverseIterator iter;
+
+		output.push_back(string_printf("\tLast %d rx packets", mRxHistory.size()));
+		for (iter = mRxHistory.rbegin(); iter != mRxHistory.rend(); ++iter) {
+			output.push_back("\t" + iter->to_string());
+		}
+	}
+}
+
+//-------------------------------------------------------------------
+// LinkStat:LinkQuality
+
+StatCollector::LinkStat::LinkQuality::LinkQuality()
+		: mTimeStamp()
+{
+	mRssi = 0;
+	mLinkQualityIncomingOutgoing = 0xff;
+}
+
+void
+StatCollector::LinkStat::LinkQuality::set(int8_t rssi, uint8_t incoming_link_quality, uint8_t outgoing_link_quality)
+{
+	mRssi = rssi;
+	mLinkQualityIncomingOutgoing = ((incoming_link_quality & 0x0f) << 4) + (outgoing_link_quality & 0x0f);
+	mTimeStamp.set_to_now();
+}
+
+StatCollector::TimeStamp
+StatCollector::LinkStat::LinkQuality::get_time_stamp(void) const
+{
+	return mTimeStamp;
+}
+
+uint8_t
+StatCollector::LinkStat::LinkQuality::get_incoming_link_quality(void) const
+{
+	return (mLinkQualityIncomingOutgoing >> 4);
+}
+
+uint8_t
+StatCollector::LinkStat::LinkQuality::get_outgoing_link_quality(void) const
+{
+	return (mLinkQualityIncomingOutgoing & 0x0f);
+}
+
+std::string
+StatCollector::LinkStat::LinkQuality::to_string(void) const
+{
+	if (mTimeStamp.is_uninitialized()) {
+		return "Uninitialized";
+	}
+
+	return mTimeStamp.to_string() + string_printf("-> RSSI: %-6d  LinkQuality(Incoming/Outgoing): %d/%d", mRssi,
+			get_incoming_link_quality(), get_outgoing_link_quality());
+}
+
+//-------------------------------------------------------------------
+// LinkStat::LinkInfo
+
+StatCollector::LinkStat::LinkInfo::LinkInfo() :
+		mLinkQualityHistory()
+{
+	clear();
+}
+
+void
+StatCollector::LinkStat::LinkInfo::clear(void)
+{
+	mLinkQualityHistory.clear();
+}
+
+bool
+StatCollector::LinkStat::LinkInfo::empty(void) const
+{
+	return mLinkQualityHistory.empty();
+}
+
+void
+StatCollector::LinkStat::LinkInfo::add_link_info(StringList& output, int count) const
+{
+	RingBuffer<LinkQuality, STAT_COLLECTOR_LINK_QUALITY_HISTORY_SIZE>::ReverseIterator iter;
+
+	if (count == 0) {
+		count = mLinkQualityHistory.size();
+	}
+
+	for (iter = mLinkQualityHistory.rbegin(); (iter != mLinkQualityHistory.rend()) && (count != 0); ++iter, --count) {
+		output.push_back("\t" + iter->to_string());
+	}
+}
+
+StatCollector::TimeStamp
+StatCollector::LinkStat::LinkInfo::get_last_update_time(void) const
+{
+	TimeStamp ts;
+	const LinkQuality *link_quality_ptr;
+
+	link_quality_ptr = mLinkQualityHistory.back();
+	if (link_quality_ptr) {
+		ts = link_quality_ptr->get_time_stamp();
+	}
+	return ts;
+}
+
+//-------------------------------------------------------------------
+// LinkStat
+
+StatCollector::LinkStat::LinkStat()
+		: mLinkInfoPool(), mLinkInfoMap()
+{
+}
+
+void
+StatCollector::LinkStat::clear(void)
+{
+	mLinkInfoMap.clear();
+	mLinkInfoPool.free_all();
+}
+
+
+StatCollector::LinkStat::LinkInfo *
+StatCollector::LinkStat::find_link_info(const EUI64Address& address)
+{
+	std::map<EUI64Address, LinkInfo *>::iterator it = mLinkInfoMap.find(address);
+
+	if (it == mLinkInfoMap.end())
+		return NULL;
+
+	return it->second;
+}
+
+StatCollector::LinkStat::LinkInfo *
+StatCollector::LinkStat::create_new_link_info(const EUI64Address& address)
+{
+	LinkInfo *link_info_ptr = NULL;
+
+	do {
+		link_info_ptr = mLinkInfoPool.alloc();
+
+		// If we can not allocate a new link info (all objects in the pool are used),
+		// we will remove oldest one in the map and try again.
+		if (!link_info_ptr) {
+			remove_oldest_link_info();
+		}
+	} while (!link_info_ptr);
+
+	link_info_ptr->clear();
+
+	mLinkInfoMap.insert(std::pair<EUI64Address, LinkInfo*>(address, link_info_ptr));
+
+	return link_info_ptr;
+}
+
+void
+StatCollector::LinkStat::remove_oldest_link_info(void)
+{
+	std::map<EUI64Address, LinkInfo*>::iterator cur_iter, oldest_iter;
+	TimeStamp ts, oldest_ts;
+
+	for (cur_iter = oldest_iter = mLinkInfoMap.begin(); cur_iter != mLinkInfoMap.end(); cur_iter++) {
+
+		ts = cur_iter->second->get_last_update_time();
+
+		if (oldest_ts.is_uninitialized()) {
+			oldest_ts = ts;
+			oldest_iter = cur_iter;
+		}
+
+		if (!ts.is_uninitialized()) {
+			if (ts < oldest_ts) {
+				oldest_ts = ts;
+				oldest_iter = cur_iter;
+			}
+		}
+	}
+
+	if (oldest_iter != mLinkInfoMap.end()) {
+		LinkInfo *link_info_ptr = oldest_iter->second;
+		mLinkInfoMap.erase(oldest_iter);
+		mLinkInfoPool.free(link_info_ptr);
+
+		syslog(LOG_INFO, "StatCollector: Out of LinkInfo objects --> Deleted the oldest LinkInfo");
+	}
+}
+
+void
+StatCollector::LinkStat::update(const uint8_t *eui64_address_arr, int8_t rssi,
+		uint8_t incoming_link_quality, uint8_t outgoing_link_quality,
+		NodeType node_type)
+{
+	LinkQuality link_quality;
+	EUI64Address address;
+	LinkInfo *link_info_ptr;
+
+	if (eui64_address_arr) {
+
+		address.read_from(eui64_address_arr);
+
+		link_quality.set(rssi, incoming_link_quality, outgoing_link_quality);
+
+		link_info_ptr = find_link_info(address);
+		if (!link_info_ptr) {
+			link_info_ptr = create_new_link_info(address);
+		}
+
+		if (link_info_ptr) {
+			link_info_ptr->mLinkQualityHistory.force_write(link_quality);
+			link_info_ptr->mNodeType = node_type;
+		}
+	}
+}
+
+void
+StatCollector::LinkStat::add_link_stat(StringList& output, int count) const
+{
+	std::map<EUI64Address, LinkInfo*>::const_iterator it;
+
+	for (it = mLinkInfoMap.begin(); it != mLinkInfoMap.end(); ++it) {
+		output.push_back("========================================================");
+		output.push_back("EUI64 address: " + it->first.to_string() + " -  Node type: " +
+			node_type_to_string(it->second->mNodeType));
+		it->second->add_link_info(output, count);
+		output.push_back("");
+	}
+}
+
+//-------------------------------------------------------------------
+// StatCollector
+
+StatCollector::StatCollector() :
+		mTxBytesTotal(), mRxBytesTotal(),
+		mRxHistory(), mTxHistory(),
+		mLastBlockingHostSleepTime(),
+		mNodeStat(), mLinkStat(),
+		mAutoLogTimer(), mLinkStatTimer()
+{
+	mControlInterface = NULL;
+
+	mTxPacketsTotal = 0;
+	mRxPacketsTotal = 0;
+
+	mLastReadyForHostSleepState = true;
+
+	mUserRequestLogLevel = STAT_COLLECTOR_LOG_LEVEL_USER_REQUEST;
+	mAutoLogLevel = STAT_COLLECTOR_AUTO_LOG_DEFAULT_LOG_LEVEL;
+
+	mAutoLogState = kAutoLogShort;
+	mAutoLogPeriod = STAT_COLLECTOR_AUTO_LOG_PERIOD_IN_MIN * Timer::kOneMinute;
+	update_auto_log_timer();
+
+	update_link_stat_timer(STAT_COLLECTOR_LINK_STAT_PERIOD_IN_MIN * Timer::kOneMinute);
+}
+
+StatCollector::~StatCollector()
+{
+	mAutoLogTimer.cancel();
+}
+
+void
+StatCollector::set_ncp_control_interface(NCPControlInterface *ncp_ctrl_interface)
+{
+	if (mControlInterface == ncp_ctrl_interface) {
+		return;
+	}
+
+	if (mControlInterface) {
+		mControlInterface->mOnPropertyChanged.disconnect(
+			boost::bind(&StatCollector::property_changed, this, _1, _2)
+		);
+	}
+
+	mControlInterface = ncp_ctrl_interface;
+
+	if (mControlInterface) {
+		mControlInterface->mOnPropertyChanged.connect(
+			boost::bind(&StatCollector::property_changed, this, _1, _2)
+		);
+	}
+}
+
+void
+StatCollector::record_inbound_packet(const uint8_t *packet)
+{
+	PacketInfo packet_info;
+
+	if (packet_info.update_from_packet(packet)) {
+		mRxPacketsTotal++;
+		switch (packet_info.mType) {
+			case IPV6_TYPE_UDP:  mRxPacketsUDP++;  break;
+			case IPV6_TYPE_TCP:  mRxPacketsTCP++;  break;
+			case IPV6_TYPE_ICMP: mRxPacketsICMP++; break;
+		}
+		mRxBytesTotal.add(packet_info.mPayloadLen);
+		mRxHistory.force_write(packet_info);
+
+		mNodeStat.update_from_inbound_packet(packet_info);
+	}
+}
+
+void
+StatCollector::record_outbound_packet(const uint8_t *packet)
+{
+	PacketInfo packet_info;
+
+	if (packet_info.update_from_packet(packet)) {
+		mTxPacketsTotal++;
+		switch (packet_info.mType) {
+			case IPV6_TYPE_UDP:  mTxPacketsUDP++;  break;
+			case IPV6_TYPE_TCP:  mTxPacketsTCP++;  break;
+			case IPV6_TYPE_ICMP: mTxPacketsICMP++; break;
+		}
+		mTxBytesTotal.add(packet_info.mPayloadLen);
+		mTxHistory.force_write(packet_info);
+
+		mNodeStat.update_from_outbound_packet(packet_info);
+	}
+}
+
+void
+StatCollector::record_ncp_state_change(NCPState new_ncp_state)
+{
+	NcpStateInfo ncp_state_info;
+	ncp_state_info.update(new_ncp_state);
+	mNCPStateHistory.force_write(ncp_state_info);
+}
+
+void
+StatCollector::record_ncp_ready_for_host_sleep_state(bool ready_for_sleep_state)
+{
+	ReadyForHostSleepState new_state;
+
+	if (mLastReadyForHostSleepState == ready_for_sleep_state) {
+		return;
+	}
+
+	if (ready_for_sleep_state) {
+		new_state.update_with_blocking_sleep_time(mLastBlockingHostSleepTime);
+
+		mReadyForSleepHistory.force_write(new_state);
+	} else {
+		mLastBlockingHostSleepTime.set_to_now();
+	}
+
+	mLastReadyForHostSleepState = ready_for_sleep_state;
+}
+
+void
+StatCollector::add_tx_history(StringList& output, int count) const
+{
+	if (count == 0) {
+		count = mTxHistory.size();
+	}
+
+	if (!mTxHistory.empty()) {
+		RingBuffer<PacketInfo, STAT_COLLECTOR_TX_HISTORY_SIZE>::ReverseIterator iter;
+
+		output.push_back("Tx History");
+		output.push_back("-------------------------");
+		for (iter = mTxHistory.rbegin(); (iter != mTxHistory.rend()) && (count != 0); ++iter, --count) {
+			output.push_back(iter->to_string());
+		}
+	} else  {
+		output.push_back("Tx history is empty");
+	}
+}
+
+void
+StatCollector::add_rx_history(StringList& output, int count) const
+{
+	if (count == 0) {
+		count = mRxHistory.size();
+	}
+
+	if (!mRxHistory.empty()) {
+		RingBuffer<PacketInfo, STAT_COLLECTOR_RX_HISTORY_SIZE>::ReverseIterator iter;
+
+		output.push_back("Rx History");
+		output.push_back("-------------------------");
+		for (iter = mRxHistory.rbegin(); (iter != mRxHistory.rend()) && (count != 0); ++iter, --count) {
+			output.push_back(iter->to_string());
+		}
+	} else  {
+		output.push_back("Rx history is empty");
+	}
+}
+
+void
+StatCollector::add_ncp_state_history(StringList& output, int count) const
+{
+	if (count == 0) {
+		count = mNCPStateHistory.size();
+	}
+
+	if(!mNCPStateHistory.empty()) {
+		RingBuffer<NcpStateInfo, STAT_COLLECTOR_NCP_STATE_HISTORY_SIZE>::ReverseIterator iter;
+
+		output.push_back("NCP State History");
+		output.push_back("-------------------------");
+		for (iter = mNCPStateHistory.rbegin(); (iter != mNCPStateHistory.rend()) && (count != 0); ++iter, --count) {
+			output.push_back(iter->to_string());
+		}
+	} else {
+		output.push_back("NCP state history is empty.");
+	}
+}
+
+void
+StatCollector::add_ncp_ready_for_host_sleep_state_history(StringList& output, int count) const
+{
+	if (count == 0) {
+		count = mReadyForSleepHistory.size();
+	}
+
+	if (!mReadyForSleepHistory.empty() || (mLastReadyForHostSleepState == false)) {
+		RingBuffer<ReadyForHostSleepState, STAT_COLLECTOR_NCP_READY_FOR_HOST_SLEEP_STATE_HISTORY_SIZE>::ReverseIterator iter;
+
+		output.push_back("\'NCP Ready For Host Sleep State\' History");
+		output.push_back("-------------------------");
+
+		if (mLastReadyForHostSleepState == false) {
+			output.push_back( mLastBlockingHostSleepTime.to_string() + " host sleep was blocked till now");
+		}
+
+		for (iter = mReadyForSleepHistory.rbegin(); (iter != mReadyForSleepHistory.rend()) && (count != 0); ++iter, --count) {
+			output.push_back(iter->to_string());
+		}
+	} else {
+		output.push_back("\'NCP Ready For Host Sleep State\' history is empty.");
+	}
+}
+
+void
+StatCollector::add_tx_stat(StringList& output) const
+{
+	output.push_back (
+		string_printf("Tx: %d packet%s (%d udp, %d tcp, %d icmp6) -- ",
+			mTxPacketsTotal, (mTxPacketsTotal == 1)? "" : "s",
+			mTxPacketsUDP,
+			mTxPacketsTCP,
+			mTxPacketsICMP
+		) +
+		mTxBytesTotal.to_string()
+	);
+}
+
+void
+StatCollector::add_rx_stat(StringList& output) const
+{
+	output.push_back (
+		string_printf("Rx: %d packet%s (%d udp, %d tcp, %d icmp6) -- ",
+			mRxPacketsTotal, (mRxPacketsTotal == 1)? "" : "s",
+			mRxPacketsUDP,
+			mRxPacketsTCP,
+			mRxPacketsICMP
+		) +
+		mRxBytesTotal.to_string()
+	);
+}
+
+void
+StatCollector::add_all_info(StringList& output, int count) const
+{
+	add_tx_stat(output);
+	add_tx_history(output, count);
+
+	output.push_back("");
+
+	add_rx_stat(output);
+	add_rx_history(output, count);
+
+	output.push_back("");
+
+	add_ncp_state_history(output, count);
+
+	output.push_back("");
+
+	if (count == 0) {
+		mNodeStat.add_node_stat_history(output);
+	} else {
+		mNodeStat.add_node_stat(output);
+	}
+
+	output.push_back("");
+	if (count == 0) {
+		mLinkStat.add_link_stat(output);
+	} else {
+		mLinkStat.add_link_stat(output, STAT_COLLECTOR_LINK_STAT_HISTORY_SIZE);
+	}
+}
+
+bool
+StatCollector::is_a_stat_property(const std::string& key)
+{
+	// Check for the prefix to match
+	return strncaseequal(key.c_str(), kWPANTUNDProperty_Stat_Prefix , sizeof(kWPANTUNDProperty_Stat_Prefix) - 1);
+}
+
+void
+StatCollector::add_help(StringList& output) const
+{
+	output.push_back("List of statistics properties");
+	output.push_back(string_printf("\t %-26s - RX statistics (all nodes)", kWPANTUNDProperty_StatRX));
+	output.push_back(string_printf("\t %-26s - TX statistics (all nodes)", kWPANTUNDProperty_StatTX));
+	output.push_back(string_printf("\t %-26s - RX packet info history (all nodes)", kWPANTUNDProperty_StatRXHistory));
+	output.push_back(string_printf("\t %-26s - TX packet info history (all nodes)", kWPANTUNDProperty_StatTXHistory));
+	output.push_back(string_printf("\t %-26s - Both RX & TX packet info history (all nodes)", kWPANTUNDProperty_StatHistory));
+	output.push_back(string_printf("\t %-26s - NCP state change history", kWPANTUNDProperty_StatNCP));
+	output.push_back(string_printf("\t %-26s - \'Blocking Host Sleep\' state change history", kWPANTUNDProperty_StatBlockingHostSleep));
+	output.push_back(string_printf("\t %-26s - List of nodes + RX/TX statistics per node", kWPANTUNDProperty_StatNode));
+	output.push_back(string_printf("\t %-26s - List of nodes + RX/TX statistics and packet history per node", kWPANTUNDProperty_StatNodeHistory));
+	output.push_back(string_printf("\t %-26s - List of nodes + RX/TX statistics and packet history for a specific node with given IP address", kWPANTUNDProperty_StatNodeHistoryID "[<ipv6>]"));
+	output.push_back(string_printf("\t %-26s - List of nodes + RX/TX statistics and packet history for a specific node with given index", kWPANTUNDProperty_StatNodeHistoryID "<index>"));
+	output.push_back(string_printf("\t %-26s - Peer link quality history - short version", kWPANTUNDProperty_StatLinkQualityShort));
+	output.push_back(string_printf("\t %-26s - Peer link quality history - long version", kWPANTUNDProperty_StatLinkQualityLong));
+	output.push_back(string_printf("\t %-26s - All info - short version", kWPANTUNDProperty_StatShort));
+	output.push_back(string_printf("\t %-26s - All info - long version", kWPANTUNDProperty_StatLong));
+	output.push_back(string_printf("\t "));
+	output.push_back(string_printf("\t %-26s - Peer link quality information - get only", kWPANTUNDProperty_StatLinkQuality));
+	output.push_back(string_printf("\t %-26s - Period interval (in seconds) for collecting peer link quality - get/set - zero to disable", kWPANTUNDProperty_StatLinkQualityPeriod));
+	output.push_back(string_printf("\t %-26s - AutoLog information - get only", kWPANTUNDProperty_StatAutoLog));
+	output.push_back(string_printf("\t %-26s - AutoLog state (\'disabled\',\'long\',\'short\'') - get/set", kWPANTUNDProperty_StatAutoLogState));
+	output.push_back(string_printf("\t %-26s - AutoLog period in minutes - get/set", kWPANTUNDProperty_StatAutoLogPeriod));
+	output.push_back(string_printf("\t %-26s - AutoLog log level - get/set", kWPANTUNDProperty_StatAutoLogLogLevel));
+	output.push_back(string_printf("\t %-26s - Log level for user requested logs - get/set", kWPANTUNDProperty_StatUserLogRequestLogLevel));
+    output.push_back(string_printf("\t %-26s : \'emerg\', \'alert\', \'crit\', \'err\', \'warning\', \'notice\', \'info\', \'debug\'","Valid log levels"));
+    output.push_back(string_printf("\t "));
+	output.push_back(string_printf("\t %-26s - Print this help", kWPANTUNDProperty_StatHelp));
+}
+
+int
+StatCollector::get_stat_property(const std::string& key, StringList& output) const
+{
+	int return_status = kWPANTUNDStatus_Ok;
+
+	if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatShort)) {
+		add_all_info(output, STAT_COLLECTOR_SHORT_HISTORY_COUNT);
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatLong)) {
+		add_all_info(output);
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatRX)) {
+		add_rx_stat(output);
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatTX)) {
+		add_tx_stat(output);
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatRXHistory)) {
+		add_rx_stat(output);
+		add_rx_history(output);
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatTXHistory)) {
+		add_tx_stat(output);
+		add_tx_history(output);
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatHistory)) {
+		add_rx_history(output);
+		output.push_back("");
+		add_tx_history(output);
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatNCP)) {
+		add_ncp_state_history(output);
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatBlockingHostSleep)) {
+		add_ncp_ready_for_host_sleep_state_history(output);
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatNode)) {
+		mNodeStat.add_node_stat(output);
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatNodeHistory)) {
+		mNodeStat.add_node_stat_history(output);
+	} else if (strncaseequal(key.c_str(), kWPANTUNDProperty_StatNodeHistoryID, sizeof(kWPANTUNDProperty_StatNodeHistoryID) - 1)) {
+		mNodeStat.add_node_stat_history(output, key.substr(sizeof(kWPANTUNDProperty_StatNodeHistoryID) - 1));
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatLinkQualityLong)) {
+		mLinkStat.add_link_stat(output);
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatLinkQualityShort)) {
+		mLinkStat.add_link_stat(output, STAT_COLLECTOR_LINK_STAT_HISTORY_SIZE);
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatHelp)) {
+		add_help(output);
+	} else {
+		output.push_back(std::string("Unknown/unsupported stat property. Please use \"get ") + kWPANTUNDProperty_StatHelp +
+			"\" to get list of supported properties by statistics collector." );
+		return_status = kWPANTUNDStatus_PropertyNotFound;
+	}
+
+	return return_status;
+}
+
+void
+StatCollector::get_property(const std::string& key, CallbackWithStatusArg1 cb)
+{
+	// First check for AutoLog properties.
+	if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatAutoLog)) {
+		std::string str;
+		switch(mAutoLogState) {
+			case kAutoLogDisabled:
+				str = "Auto stat log is disabled.";
+				break;
+
+			case kAutoLogLong:
+				str = string_printf("Auto stat log is enabled using long version every %d min at log level \'%s\'.",
+					mAutoLogPeriod / Timer::kOneMinute, log_level_to_string(mAutoLogLevel).c_str());
+				break;
+
+			case kAutoLogShort:
+				str = string_printf("Auto stat log is enabled using short version of stat every %d min at log level \'%s\'.",
+					mAutoLogPeriod / Timer::kOneMinute, log_level_to_string(mAutoLogLevel).c_str());
+				break;
+		}
+		cb(kWPANTUNDStatus_Ok, boost::any(str));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatAutoLogState)) {
+		std::string state;
+
+		switch (mAutoLogState) {
+			case kAutoLogDisabled:  state = kWPANTUNDStatAutoLogState_Disabled; break;
+			case kAutoLogShort:     state = kWPANTUNDStatAutoLogState_Short;    break;
+			case kAutoLogLong:      state = kWPANTUNDStatAutoLogState_Long;     break;
+		}
+		cb(kWPANTUNDStatus_Ok, boost::any(state));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatAutoLogPeriod)) {
+		int period_in_min = mAutoLogPeriod / Timer::kOneMinute;
+		cb(kWPANTUNDStatus_Ok, boost::any(period_in_min));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatAutoLogLogLevel)) {
+		cb(kWPANTUNDStatus_Ok, boost::any(log_level_to_string(mAutoLogLevel)));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatUserLogRequestLogLevel)) {
+		cb(kWPANTUNDStatus_Ok, boost::any(log_level_to_string(mUserRequestLogLevel)));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatLinkQuality)) {
+		int period_in_sec = static_cast<int>(mLinkStatTimer.get_interval() / Timer::kOneSecond);
+		std::string str;
+
+		if (period_in_sec == 0) {
+			str = "Periodic query of peer link quality is disabled";
+		} else {
+			str = string_printf("Peer link quality is collected every %d second%s",
+				period_in_sec, (period_in_sec == 1)? "" : "s");
+		}
+		cb(kWPANTUNDStatus_Ok, boost::any(str));
+
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatLinkQualityPeriod)) {
+		int period_in_sec = static_cast<int>(mLinkStatTimer.get_interval() / Timer::kOneSecond);
+		cb(kWPANTUNDStatus_Ok, boost::any(period_in_sec));
+
+	} else {
+		// If not an AutoLog property, check for the stat properties.
+		StringList output;
+		int status = get_stat_property(key, output);
+
+		if (status == kWPANTUNDStatus_Ok) {
+			cb(status, boost::any(output));
+		} else {
+			std::string err_str;
+			err_str = std::string("Unknown stat property. Please use \"get ") + kWPANTUNDProperty_StatHelp
+				+ "\" to get help about StatCollector.";
+			cb(status, boost::any(err_str));
+		}
+	}
+}
+
+void
+StatCollector::set_property(const std::string& key, const boost::any& value, CallbackWithStatus cb)
+{
+	int status = kWPANTUNDStatus_Ok;
+
+	// First check for AutoLog properties.
+	if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatAutoLogState)) {
+		std::string new_state_str = any_to_string(value);
+		AutoLogState new_state;
+
+		if (strcaseequal(new_state_str.c_str(), kWPANTUNDStatAutoLogState_Disabled) ||
+		    strcaseequal(new_state_str.c_str(), "off") ||
+		    strcaseequal(new_state_str.c_str(), "no") ||
+		    strcaseequal(new_state_str.c_str(), "0") )
+		{
+			new_state = kAutoLogDisabled;
+		} else if (
+		    strcaseequal(new_state_str.c_str(), kWPANTUNDStatAutoLogState_Short) ||
+		    strcaseequal(new_state_str.c_str(), "on") ||
+		    strcaseequal(new_state_str.c_str(), "yes") ||
+		    strcaseequal(new_state_str.c_str(), "1") )
+		{
+			new_state = kAutoLogShort;
+		} else if (strcaseequal(new_state_str.c_str(), kWPANTUNDStatAutoLogState_Long)) {
+			new_state = kAutoLogLong;
+		} else {
+			status = kWPANTUNDStatus_InvalidArgument;
+		}
+
+		if (status == kWPANTUNDStatus_Ok) {
+			if (new_state != mAutoLogState) {
+				mAutoLogState = new_state;
+				update_auto_log_timer();
+			}
+		}
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatAutoLogPeriod)) {
+		int period_in_min = any_to_int(value);
+		if ((period_in_min > 0) && (period_in_min <= STAT_COLLECTOR_AUTO_LOG_MAX_PERIOD)) {
+			mAutoLogPeriod = period_in_min * Timer::kOneMinute;
+			update_auto_log_timer();
+		} else {
+			status = kWPANTUNDStatus_InvalidArgument;
+		}
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatAutoLogLogLevel)) {
+		int log_level = log_level_from_string(any_to_string(value).c_str());
+		if (log_level >= 0) {
+			status = kWPANTUNDStatus_Ok;
+			mAutoLogLevel = log_level;
+		} else {
+			status = kWPANTUNDStatus_InvalidArgument;
+		}
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatUserLogRequestLogLevel)) {
+		int log_level = log_level_from_string(any_to_string(value).c_str());
+		if (log_level >= 0) {
+			status = kWPANTUNDStatus_Ok;
+			mUserRequestLogLevel = log_level;
+		} else {
+			status = kWPANTUNDStatus_InvalidArgument;
+		}
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_StatLinkQualityPeriod)) {
+		int period_in_sec = any_to_int(value);
+		if (period_in_sec >= 0) {
+			update_link_stat_timer(period_in_sec * Timer::kOneSecond);
+		}
+	} else {
+		StringList output;
+
+		status = get_stat_property(key, output);
+
+		if (status == kWPANTUNDStatus_Ok) {
+			for(StringList::iterator it = output.begin(); it != output.end(); ++it) {
+				syslog(mUserRequestLogLevel, "Stat: %s", it->c_str());
+			}
+		}
+	}
+
+	cb(status);
+}
+
+void
+StatCollector::update_auto_log_timer(void)
+{
+	if (mAutoLogState == kAutoLogDisabled) {
+		mAutoLogTimer.cancel();
+	} else {
+		mAutoLogTimer.schedule(
+			mAutoLogPeriod,
+			boost::bind(&StatCollector::auto_log_timer_did_fire, this),
+			Timer::kPeriodicFixedDelay
+		);
+
+		// Invoke the callback directly for the first iteration
+		auto_log_timer_did_fire();
+	}
+}
+
+void
+StatCollector::auto_log_timer_did_fire(void)
+{
+	StringList output;
+	int status = kWPANTUNDStatus_Canceled;
+
+	switch (mAutoLogState) {
+		case kAutoLogDisabled:
+			mAutoLogTimer.cancel();
+			break;
+
+		case kAutoLogLong:
+			status = get_stat_property(kWPANTUNDProperty_StatLong, output);
+			break;
+
+		case kAutoLogShort:
+			status = get_stat_property(kWPANTUNDProperty_StatShort, output);
+			break;
+	}
+
+	if (status == kWPANTUNDStatus_Ok) {
+		for(StringList::iterator it = output.begin(); it != output.end(); ++it) {
+			syslog(mAutoLogLevel, "Stat (autolog): %s", it->c_str());
+		}
+	}
+}
+
+void
+StatCollector::update_link_stat_timer(Timer::Interval interval)
+{
+	if (interval == 0) {
+		mLinkStatTimer.cancel();
+	} else {
+		mLinkStatTimer.schedule(
+			interval,
+			boost::bind(&StatCollector::link_stat_timer_did_fire, this),
+			Timer::kPeriodicFixedDelay
+		);
+
+		// Invoke the callback directly for the first iteration
+		link_stat_timer_did_fire();
+	}
+}
+
+void
+StatCollector::link_stat_timer_did_fire(void)
+{
+}
+
+void
+StatCollector::did_get_rip_entry_value_map(int status, const boost::any& value)
+{
+	if (status == kWPANTUNDStatus_Ok) {
+		if (value.type() == typeid(std::list<ValueMap>)) {
+			std::list<ValueMap> rip_entry_list;
+			std::list<ValueMap>::const_iterator it;
+
+			rip_entry_list = boost::any_cast< std::list<ValueMap> >(value);
+
+			for (it = rip_entry_list.begin(); it != rip_entry_list.end(); ++it) {
+				record_rip_entry(*it);
+			}
+		}
+	}
+}
+
+int
+StatCollector::record_rip_entry(const ValueMap& rip_entry)
+{
+	ValueMap::const_iterator it;
+	Data eui64;
+	int8_t rssi;
+	uint8_t in_lqi;
+	uint8_t out_lqi;
+	NodeType node_type;
+
+
+	mLinkStat.update(eui64.data(), rssi, in_lqi, out_lqi, node_type);
+
+	return kWPANTUNDStatus_Ok;
+}
+
+void
+StatCollector::property_changed(const std::string& key, const boost::any& value)
+{
+	if (strcaseequal(key.c_str(), kWPANTUNDProperty_NCPState)) {
+		record_ncp_state_change(string_to_ncp_state(any_to_string(value)));
+	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_DaemonReadyForHostSleep)) {
+		record_ncp_ready_for_host_sleep_state(any_to_bool(value));
+	}
+}
diff --git a/src/wpantund/StatCollector.h b/src/wpantund/StatCollector.h
new file mode 100644
index 0000000..30087de
--- /dev/null
+++ b/src/wpantund/StatCollector.h
@@ -0,0 +1,331 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      Declaration of Statistics collector module
+ *
+ */
+
+#ifndef wpantund_StatCollector_h
+#define wpantund_StatCollector_h
+
+#include <stdint.h>
+#include <string>
+#include <list>
+#include <map>
+#include "time-utils.h"
+#include "RingBuffer.h"
+#include "ObjectPool.h"
+#include "NCPControlInterface.h"
+#include "NCPTypes.h"
+#include "Timer.h"
+#include "ValueMap.h"
+
+namespace nl {
+namespace wpantund {
+
+// Size of the rx/tx history (for all nodes)
+#define STAT_COLLECTOR_RX_HISTORY_SIZE        64
+#define STAT_COLLECTOR_TX_HISTORY_SIZE        64
+
+// Size of the NCP state history
+#define STAT_COLLECTOR_NCP_STATE_HISTORY_SIZE 64
+
+// Size of the NCP "ReadyForHostSleep" state history
+#define STAT_COLLECTOR_NCP_READY_FOR_HOST_SLEEP_STATE_HISTORY_SIZE  64
+
+// Max number of nodes to track at the same time (nodes are tracked by IP address)
+#define STAT_COLLECTOR_MAX_NODES   64
+
+// Size of rx/tx history per node
+#define STAT_COLLECTOR_PER_NODE_RX_HISTORY_SIZE  5
+#define STAT_COLLECTOR_PER_NODE_TX_HISTORY_SIZE  5
+
+// Maximm number of peer nodes for which we store link quality
+#define STAT_COLLECTOR_MAX_LINKS   64
+
+// History length of link quality info per peer
+#define STAT_COLLECTOR_LINK_QUALITY_HISTORY_SIZE 40
+
+class StatCollector
+{
+public:
+	StatCollector();
+	virtual ~StatCollector();
+
+	void set_ncp_control_interface(NCPControlInterface *ncp_ctrl_interface);
+
+	// Static class methods
+
+	static bool is_a_stat_property(const std::string& key);   // returns true if the property key is associated with stat module
+
+	void get_property(const std::string& key, CallbackWithStatusArg1 cb);
+	void set_property(const std::string& key, const boost::any& value, CallbackWithStatus cb);
+
+	// Methods to inform StatCollector about received/sent packets and state changes
+	void record_inbound_packet(const uint8_t *ipv6_packet);
+	void record_outbound_packet(const uint8_t *ipv6_packet);
+
+private:
+	// Internal types and data structures
+
+	typedef std::list<std::string> StringList;
+
+	struct IPAddress
+	{
+		std::string to_string(void) const;
+		void read_from(const uint8_t *arr);
+		bool operator==(const IPAddress& lhs) const;
+		bool operator<(const IPAddress& lhs) const;
+	private:
+		uint32_t mAddressBuffer[4];
+	};
+
+	struct EUI64Address
+	{
+		std::string to_string(void) const;
+		void read_from(const uint8_t *arr);
+
+		bool operator==(const EUI64Address& lhs) const;
+		bool operator<(const EUI64Address& lhs) const;
+	private:
+		uint32_t mAddress[2];
+	};
+
+	struct TimeStamp
+	{
+		TimeStamp();
+		void set_to_now(void);
+		void clear(void);
+		cms_t get_ms_till_now(void) const;
+		bool is_expired(void) const;
+		bool is_uninitialized(void) const;
+		std::string to_string(void) const;
+		bool operator==(const TimeStamp& lhs);
+		bool operator<(const TimeStamp& lhs);
+
+		static int32_t time_difference_in_ms(TimeStamp t1, TimeStamp t2);
+
+	private:
+		cms_t mTime;
+	};
+
+	struct PacketInfo
+	{
+		TimeStamp  mTimeStamp;
+		uint16_t   mPayloadLen;
+		uint8_t    mType;
+		uint8_t    mSubtype;
+		uint16_t   mSrcPort;
+		uint16_t   mDstPort;
+		IPAddress  mSrcAddress;
+		IPAddress  mDstAddress;
+
+		bool update_from_packet(const uint8_t *ipv6_packet);
+		std::string to_string(void) const;
+	};
+
+	struct BytesTotal
+	{
+	public:
+		BytesTotal();
+		void add(uint16_t bytes);
+		void clear(void);
+		std::string to_string(void) const;
+	private:
+		uint16_t mBytes;      // Number of bytes remaining till next Kilo bytes (1024 bytes)
+		uint32_t mKiloBytes;  // Can go up to 2^32 KB which is 4.3 terabytes (> 4 years of continuous exchange at 250 kbps)
+	};
+
+	struct NcpStateInfo
+	{
+	public:
+		void update(NCPState new_state);
+		std::string to_string(void) const;
+		bool is_expired(void) const;
+	private:
+		NCPState mNcpState;
+		TimeStamp mTimeStamp;
+	};
+
+	struct ReadyForHostSleepState
+	{
+	public:
+		void update_with_blocking_sleep_time(TimeStamp blocking_sleep_time);
+		std::string to_string(void) const;
+	private:
+		TimeStamp mStartBlockingHostSleepTime;
+		TimeStamp mReadyForHostSleepTime;
+	};
+
+	class NodeStat
+	{
+	public:
+		struct NodeInfo
+		{
+		public:
+			uint32_t mTxPacketsTotal;
+			uint32_t mTxPacketsUDP;
+			uint32_t mTxPacketsTCP;
+
+			uint32_t mRxPacketsTotal;
+			uint32_t mRxPacketsUDP;
+			uint32_t mRxPacketsTCP;
+
+			RingBuffer<PacketInfo, STAT_COLLECTOR_PER_NODE_RX_HISTORY_SIZE> mRxHistory;
+			RingBuffer<PacketInfo, STAT_COLLECTOR_PER_NODE_TX_HISTORY_SIZE> mTxHistory;
+
+			NodeInfo();
+			void clear();
+			TimeStamp get_last_rx_time(void) const;
+			TimeStamp get_last_tx_time(void) const;
+			TimeStamp get_last_rx_or_tx_time(void) const;
+			void add_rx_stat(StringList& output, bool add_last_rx_time = true) const;
+			void add_tx_stat(StringList& output, bool add_last_tx_time = true) const;
+			void add_node_info(StringList& output) const;
+		};
+
+		NodeStat();
+		void clear(void);
+		void update_from_inbound_packet(const PacketInfo& packet_info);
+		void update_from_outbound_packet(const PacketInfo& packet_info);
+		void add_node_stat(StringList& output) const;
+		void add_node_stat_history(StringList& output, std::string node_indicator = "") const;
+
+	private:
+		NodeInfo *find_node_info(const IPAddress& address);
+		NodeInfo *create_new_node_info(const IPAddress& address);
+		void remove_oldest_node_info(void);
+		void add_node_info_map_iter(StringList &output, const std::map<IPAddress, NodeInfo*>::const_iterator& it) const;
+
+		ObjectPool<NodeInfo, STAT_COLLECTOR_MAX_NODES> mNodeInfoPool;
+		std::map<IPAddress, NodeInfo*> mNodeInfoMap;
+	};
+
+	class LinkStat
+	{
+	public:
+		struct LinkQuality
+		{
+			LinkQuality();
+			void set(int8_t rssi, uint8_t incoming_link_quality, uint8_t outgoing_link_quality);
+			std::string to_string(void) const;
+			TimeStamp get_time_stamp(void) const;
+		private:
+			uint8_t get_incoming_link_quality(void) const;
+			uint8_t get_outgoing_link_quality(void) const;
+
+			int8_t mRssi;
+			uint8_t mLinkQualityIncomingOutgoing;  // High 4 bits are for incoming, low 4 bits are for outgoing
+			TimeStamp mTimeStamp;
+		};
+
+		struct LinkInfo
+		{
+			NodeType mNodeType;
+			RingBuffer<LinkQuality, STAT_COLLECTOR_LINK_QUALITY_HISTORY_SIZE> mLinkQualityHistory;
+
+			LinkInfo();
+			void clear(void);
+			bool empty(void) const;
+			void add_link_info(StringList& output, int count = 0) const;
+			TimeStamp get_last_update_time(void) const;
+		};
+
+		LinkStat();
+		void clear();
+		void update(const uint8_t *eui64_address, int8_t rssi, uint8_t incoming_link_quality, uint8_t outgoing_link_quality,
+			NodeType node_type);
+		void add_link_stat(StringList& output, int count = 0) const;
+
+	private:
+		LinkInfo *find_link_info(const EUI64Address& address);
+		LinkInfo *create_new_link_info(const EUI64Address & address);
+		void remove_oldest_link_info(void);
+
+		ObjectPool<LinkInfo, STAT_COLLECTOR_MAX_LINKS> mLinkInfoPool;
+		std::map<EUI64Address, LinkInfo *> mLinkInfoMap;
+	};
+
+	enum AutoLogState
+	{
+		kAutoLogDisabled,
+		kAutoLogLong,
+		kAutoLogShort
+	};
+
+private:
+	void record_ncp_state_change(NCPState new_ncp_state);
+	void record_ncp_ready_for_host_sleep_state(bool ready_to_sleep);
+	void add_tx_stat(StringList& output) const;
+	void add_rx_stat(StringList& output) const;
+	void add_rx_history(StringList& output, int count = 0) const;
+	void add_tx_history(StringList& output, int count = 0) const;
+	void add_ncp_state_history(StringList& output, int count = 0) const;
+	void add_ncp_ready_for_host_sleep_state_history(StringList& output, int count = 0) const;
+	void add_help(StringList& output) const;
+	void add_all_info(StringList& output, int count = 0) const;
+	int  get_stat_property(const std::string& key, StringList& output) const;
+	void update_auto_log_timer(void);
+	void auto_log_timer_did_fire(void);
+	void update_link_stat_timer(Timer::Interval interval);
+	void link_stat_timer_did_fire(void);
+	void did_get_rip_entry_value_map(int status, const boost::any& value);
+	int  record_rip_entry(const ValueMap& rip_entry);
+	void property_changed(const std::string& key, const boost::any& value);
+
+private:
+	NCPControlInterface *mControlInterface;
+
+	uint32_t mTxPacketsTotal;
+	uint32_t mRxPacketsUDP;
+	uint32_t mRxPacketsTCP;
+	uint32_t mRxPacketsICMP;
+	BytesTotal mTxBytesTotal;
+
+	uint32_t mRxPacketsTotal;
+	uint32_t mTxPacketsUDP;
+	uint32_t mTxPacketsTCP;
+	uint32_t mTxPacketsICMP;
+	BytesTotal mRxBytesTotal;
+
+	RingBuffer<PacketInfo, STAT_COLLECTOR_RX_HISTORY_SIZE> mRxHistory;
+	RingBuffer<PacketInfo, STAT_COLLECTOR_TX_HISTORY_SIZE> mTxHistory;
+
+	RingBuffer<NcpStateInfo, STAT_COLLECTOR_NCP_STATE_HISTORY_SIZE> mNCPStateHistory;
+
+	RingBuffer<ReadyForHostSleepState, STAT_COLLECTOR_NCP_READY_FOR_HOST_SLEEP_STATE_HISTORY_SIZE> mReadyForSleepHistory;
+	bool mLastReadyForHostSleepState;
+	TimeStamp mLastBlockingHostSleepTime;
+
+	NodeStat mNodeStat;
+	LinkStat mLinkStat;
+
+	Timer mAutoLogTimer;
+	Timer mLinkStatTimer;
+
+	Timer::Interval mAutoLogPeriod;
+	enum AutoLogState mAutoLogState;
+
+	int mAutoLogLevel;
+	int mUserRequestLogLevel;
+};
+
+}; // namespace wpantund
+}; // namespace nl
+
+#endif  // defined(wpantund_StatCollector_h)
diff --git a/src/wpantund/wpan-error.h b/src/wpantund/wpan-error.h
new file mode 100644
index 0000000..abb7e39
--- /dev/null
+++ b/src/wpantund/wpan-error.h
@@ -0,0 +1,76 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef wpantund_wpan_error_h
+#define wpantund_wpan_error_h
+#include <stddef.h>
+
+__BEGIN_DECLS
+
+typedef enum {
+	kWPANTUNDStatus_Ok                            = 0,
+	kWPANTUNDStatus_Failure                       = 1,
+
+	kWPANTUNDStatus_InvalidArgument               = 2,
+	kWPANTUNDStatus_InvalidWhenDisabled           = 3,
+	kWPANTUNDStatus_InvalidForCurrentState        = 4,
+	kWPANTUNDStatus_InvalidType                   = 5,
+	kWPANTUNDStatus_InvalidRange                  = 6,
+
+	kWPANTUNDStatus_Timeout                       = 7,
+	kWPANTUNDStatus_SocketReset                   = 8,
+	kWPANTUNDStatus_Busy                          = 9,
+
+	kWPANTUNDStatus_Already                       = 10,
+	kWPANTUNDStatus_Canceled                      = 11,
+	kWPANTUNDStatus_InProgress                    = 12,
+	kWPANTUNDStatus_TryAgainLater                 = 13,
+
+	kWPANTUNDStatus_FeatureNotSupported           = 14,
+	kWPANTUNDStatus_FeatureNotImplemented         = 15,
+
+	kWPANTUNDStatus_PropertyNotFound              = 16,
+	kWPANTUNDStatus_PropertyEmpty                 = 17,
+
+	kWPANTUNDStatus_JoinFailedUnknown             = 18,
+	kWPANTUNDStatus_JoinFailedAtScan              = 19,
+	kWPANTUNDStatus_JoinFailedAtAuthenticate      = 20,
+	kWPANTUNDStatus_FormFailedAtScan              = 21,
+
+	kWPANTUNDStatus_NCP_Crashed                   = 22,
+	kWPANTUNDStatus_NCP_Fatal                     = 23,
+	kWPANTUNDStatus_NCP_InvalidArgument           = 24,
+	kWPANTUNDStatus_NCP_InvalidRange              = 25,
+
+	kWPANTUNDStatus_MissingXPANID                 = 26,
+
+	kWPANTUNDStatus_NCP_Reset                     = 27,
+
+	kWPANTUNDStatus_NCPError_First = 0xEA0000,
+	kWPANTUNDStatus_NCPError_Last = 0xEAFFFF,
+} wpantund_status_t;
+
+#define WPANTUND_NCPERROR_MASK		0xFFFF
+
+#define WPANTUND_STATUS_IS_NCPERROR(x)	((((int)x)&~WPANTUND_NCPERROR_MASK) == kWPANTUNDStatus_NCPError_First)
+#define WPANTUND_NCPERROR_TO_STATUS(x)	(wpantund_status_t)((((int)x)&WPANTUND_NCPERROR_MASK)|kWPANTUNDStatus_NCPError_First)
+#define WPANTUND_STATUS_TO_NCPERROR(x)	((x)&WPANTUND_NCPERROR_MASK)
+
+__END_DECLS
+
+#endif
diff --git a/src/wpantund/wpan-properties.h b/src/wpantund/wpan-properties.h
new file mode 100644
index 0000000..8c7124e
--- /dev/null
+++ b/src/wpantund/wpan-properties.h
@@ -0,0 +1,162 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *      This file contains the enumeration of the properties that can be
+ *      gotten or set via the "get_prop()" and "set_prop()" methods.
+ *
+ */
+
+#ifndef wpantund_wpan_properties_h
+#define wpantund_wpan_properties_h
+
+
+#define kWPANTUNDProperty_ConfigNCPSocketPath              "Config:NCP:SocketPath"
+#define kWPANTUNDProperty_ConfigNCPSocketBaud              "Config:NCP:SocketBaud"
+#define kWPANTUNDProperty_ConfigNCPDriverName              "Config:NCP:DriverName"
+#define kWPANTUNDProperty_ConfigNCPHardResetPath           "Config:NCP:HardResetPath"
+#define kWPANTUNDProperty_ConfigNCPPowerPath               "Config:NCP:PowerPath"
+#define kWPANTUNDProperty_ConfigNCPReliabilityLayer        "Config:NCP:ReliabilityLayer"
+#define kWPANTUNDProperty_ConfigNCPFirmwareCheckCommand    "Config:NCP:FirmwareCheckCommand"
+#define kWPANTUNDProperty_ConfigNCPFirmwareUpgradeCommand  "Config:NCP:FirmwareUpgradeCommand"
+#define kWPANTUNDProperty_ConfigTUNInterfaceName           "Config:TUN:InterfaceName"
+#define kWPANTUNDProperty_ConfigDaemonPIDFile              "Config:Daemon:PIDFile"
+#define kWPANTUNDProperty_ConfigDaemonPrivDropToUser       "Config:Daemon:PrivDropToUser"
+#define kWPANTUNDProperty_ConfigDaemonChroot               "Config:Daemon:Chroot"
+
+#define kWPANTUNDProperty_DaemonVersion                   "Daemon:Version"
+#define kWPANTUNDProperty_DaemonEnabled                   "Daemon:Enabled"
+#define kWPANTUNDProperty_DaemonSyslogMask                "Daemon:SyslogMask"
+#define kWPANTUNDProperty_DaemonTerminateOnFault          "Daemon:TerminateOnFault"
+#define kWPANTUNDProperty_DaemonReadyForHostSleep         "Daemon:ReadyForHostSleep"
+#define kWPANTUNDProperty_DaemonAutoAssociateAfterReset   "Daemon:AutoAssociateAfterReset"
+#define kWPANTUNDProperty_DaemonAutoFirmwareUpdate        "Daemon:AutoFirmwareUpdate"
+#define kWPANTUNDProperty_DaemonAutoDeepSleep             "Daemon:AutoDeepSleep"
+#define kWPANTUNDProperty_DaemonFaultReason               "Daemon:FaultReason"
+
+#define kWPANTUNDProperty_NCPVersion             "NCP:Version"
+#define kWPANTUNDProperty_NCPState               "NCP:State"
+#define kWPANTUNDProperty_NCPHardwareAddress     "NCP:HardwareAddress"
+#define kWPANTUNDProperty_NCPExtendedAddress     "NCP:ExtendedAddress"
+#define kWPANTUNDProperty_NCPChannel             "NCP:Channel"
+#define kWPANTUNDProperty_NCPFrequency           "NCP:Frequency"
+#define kWPANTUNDProperty_NCPTXPower             "NCP:TXPower"
+#define kWPANTUNDProperty_NCPTXPowerLimit        "NCP:TXPowerLimit"
+#define kWPANTUNDProperty_NCPCCAThreshold        "NCP:CCAThreshold"
+#define kWPANTUNDProperty_NCPChannelMask         "NCP:ChannelMask"
+#define kWPANTUNDProperty_NCPSleepyPollInterval  "NCP:SleepyPollInterval"
+#define kWPANTUNDProperty_NCPRSSI                "NCP:RSSI"
+
+#define kWPANTUNDProperty_NetworkName            "Network:Name"
+#define kWPANTUNDProperty_NetworkXPANID          "Network:XPANID"
+#define kWPANTUNDProperty_NetworkPANID           "Network:PANID"
+#define kWPANTUNDProperty_NetworkNodeType        "Network:NodeType"
+#define kWPANTUNDProperty_NetworkKey             "Network:Key"
+#define kWPANTUNDProperty_NetworkKeyIndex        "Network:KeyIndex"
+#define kWPANTUNDProperty_NetworkIsCommissioned  "Network:IsCommissioned"
+
+#define kWPANTUNDProperty_IPv6LinkLocalAddress   "IPv6:LinkLocalAddress"
+#define kWPANTUNDProperty_IPv6MeshLocalAddress   "IPv6:MeshLocalAddress"
+#define kWPANTUNDProperty_IPv6MeshLocalPrefix    "IPv6:MeshLocalPrefix"
+#define kWPANTUNDProperty_IPv6AllAddresses       "IPv6:AllAddresses"
+
+#define kWPANTUNDProperty_ThreadLeaderAddress     "Thread:Leader:Address"
+#define kWPANTUNDProperty_ThreadLeaderRouterID    "Thread:Leader:RouterID"
+#define kWPANTUNDProperty_ThreadLeaderWeight      "Thread:Leader:Weight"
+#define kWPANTUNDProperty_ThreadLeaderLocalWeight "Thread:Leader:LocalWeight"
+#define kWPANTUNDProperty_ThreadNetworkData       "Thread:NetworkData"
+#define kWPANTUNDProperty_ThreadNetworkDataVersion       "Thread:NetworkDataVersion"
+#define kWPANTUNDProperty_ThreadStableNetworkData        "Thread:StableNetworkData"
+#define kWPANTUNDProperty_ThreadStableNetworkDataVersion "Thread:StableNetworkDataVersion"
+
+#define kWPANTUNDProperty_DebugIPv6GlobalIPAddressList         "Debug:IPv6:GlobalIPAddressList"
+
+
+#define kWPANTUNDProperty_NestLabs_NetworkAllowingJoin         "com.nestlabs.internal:Network:AllowingJoin"
+#define kWPANTUNDProperty_NestLabs_NetworkPassthruPort         "com.nestlabs.internal:Network:PassthruPort"
+#define kWPANTUNDProperty_NestLabs_NCPTransmitHookActive       "com.nestlabs.internal:NCP:TransmitHookActive"
+#define kWPANTUNDProperty_NestLabs_LegacyPreferInterface       "com.nestlabs.internal:Legacy:PreferInterface"
+#define kWPANTUNDProperty_NestLabs_LegacyMeshLocalAddress      "com.nestlabs.internal:Legacy:MeshLocalAddress"
+#define kWPANTUNDProperty_NestLabs_LegacyMeshLocalPrefix       "com.nestlabs.internal:Legacy:MeshLocalPrefix"
+#define kWPANTUNDProperty_NestLabs_LegacyEnabled               "com.nestlabs.internal:Legacy:Enabled"
+#define kWPANTUNDProperty_NestLabs_NetworkWakeData             "com.nestlabs.internal:NetworkWake:Data"
+#define kWPANTUNDProperty_NestLabs_NetworkWakeRemaining        "com.nestlabs.internal:NetworkWake:Remaining"
+#define kWPANTUNDProperty_NestLabs_NetworkWakeBlacklist        "com.nestlabs.internal:NetworkWake:Blacklist"
+#define kWPANTUNDProperty_NestLabs_HackUseDeepSleepOnLowPower  "com.nestlabs.internal:Hack:UseDeepSleepOnLowPower"
+#define kWPANTUNDProperty_NestLabs_HackAlwaysResetToWake       "com.nestlabs.internal:Hack:AlwaysResetToWake"
+
+#define kWPANTUNDProperty_Stat_Prefix                "Stat:"
+#define kWPANTUNDProperty_StatRX                     "Stat:RX"
+#define kWPANTUNDProperty_StatTX                     "Stat:TX"
+#define kWPANTUNDProperty_StatRXHistory              "Stat:RX:History"
+#define kWPANTUNDProperty_StatTXHistory              "Stat:TX:History"
+#define kWPANTUNDProperty_StatHistory                "Stat:History"
+#define kWPANTUNDProperty_StatNCP                    "Stat:NCP"
+#define kWPANTUNDProperty_StatBlockingHostSleep      "Stat:BlockingHostSleep"
+#define kWPANTUNDProperty_StatNode                   "Stat:Node"
+#define kWPANTUNDProperty_StatNodeHistory            "Stat:Node:History"
+#define kWPANTUNDProperty_StatNodeHistoryID          "Stat:Node:History:"
+#define kWPANTUNDProperty_StatShort                  "Stat:Short"
+#define kWPANTUNDProperty_StatLong                   "Stat:Long"
+#define kWPANTUNDProperty_StatAutoLog                "Stat:AutoLog"
+#define kWPANTUNDProperty_StatAutoLogState           "Stat:AutoLog:State"
+#define kWPANTUNDProperty_StatAutoLogPeriod          "Stat:AutoLog:Period"
+#define kWPANTUNDProperty_StatAutoLogLogLevel        "Stat:AutoLog:LogLevel"
+#define kWPANTUNDProperty_StatUserLogRequestLogLevel "Stat:UserRequest:LogLevel"
+#define kWPANTUNDProperty_StatLinkQuality            "Stat:LinkQuality"
+#define kWPANTUNDProperty_StatLinkQualityLong        "Stat:LinkQuality:Long"
+#define kWPANTUNDProperty_StatLinkQualityShort       "Stat:LinkQuality:Short"
+#define kWPANTUNDProperty_StatLinkQualityPeriod      "Stat:LinkQuality:Period"
+#define kWPANTUNDProperty_StatHelp                   "Stat:Help"
+
+// ----------------------------------------------------------------------------
+
+#define kWPANTUNDNodeType_Unknown          "unknown"
+#define kWPANTUNDNodeType_Router           "router"
+#define kWPANTUNDNodeType_EndDevice        "end-device"
+#define kWPANTUNDNodeType_SleepyEndDevice  "sleepy-end-device"
+#define kWPANTUNDNodeType_NestLurker       "nl-lurker"
+#define kWPANTUNDNodeType_Commissioner     "commissioner"
+#define kWPANTUNDNodeType_Leader           "leader"
+
+// ----------------------------------------------------------------------------
+
+// When querying the value of the association state property,
+// the returned value will be a human-readable string. Compare
+// it with one of the constants below to get the exact meaning.
+#define kWPANTUNDStateUninitialized            "uninitialized"
+#define kWPANTUNDStateFault                    "uninitialized:fault"
+#define kWPANTUNDStateUpgrading                "uninitialized:upgrading"
+#define kWPANTUNDStateDeepSleep                "offline:deep-sleep"
+#define kWPANTUNDStateOffline                  "offline"
+#define kWPANTUNDStateCommissioned             "offline:commissioned"
+#define kWPANTUNDStateAssociating              "associating"
+#define kWPANTUNDStateCredentialsNeeded        "associating:credentials-needed"
+#define kWPANTUNDStateAssociated               "associated"
+#define kWPANTUNDStateIsolated                 "associated:no-parent"
+#define kWPANTUNDStateNetWake_Asleep           "associated:netwake-asleep"
+#define kWPANTUNDStateNetWake_Waking           "associated:netwake-waking"
+
+// ----------------------------------------------------------------------------
+
+// Values of  the property kWPANTUNDProperty_StatAutoLogState
+#define kWPANTUNDStatAutoLogState_Disabled          "disabled"
+#define kWPANTUNDStatAutoLogState_Long              "long"
+#define kWPANTUNDStatAutoLogState_Short             "short"
+
+
+#endif
diff --git a/src/wpantund/wpantund.conf b/src/wpantund/wpantund.conf
new file mode 100644
index 0000000..1d98db8
--- /dev/null
+++ b/src/wpantund/wpantund.conf
@@ -0,0 +1,154 @@
+# Example wpantund configuration file
+#
+
+# The desired name of the network interface (not supported on all platforms)
+# Default value is `wpan0`.
+#
+#Config:TUN:InterfaceName wpan0
+
+# Path to serial port used to communicate with the NCP.
+# Has special meaning when prefixed with `system:` or `serial:`.
+# If the path is an IPv4 address/port, it will use a TCP socket.
+#
+#Config:NCP:SocketPath "/dev/tty.usbmodem1234"
+#Config:NCP:SocketPath "127.0.0.1:4901"
+#Config:NCP:SocketPath "system:/usr/local/sbin/spi-server -p - -s /dev/spidev2.0"
+#Config:NCP:SocketPath "serial:/dev/ttyO1,raw,b115200,crtscts=1"
+
+# The desired NCP driver to use.
+# Default value is `spinel`.
+#
+#Config:NCP:DriverName spinel
+
+# Describes what type of serial reliability layer should be used, if any.
+# Generally this is set appropriately automatically, so you usually
+# don't need to bother setting this.
+#
+# Optional. Default value is automatic.
+#
+#Config:NCP:ReliabilityLayer libsoot
+
+# The default transmit power of the NCP, measured in dBm
+# (0 dBm is one milliwatt, can be negative)
+#
+# Optional. Default value is NCP-specific.
+#
+#NCP:TXPower 0
+
+# The CCA threshold value, measured in dBm
+#
+# Optional. Default value is NCP-specific.
+#
+#NCP:CCAThreshold -70
+
+# Path to reset pin. This socket is opened and values sent to it
+# based on the desired reset state. wpantund sends `0\n` to
+# assert the reset pin and sends `1\n` to deassert the reset pin.
+# Can be used with the same `system:` and `serial:` prefixes defined
+# above to handle special cases. On linux, you would generally
+# set this to the `value` file of the RESET GPIO.
+#
+# Optional. If not set, only software resets will be possible.
+#
+#Config:NCP:HardResetPath "/sys/class/gpio/gpio49/value"
+
+# Path to power pin. The semantics are similar to those for
+# `NCPHardResetPath` above, except that wpantund sends `0\n` to
+# indicate that the NCP power should be disabled and `1\n` to
+# indicate that the NCP power should be enabled.
+#
+# Optional. If not set, only software induced sleep will be used
+# for low-power states.
+#
+#Config:NCP:PowerPath "/sys/class/gpio/gpio11/value"
+
+# Syslog mask adjustment. This property is a set of
+# boolean masks for manipulating the bitmask used by `syslog()`.
+# The string can contain the following words that represent
+# the associated bit. The presence of the word in the string
+# indicates that that bit should be set. Prefixing the word with
+# a `-` (dash/minus) indicates that that bit should be cleared.
+# The following keywords are supported:
+#
+# * `all` (All log levels)
+# * `emerg`
+# * `alert`
+# * `crit`
+# * `err`
+# * `warn`
+# * `notice`
+# * `info`
+# * `debug`
+#
+# So, for example, to get all log messages except debugging
+# messages, you would use `all -debug`.
+#
+# Optional. The default value for non-debug builds is
+# `all -info -debug`.
+#
+#Daemon:SyslogMask "all -info -debug"
+
+# Drop root privileges to the given user (and that user's group)
+# after setting up all network interfaces and socket connections.
+# Doing this helps mitigate the implications of security exploits,
+# but may interfere with how hard resets are performed for certain
+# NCPs.
+#
+# Optional. Default value is empty, which means that privileges
+# are not dropped.
+#
+#Config:Daemon:PrivDropToUser "nobody"
+
+# Call `chroot()` to change the root directory to the directory
+# indicated, after setting up all network interfaces and socket
+# connections, but before privileges are dropped for `PrivDropToUser`
+# (if set). Doing this helps mitigate the implications of security
+# exploits, but may interfere with how hard resets are performed
+# for certain NCPs.
+#
+# Optional. Default value is empty, which means that `chroot` is
+# not called.
+#
+#Config:Daemon:Chroot "/var/empty"
+
+# Automatic firmware update enable/disable. This flag determines
+# if the automatic firmware update mechanism (which uses the
+# properties `FirmwareCheckCommand` and `FirmwareUpgradeCommand`,
+# described below) is enabled or disabled. This flag is used
+# when the driver determines that it doesn't know how to talk to
+# the version of the firmware that is running on the NCP.
+#
+# If set to true, the NCP association state will then be set to
+# `upgrading` and the firmware update process will begin. If set to
+# false, the NCP association state will change to `fault`.
+#
+# This property may be changed at runtime to more strictly
+# control when a firmware update is allowed to take place.
+#
+# Optional. The default value is false---which will prevent
+# automatic wpantund-driven firmware updates.
+#
+#Daemon:AutoFirmwareUpdate true
+
+# Firmware update check command. This command is executed with
+# the retrieved version string of the NCP appended as the last
+# argument. If the command returns `0`, a firmware update is
+# necessary. This configuration option is fairly useless without
+# the `FirmwareUpgradeCommand` option also being set, defined below.
+#
+# Optional. If left blank, wpantund-driven firmware updates will
+# be disabled.
+#
+#Config:NCP:FirmwareCheckCommand "test 'MyFunStack/1.0' !="
+#Config:NCP:FirmwareCheckCommand "/usr/local/sbin/zb-loader --is-update-required /usr/share/ncp-firmware/ip-modem-app.bin"
+
+# Firmware upgrade command. This is the command that is actually
+# executed that performs the firmware update. Before calling this
+# command, `wpantund` disconnects entirely from the NCP. While
+# this command is executing, the association state reported
+# for this interface will be `upgrading`.
+#
+# Optional. If left blank, wpantund-driven firmware updates will
+# be disabled.
+#
+#Config:NCP:FirmwareUpgradeCommand "/usr/local/sbin/zb-loader /dev/ttyO1 --app-easyload /usr/share/ncp-firmware/ip-modem-app.bin"
diff --git a/src/wpantund/wpantund.cpp b/src/wpantund/wpantund.cpp
new file mode 100644
index 0000000..623a965
--- /dev/null
+++ b/src/wpantund/wpantund.cpp
@@ -0,0 +1,884 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *    Description:
+ *		This file implements the main program entry point for the
+ *		WPAN Tunnel Driver, masterfuly named `wpantund`.
+ *
+ */
+
+#define DEBUG 1
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef ASSERT_MACROS_USE_SYSLOG
+#define ASSERT_MACROS_USE_SYSLOG 1
+#endif
+
+#ifndef ASSERT_MACROS_SQUELCH
+#define ASSERT_MACROS_SQUELCH 0
+#endif
+
+#include "assert-macros.h"
+
+#include "wpantund.h"
+
+#include "CallbackStore.hpp"
+#include "config-file.h"
+#include "args.h"
+#include "pt.h"
+#include "tunnel.h"
+#include "socket-utils.h"
+#include "string-utils.h"
+#include "version.h"
+#include "SuperSocket.h"
+#include "Timer.h"
+
+#include "DBUSIPCServer.h"
+#include "NCPControlInterface.h"
+#include "NCPInstance.h"
+
+#include "nlpt.h"
+
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <unistd.h>
+#include <signal.h>
+
+#include <poll.h>
+#include <sys/select.h>
+
+#include <syslog.h>
+#include <errno.h>
+#include <libgen.h>
+
+#include <exception>
+#include <algorithm>
+
+#include "any-to.h"
+
+#if HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#ifndef PREFIX
+#define PREFIX "/usr/local"
+#endif
+
+#ifndef SYSCONFDIR
+#define SYSCONFDIR PREFIX "/etc"
+#endif
+
+#ifndef DEFAULT_MAX_LOG_LEVEL
+#if DEBUG
+#define DEFAULT_MAX_LOG_LEVEL	LOG_INFO
+#else
+#define DEFAULT_MAX_LOG_LEVEL	LOG_NOTICE
+#endif
+#endif
+
+#ifndef WPANTUND_DEFAULT_PRIV_DROP_USER
+#define WPANTUND_DEFAULT_PRIV_DROP_USER     NULL
+#endif
+
+#ifndef WPANTUND_DEFAULT_CHROOT_PATH
+#define WPANTUND_DEFAULT_CHROOT_PATH        NULL
+#endif
+
+#ifndef WPANTUND_BACKTRACE
+#define WPANTUND_BACKTRACE	(HAVE_EXECINFO_H || __APPLE__)
+#endif
+
+#if WPANTUND_BACKTRACE
+#include <execinfo.h>
+#if HAVE_ASM_SIGCONTEXT
+#include <asm/sigcontext.h>
+#endif
+#ifndef FAULT_BACKTRACE_STACK_DEPTH
+#define FAULT_BACKTRACE_STACK_DEPTH		20
+#endif
+#endif
+
+#ifndef SOURCE_VERSION
+#define SOURCE_VERSION		PACKAGE_VERSION
+__BEGIN_DECLS
+#include "version.c.in"
+__END_DECLS
+#endif
+
+using namespace nl;
+using namespace wpantund;
+
+static arg_list_item_t option_list[] = {
+	{ 'h', "help",   NULL,	"Print Help"},
+	{ 'd', "debug",  "<level>", "Enable debugging mode"},
+	{ 'c', "config", "<filename>", "Config File"},
+	{ 'o', "option", "<option-string>", "Config option"},
+	{ 'I', "interface", "<iface>", "Network interface name"},
+	{ 's', "socket", "<socket>", "Socket file"},
+	{ 'b', "baudrate", "<integer>", "Baudrate"},
+	{ 'v', "version", NULL, "Print version" },
+#if HAVE_PWD_H
+	{ 'u', "user", NULL, "Username for dropping privileges" },
+#endif
+	{ 0 }
+};
+
+static int gRet;
+
+static const char* gProcessName = "wpantund";
+static const char* gPIDFilename = NULL;
+static const char* gChroot = WPANTUND_DEFAULT_CHROOT_PATH;
+
+#if HAVE_PWD_H
+static const char* gPrivDropToUser = WPANTUND_DEFAULT_PRIV_DROP_USER;
+#endif
+
+/* ------------------------------------------------------------------------- */
+/* MARK: Signal Handlers */
+
+static sig_t gPreviousHandlerForSIGINT;
+static sig_t gPreviousHandlerForSIGTERM;
+
+static void
+signal_SIGINT(int sig)
+{
+	gRet = ERRORCODE_INTERRUPT;
+	syslog(LOG_NOTICE, "Caught SIGINT!");
+
+	// Restore the previous handler so that if we end up getting
+	// this signal again we peform the system default action.
+	signal(SIGINT, gPreviousHandlerForSIGINT);
+	gPreviousHandlerForSIGINT = NULL;
+}
+
+static void
+signal_SIGTERM(int sig)
+{
+	gRet = ERRORCODE_QUIT;
+	syslog(LOG_NOTICE, "Caught SIGTERM!");
+
+	// Restore the previous handler so that if we end up getting
+	// this signal again we peform the system default action.
+	signal(SIGTERM, gPreviousHandlerForSIGTERM);
+	gPreviousHandlerForSIGTERM = NULL;
+}
+
+static void
+signal_SIGHUP(int sig)
+{
+	gRet = ERRORCODE_SIGHUP;
+	syslog(LOG_NOTICE, "Caught SIGHUP!");
+
+	// We don't restore the "previous handler"
+	// because we always want to let the main
+	// loop decide what to do for hangups.
+}
+
+static void
+signal_critical(int sig, siginfo_t * info, void * ucontext)
+{
+	// This is the last hurah for this process.
+	// We dump the stack, because that's all we can do.
+
+#if WPANTUND_BACKTRACE
+	void *stack_mem[FAULT_BACKTRACE_STACK_DEPTH];
+	void **stack = stack_mem;
+	char **stack_symbols;
+	int stack_depth, i;
+	ucontext_t *uc = (ucontext_t*)ucontext;
+
+	// Shut up compiler warning.
+	(void)uc;
+
+#endif // WPANTUND_BACKTRACE
+
+	fprintf(stderr, " *** FATAL ERROR: Caught signal %d (%s):\n", sig, strsignal(sig));
+
+#if WPANTUND_BACKTRACE
+	stack_depth = backtrace(stack, FAULT_BACKTRACE_STACK_DEPTH);
+
+#if __APPLE__
+	// OS X adds an extra call onto the stack that
+	// we can leave out for clarity sake.
+	stack[1] = stack[0];
+	stack++;
+	stack_depth--;
+#endif
+
+	// Here are are trying to update the pointer in the backtrace
+	// to be the actual location of the fault.
+#if __linux__
+#if defined(__x86_64__)
+	stack[1] = (void *) uc->uc_mcontext.gregs[REG_RIP];
+#elif defined(__arm__)
+	stack[1] = (void *) uc->uc_mcontext.arm_ip;
+#else
+#warning TODO: Add this arch to signal_critical
+#endif
+
+#elif __APPLE__ // #if __linux__
+#if defined(__x86_64__)
+	stack[1] = (void *) uc->uc_mcontext->__ss.__rip;
+#endif
+
+#else //#elif __APPLE__
+#warning TODO: Add this OS to signal_critical
+#endif
+
+	// Now dump the symbols to stderr, in case syslog barfs.
+	backtrace_symbols_fd(stack, stack_depth, STDERR_FILENO);
+
+	// Load up the symbols individually, so we can output to syslog, too.
+	stack_symbols = backtrace_symbols(stack, stack_depth);
+#endif // WPANTUND_BACKTRACE
+
+	syslog(LOG_CRIT, " *** FATAL ERROR: Caught signal %d (%s):", sig, strsignal(sig));
+
+#if WPANTUND_BACKTRACE
+	for(i = 0; i != stack_depth; i++) {
+#if __APPLE__
+		syslog(LOG_CRIT, "[BT] %s", stack_symbols[i]);
+#else
+		syslog(LOG_CRIT, "[BT] %2d: %s", i, stack_symbols[i]);
+#endif
+	}
+#endif // WPANTUND_BACKTRACE
+
+	free(stack_symbols);
+
+	exit(EXIT_FAILURE);
+}
+
+/* ------------------------------------------------------------------------- */
+/* MARK: Misc. */
+
+// TODO: Refactor and clean up.
+static int
+set_config_param(
+    void* ignored, const char* key, const char* value
+    )
+{
+	int ret = -1;
+
+	syslog(LOG_INFO, "set-config-param: \"%s\" = \"%s\"", key, value);
+
+	if (strcaseequal(key, kWPANTUNDProperty_ConfigNCPSocketBaud)) {
+		int baud = atoi(value);
+		ret = 0;
+		require(9600 <= baud, bail);
+		gSocketWrapperBaud = baud;
+#if HAVE_PWD_H
+	} else if (strcaseequal(key, kWPANTUNDProperty_ConfigDaemonPrivDropToUser)) {
+		if (value[0] == 0) {
+			gPrivDropToUser = NULL;
+		} else {
+			gPrivDropToUser = strdup(value);
+		}
+		ret = 0;
+#endif
+	} else if (strcaseequal(key, kWPANTUNDProperty_ConfigDaemonChroot)) {
+		if (value[0] == 0) {
+			gChroot = NULL;
+		} else {
+			gChroot = strdup(value);
+		}
+		ret = 0;
+	} else if (strcaseequal(key, kWPANTUNDProperty_DaemonSyslogMask)) {
+		setlogmask(strtologmask(value, setlogmask(0)));
+		ret = 0;
+	} else if (strcaseequal(key, kWPANTUNDProperty_ConfigDaemonPIDFile)) {
+		if (gPIDFilename)
+			goto bail;
+		gPIDFilename = strdup(value);
+		unlink(gPIDFilename);
+		FILE* pidfile = fopen(gPIDFilename, "w");
+		if (!pidfile) {
+			syslog(LOG_ERR, "Unable to open PID file \"%s\".", gPIDFilename);
+			goto bail;
+		}
+		fclose(pidfile);
+		ret = 0;
+	}
+
+bail:
+	if (ret == 0) {
+		syslog(LOG_INFO, "set-config-param: \"%s\" set succeded", key);
+	}
+	return ret;
+}
+
+// Used with `read_config()` to collect configuration settings into a map.
+static int
+add_to_map(
+    void* context,
+	const char* key,
+	const char* value
+) {
+	std::map<std::string, std::string> *map = static_cast<std::map<std::string, std::string>*>(context);
+	(*map)[key] = value;
+	return 0;
+}
+
+static void
+handle_error(int err)
+{
+	gRet = err;
+}
+
+std::string
+nl::wpantund::get_wpantund_version_string(void)
+{
+	std::string version = PACKAGE_VERSION;
+
+	if ((internal_build_source_version[0] == 0) || strequal(SOURCE_VERSION, internal_build_source_version)) {
+		// Ignore any coverty warnings about the following line being pointless.
+		// I assure you, it is not pointless.
+		if (strequal(PACKAGE_VERSION, SOURCE_VERSION)) {
+			version += " (";
+			version += internal_build_date;
+			version += ")";
+		} else {
+			version += " (";
+			version += SOURCE_VERSION;
+			version += "; ";
+			version += internal_build_date;
+			version += ")";
+		}
+	} else {
+		// Ignore any coverty warnings about the following line being pointless.
+		// I assure you, it is not pointless.
+		if (strequal(SOURCE_VERSION, PACKAGE_VERSION) || strequal(PACKAGE_VERSION, internal_build_source_version)) {
+			version += " (";
+			version += internal_build_source_version;
+			version += "; ";
+			version += internal_build_date;
+			version += ")";
+		} else {
+			version += " (";
+			version += SOURCE_VERSION;
+			version += "/";
+			version += internal_build_source_version;
+			version += "; ";
+			version += internal_build_date;
+			version += ")";
+		}
+	}
+	return version;
+}
+
+static void
+print_version()
+{
+	printf("wpantund %s\n", get_wpantund_version_string().c_str() );
+}
+
+/* ------------------------------------------------------------------------- */
+/* MARK: NLPT Hooks */
+
+static fd_set gReadableFDs;
+static fd_set gWritableFDs;
+static fd_set gErrorableFDs;
+
+bool
+nlpt_hook_check_read_fd_source(struct nlpt* nlpt, int fd)
+{
+
+	bool ret = false;
+	if (fd >= 0) {
+		ret = FD_ISSET(fd, &gReadableFDs) || FD_ISSET(fd, &gErrorableFDs);
+		FD_CLR(fd, &gReadableFDs);
+		FD_CLR(fd, &gErrorableFDs);
+	}
+	return ret;
+}
+
+bool
+nlpt_hook_check_write_fd_source(struct nlpt* nlpt, int fd)
+{
+	bool ret = false;
+	if (fd >= 0) {
+		ret = FD_ISSET(fd, &gWritableFDs) || FD_ISSET(fd, &gErrorableFDs);
+		FD_CLR(fd, &gWritableFDs);
+		FD_CLR(fd, &gErrorableFDs);
+	}
+	return ret;
+}
+
+static void
+syslog_dump_select_info(int loglevel, fd_set *read_fd_set, fd_set *write_fd_set, fd_set *error_fd_set, int fd_count, cms_t timeout)
+{
+#define DUMP_FD_SET(l, x) do {\
+		int i; \
+		std::string buffer; \
+		for (i = 0; i < fd_count; i++) { \
+			if (!FD_ISSET(i, x)) { \
+				continue; \
+			} \
+			if (buffer.size() != 0) { \
+				buffer += ", "; \
+			} \
+			char str[10]; \
+			snprintf(str, sizeof(str), "%d", i); \
+			buffer += str; \
+		} \
+		syslog(l, "SELECT:     %s: %s", #x, buffer.c_str()); \
+	} while (0)
+
+	// Check the log level preemptively to avoid wasted CPU.
+	if((setlogmask(0)&LOG_MASK(loglevel))) {
+		syslog(loglevel, "SELECT: fd_count=%d cms_timeout=%d", fd_count, timeout);
+
+		DUMP_FD_SET(loglevel, read_fd_set);
+		DUMP_FD_SET(loglevel, write_fd_set);
+		//DUMP_FD_SET(loglevel, error_fd_set); // Commented out to reduce log volume
+	}
+}
+
+/* ------------------------------------------------------------------------- */
+/* MARK: Main Function */
+
+int
+main(int argc, char * argv[])
+{
+	int c;
+	int fds_ready = 0;
+	bool interface_added = false;
+	int zero_cms_in_a_row_count = 0;
+	const char* config_file = SYSCONFDIR "/wpantund.conf";
+	const char* alt_config_file = SYSCONFDIR "/wpan-tunnel-driver.conf";
+	nl::wpantund::IPCServer *ipc_server = NULL;
+	nl::wpantund::NCPInstance *ncp_instance = NULL;
+	std::map<std::string, std::string> cmd_line_settings;
+
+	// ========================================================================
+	// INITIALIZATION and ARGUMENT PARSING
+
+	gPreviousHandlerForSIGINT = signal(SIGINT, &signal_SIGINT);
+	gPreviousHandlerForSIGTERM = signal(SIGTERM, &signal_SIGTERM);
+	signal(SIGHUP, &signal_SIGHUP);
+
+	{
+		struct sigaction sigact;
+		sigact.sa_sigaction = &signal_critical;
+		sigact.sa_flags = SA_RESTART | SA_SIGINFO | SA_NOCLDWAIT;
+
+		sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL);
+		sigaction(SIGBUS, &sigact, (struct sigaction *)NULL);
+		sigaction(SIGILL, &sigact, (struct sigaction *)NULL);
+		sigaction(SIGABRT, &sigact, (struct sigaction *)NULL);
+	}
+
+	openlog(basename(argv[0]), LOG_PERROR | LOG_PID | LOG_CONS, LOG_DAEMON);
+
+	// Temper the amount of logging.
+	setlogmask(setlogmask(0) & LOG_UPTO(DEFAULT_MAX_LOG_LEVEL));
+
+	gRet = ERRORCODE_UNKNOWN;
+
+	if (argc && argv[0][0]) {
+		gProcessName = basename(argv[0]);
+	}
+	optind = 0;
+	while(1) {
+		static struct option long_options[] =
+		{
+			{"help",	no_argument,		0,	'h'},
+			{"version",	no_argument,		0,	'v'},
+			{"debug",	no_argument,		0,	'd'},
+			{"config",	required_argument,	0,	'c'},
+			{"option",	required_argument,	0,	'o'},
+			{"interface",	required_argument,	0,	'I'},
+			{"socket",	required_argument,	0,	's'},
+			{"baudrate",	required_argument,	0,	'b'},
+			{"user",	required_argument,	0,	'u'},
+			{0,		0,			0,	0}
+		};
+
+		int option_index = 0;
+		c = getopt_long(argc, argv, "hvd:c:o:I:s:b:u:", long_options,
+			&option_index);
+
+		if (c == -1)
+			break;
+
+		switch(c) {
+		case 'h':
+			print_arg_list_help(option_list, argv[0], "[options]");
+			gRet = ERRORCODE_HELP;
+			goto bail;
+
+		case 'v':
+			print_version();
+			gRet = 0;
+			goto bail;
+
+		case 'd':
+			setlogmask(~0);
+			break;
+
+		case 'c':
+			config_file = optarg;
+			break;
+
+		case 'I':
+			cmd_line_settings[kWPANTUNDProperty_ConfigTUNInterfaceName] = optarg;
+			break;
+
+		case 's':
+			cmd_line_settings[kWPANTUNDProperty_ConfigNCPSocketPath] = optarg;
+			break;
+
+		case 'b':
+			cmd_line_settings[kWPANTUNDProperty_ConfigNCPSocketBaud] = optarg;
+			break;
+
+		case 'u':
+			cmd_line_settings[kWPANTUNDProperty_ConfigDaemonPrivDropToUser] = optarg;
+			break;
+
+		case 'o':
+			if ((optind >= argc) || (strncmp(argv[optind], "-", 1) == 0)) {
+				syslog(LOG_ERR, "Missing argument to '-o'.");
+				gRet = ERRORCODE_BADARG;
+				goto bail;
+			}
+			char *key = optarg;
+			char *value = argv[optind];
+			optind++;
+
+			// We handle this option after we try loading the configuration
+			// file, so that command-line specified settings can override
+			// settings read from the configuration file.
+			cmd_line_settings[key] = value;
+			break;
+		}
+	}
+
+	if (optind < argc) {
+		fprintf(stderr,
+		        "%s: error: Unexpected extra argument: \"%s\"\n",
+		        argv[0],
+		        argv[optind]);
+		gRet = ERRORCODE_BADARG;
+		goto bail;
+	}
+
+	// ========================================================================
+	// STARTUP
+
+	syslog(LOG_NOTICE, "Starting %s " PACKAGE_VERSION " (%s) . . .", gProcessName, internal_build_date);
+
+	if (("" SOURCE_VERSION)[0] != 0) {
+		syslog(LOG_NOTICE, "\tSOURCE_VERSION = " SOURCE_VERSION);
+	}
+
+	if (internal_build_source_version[0] != 0) {
+		syslog(LOG_NOTICE, "\tBUILD_VERSION = %s", internal_build_source_version);
+	}
+
+	if (getuid() != 0) {
+		// Warn people if we aren't root.
+		syslog(LOG_WARNING, "wpantund was not started as 'root'! If wpantund fails immediately, this is probably why.");
+	}
+
+	try {
+		std::map<std::string, std::string> settings;
+
+		// Read the configuration file into the settings map.
+		if (0 == read_config(config_file, &add_to_map, &settings)) {
+			syslog(LOG_NOTICE, "Configuration file \"%s\" read.", config_file);
+		} else if (0 == read_config(alt_config_file, &add_to_map, &settings)) {
+			syslog(LOG_NOTICE, "Configuration file \"%s\" read.", alt_config_file);
+		} else {
+			syslog(LOG_WARNING, "Configuration file \"%s\" not found, will use defaults.", config_file);
+		}
+
+		// Command line settings override configuration file settings.
+		// This is weird because the `insert()` method doesn't replace
+		// a key value pair if it already exists, so to get the desired
+		// behavior, we insert into `cmd_line_settings`, not `settings`.
+		cmd_line_settings.insert(settings.begin(), settings.end());
+
+		// Perform depricated property translation
+		{
+			std::map<std::string, std::string>::const_iterator iter;
+			settings.clear();
+
+			for (iter = cmd_line_settings.begin(); iter != cmd_line_settings.end(); iter++) {
+				std::string key(iter->first);
+				boost::any value(iter->second);
+				if (NCPControlInterface::translate_deprecated_property(key, value)) {
+					if (key.empty()) {
+						syslog(LOG_WARNING, "Configuration property \"%s\" is no longer supported. Please remove it from your configuration.", iter->first.c_str());
+					} else {
+						syslog(LOG_WARNING, "CONFIGURATION PROPERTY \"%s\" IS DEPRECATED. Please use \"%s\" instead.", iter->first.c_str(), key.c_str());
+					}
+				}
+				if (!key.empty()) {
+					settings[key] = any_to_string(value);
+				}
+			}
+		}
+
+		// Handle all of the options/settings.
+		if (!settings.empty()) {
+			std::map<std::string, std::string>::const_iterator iter;
+			std::map<std::string, std::string> settings_for_ncp_control_interface;
+
+			for(iter = settings.begin(); iter != settings.end(); iter++) {
+				if (set_config_param(NULL, iter->first.c_str(), iter->second.c_str())) {
+					// If set_config_param() doesn't like the argument,
+					// we hold onto it for now so we can try passing it as
+					// a property to the NCP instance.
+					settings_for_ncp_control_interface[iter->first] = iter->second;
+				}
+			}
+			settings = settings_for_ncp_control_interface;
+		}
+
+		//require_string(settings.count(kWPANTUNDProperty_ConfigNCPSocketPath) == 1, bail, "Missing "kWPANTUNDProperty_ConfigNCPSocketPath);
+
+		ipc_server = new DBUSIPCServer();
+
+		ncp_instance = NCPInstance::alloc(settings);
+
+		require_string(ncp_instance != NULL, bail, "Unable to create NCPInstance");
+
+		ncp_instance->mOnFatalError.connect(&handle_error);
+
+		ncp_instance->get_stat_collector().set_ncp_control_interface(&ncp_instance->get_control_interface());
+
+	} catch(std::runtime_error x) {
+		syslog(LOG_ERR, "Runtime error thrown while starting up, \"%s\"",x.what());
+		ncp_instance = NULL;
+		goto bail;
+
+	} catch(std::exception x) {
+		syslog(LOG_ERR, "Exception thrown while starting up, \"%s\"",x.what());
+		ncp_instance = NULL;
+		goto bail;
+	}
+
+	// ========================================================================
+	// Dropping Privileges
+
+	if (gChroot != NULL) {
+		if (getuid() == 0) {
+			if (chdir(gChroot) != 0) {
+				syslog(LOG_CRIT, "chdir: %s", strerror(errno));
+				gRet = ERRORCODE_ERRNO;
+				goto bail;
+			}
+
+			if (chroot(gChroot) != 0) {
+				syslog(LOG_CRIT, "chroot: %s", strerror(errno));
+				gRet = ERRORCODE_ERRNO;
+				goto bail;
+			} else {
+				syslog(LOG_INFO, "Successfully changed root directory to \"%s\".", gChroot);
+			}
+		} else {
+			syslog(LOG_WARNING, "Not running as root, cannot chroot");
+		}
+	}
+
+#if HAVE_PWD_H
+	if (getuid() == 0) {
+		uid_t target_uid = 0;
+		gid_t target_gid = 0;
+
+		if (gPrivDropToUser != NULL) {
+			struct passwd *passwd = getpwnam(gPrivDropToUser);
+
+			if (passwd == NULL) {
+				syslog(LOG_CRIT, "getpwnam: Unable to lookup user \"%s\", cannot drop privileges.", gPrivDropToUser);
+				gRet = ERRORCODE_ERRNO;
+				goto bail;
+			}
+
+			target_uid = passwd->pw_uid;
+			target_gid = passwd->pw_gid;
+		}
+
+		if (target_gid != 0) {
+			if (setgid(target_gid) != 0) {
+				syslog(LOG_CRIT, "setgid: Unable to drop group privileges: %s", strerror(errno));
+				gRet = ERRORCODE_ERRNO;
+				goto bail;
+			} else {
+				syslog(LOG_INFO, "Group privileges dropped to GID:%d", (int)target_gid);
+			}
+		}
+
+		if (target_uid != 0) {
+			if (setuid(target_uid) != 0) {
+				syslog(LOG_CRIT, "setuid: Unable to drop user privileges: %s", strerror(errno));
+				gRet = ERRORCODE_ERRNO;
+				goto bail;
+			} else {
+				syslog(LOG_INFO, "User privileges dropped to UID:%d", (int)target_uid);
+			}
+		}
+
+		if ((target_gid == 0) || (target_uid == 0)) {
+			syslog(LOG_NOTICE, "Running as root without dropping privileges!");
+		}
+	} else if (gPrivDropToUser != NULL) {
+		syslog(LOG_NOTICE, "Not running as root, skipping dropping privileges");
+	}
+#endif // #if HAVE_PWD_H
+
+	// ========================================================================
+	// MAIN LOOP
+
+	gRet = 0;
+
+	while (!gRet) {
+		const cms_t max_main_loop_timeout(CMS_DISTANT_FUTURE);
+		cms_t cms_timeout(max_main_loop_timeout);
+		int max_fd(-1);
+		struct timeval timeout;
+
+		FD_ZERO(&gReadableFDs);
+		FD_ZERO(&gWritableFDs);
+		FD_ZERO(&gErrorableFDs);
+
+		// Update the FD masks and timeouts
+		ncp_instance->update_fd_set(&gReadableFDs, &gWritableFDs, &gErrorableFDs, &max_fd, &cms_timeout);
+		ipc_server->update_fd_set(&gReadableFDs, &gWritableFDs, &gErrorableFDs, &max_fd, &cms_timeout);
+		Timer::update_timeout(&cms_timeout);
+
+		require_string(max_fd < FD_SETSIZE, bail, "Too many file descriptors");
+
+		// Negative CMS timeout values are not valid.
+		if (cms_timeout < 0) {
+			syslog(LOG_DEBUG, "Negative CMS value: %d", cms_timeout);
+			cms_timeout = 0;
+		}
+
+		// Identify conditions where we are burning too much CPU.
+		if (cms_timeout == 0) {
+			switch (++zero_cms_in_a_row_count) {
+			case 10:
+				syslog(LOG_WARNING, "BUG: Main loop is thrashing!");
+				break;
+
+			case 200:
+				syslog(LOG_ERR, "BUG: Main loop is still thrashing! Slowing things down.");
+				break;
+
+			case 1000:
+				syslog(LOG_CRIT, "BUG: Main loop had over 1000 iterations in a row with a zero timeout! Terminating.");
+				gRet = ERRORCODE_UNKNOWN;
+				break;
+
+			default:
+				if (zero_cms_in_a_row_count > 200) {
+					// If the past 200 iterations have had a zero timeout,
+					// start using a minimum timeout of 10ms, so that we
+					// don't bring the rest of the system to a grinding halt.
+					cms_timeout = 10;
+				}
+				break;
+			}
+		} else {
+			zero_cms_in_a_row_count = 0;
+		}
+
+		// Convert our `cms` value into timeval compatible with select().
+		timeout.tv_sec = cms_timeout / MSEC_PER_SEC;
+		timeout.tv_usec = (cms_timeout % MSEC_PER_SEC) * USEC_PER_MSEC;
+
+#if DEBUG
+		syslog_dump_select_info(
+			LOG_DEBUG,
+			&gReadableFDs,
+			&gWritableFDs,
+			&gErrorableFDs,
+			max_fd + 1,
+			cms_timeout
+		);
+#endif
+
+		// Block until we timeout or there is FD activity.
+		fds_ready = select(
+			max_fd + 1,
+			&gReadableFDs,
+			&gWritableFDs,
+			&gErrorableFDs,
+			&timeout
+		);
+
+		if (fds_ready < 0) {
+			syslog(LOG_ERR, "select() errno=\"%s\" (%d)", strerror(errno),
+			       errno);
+
+			if (errno == EINTR) {
+				// EINTR isn't necessarily bad. If it was something bad,
+				// we would either already be terminated or gRet will be
+				// set and we will break out of the main loop in a moment.
+				continue;
+			}
+			gRet = ERRORCODE_ERRNO;
+			break;
+		}
+
+		// Process callback timers.
+		Timer::process();
+
+		// Process any necessary IPC actions.
+		ipc_server->process();
+
+		// Process the NCP instance.
+		ncp_instance->process();
+
+		// We only expose the interface via IPC after it is
+		// successfully initialized for the first time.
+		if (!interface_added) {
+			const boost::any value = ncp_instance->get_control_interface().get_property(kWPANTUNDProperty_NCPState);
+			if ((value.type() == boost::any(std::string()).type())
+			 && (boost::any_cast<std::string>(value) != kWPANTUNDStateUninitialized)
+			) {
+				ipc_server->add_interface(&ncp_instance->get_control_interface());
+				interface_added = true;
+			}
+		}
+	} // while (!gRet)
+
+bail:
+	syslog(LOG_NOTICE, "Cleaning up. (gRet = %d)", gRet);
+
+	if (gRet == ERRORCODE_QUIT) {
+		gRet = 0;
+	}
+
+	if (gPIDFilename) {
+		unlink(gPIDFilename);
+	}
+
+	syslog(LOG_NOTICE, "Stopped.");
+	return gRet;
+}
diff --git a/src/wpantund/wpantund.h b/src/wpantund/wpantund.h
new file mode 100644
index 0000000..192bd28
--- /dev/null
+++ b/src/wpantund/wpantund.h
@@ -0,0 +1,33 @@
+/*
+ *
+ * Copyright (c) 2016 Nest Labs, Inc.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef wpantund_h
+#define wpantund_h
+
+#include <string>
+
+namespace nl {
+namespace wpantund {
+
+std::string get_wpantund_version_string(void);
+
+};
+};
+
+#endif
diff --git a/third_party/Makefile.am b/third_party/Makefile.am
new file mode 100644
index 0000000..dd9506c
--- /dev/null
+++ b/third_party/Makefile.am
@@ -0,0 +1,56 @@
+#
+# Copyright (c) 2016 Nest Labs, Inc.
+# All rights reserved.
+#
+# This document is the property of Nest. It is considered
+# confidential and proprietary information.
+#
+# This document may not be reproduced or transmitted in any form,
+# in whole or in part, without the express written permission of
+# Nest.
+#
+
+DISTCLEANFILES = \
+	.deps \
+	Makefile \
+	$(NULL)
+
+# This line is needed for the lines below to work.
+EXTRA_DIST = $(NULL)
+
+# Protothreads
+EXTRA_DIST += \
+	pt/LICENSE \
+	pt/Makefile \
+	pt/README \
+	pt/README-VISUAL-C++.txt \
+	pt/README.google \
+	pt/doc/Doxyfile \
+	pt/doc/Makefile \
+	pt/doc/header.tex \
+	pt/doc/pt-doc.txt \
+	pt/doc/pt-mainpage.txt \
+	pt/doc/sicslogo.pdf \
+	pt/example-buffer.c \
+	pt/example-codelock.c \
+	pt/example-small.c \
+	pt/lc-addrlabels.h \
+	pt/lc-switch.h \
+	pt/lc.h \
+	pt/pt-sem.h \
+	pt/pt.h \
+	$(NULL)
+
+# Assert Macros
+EXTRA_DIST += \
+	assert-macros/LICENSE \
+	assert-macros/README.google \
+	assert-macros/assert-macros.h \
+	$(NULL)
+
+# fgetln
+EXTRA_DIST += \
+	fgetln/LICENSE \
+	fgetln/README.google \
+	fgetln/fgetln.h \
+	$(NULL)
diff --git a/third_party/assert-macros/LICENSE b/third_party/assert-macros/LICENSE
new file mode 100644
index 0000000..a25d13c
--- /dev/null
+++ b/third_party/assert-macros/LICENSE
@@ -0,0 +1,26 @@
+MIT License
+===========
+
+Copyright (C) 2013 Robert Quattlebaum
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute,
+sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall
+be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/third_party/assert-macros/README.google b/third_party/assert-macros/README.google
new file mode 100644
index 0000000..c5e47f8
--- /dev/null
+++ b/third_party/assert-macros/README.google
@@ -0,0 +1,12 @@
+URL: https://raw.githubusercontent.com/darconeous/smcp/c3b3d11fbbe0f63aa664320d296694d24ca09b37/src/smcp/assert-macros.h
+Version: c3b3d11fbbe0f63aa664320d296694d24ca09b37
+License: MIT
+License File: LICENSE
+
+Description:
+Introduces an expanded collection of assert macros, including `check()`,
+`require()`, etc.
+
+Local Modifications:
+Minor modifications and improvements. LICENSE file has been created for
+compliance purposes. Not included in original distribution.
diff --git a/third_party/assert-macros/assert-macros.h b/third_party/assert-macros/assert-macros.h
new file mode 100644
index 0000000..1a34df7
--- /dev/null
+++ b/third_party/assert-macros/assert-macros.h
@@ -0,0 +1,157 @@
+/*!	@file assert-macros.h
+**	@author Robert Quattlebaum <darco@deepdarc.com>
+**	@brief "Assert" and "Require" macros
+**
+**  MIT-Licensed Version
+**
+**	Copyright (C) 2013 Robert Quattlebaum
+**
+**	Permission is hereby granted, free of charge, to any person
+**	obtaining a copy of this software and associated
+**	documentation files (the "Software"), to deal in the
+**	Software without restriction, including without limitation
+**	the rights to use, copy, modify, merge, publish, distribute,
+**	sublicense, and/or sell copies of the Software, and to
+**	permit persons to whom the Software is furnished to do so,
+**	subject to the following conditions:
+**
+**	The above copyright notice and this permission notice shall
+**	be included in all copies or substantial portions of the
+**	Software.
+**
+**	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+**	KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+**	WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+**	PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+**	OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+**	OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+**	OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+**	SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+**
+**	See <http://www.deepdarc.com/> for other cool stuff.
+**
+**	-------------------------------------------------------------------
+**
+**	See <http://www.mactech.com/articles/develop/issue_11/Parent_final.html>
+**	for an explanation about how to use these macros and justification
+**	for using this pattern in general.
+*/
+
+#ifndef __DARC_ASSERT_MACROS__
+#define __DARC_ASSERT_MACROS__
+
+#ifndef assert_error_stream
+#if CONTIKI
+#define assert_error_stream     stdout
+#else
+#define assert_error_stream     stderr
+#endif
+#endif
+
+#if !DEBUG && !defined(NDEBUG)
+#define NDEBUG 1
+#endif
+
+#if ASSERT_MACROS_USE_SYSLOG
+#include <syslog.h>
+#endif
+
+#ifndef ASSERT_MACROS_SQUELCH
+#define ASSERT_MACROS_SQUELCH !DEBUG
+#endif
+
+#ifndef ASSERT_MACROS_SYSLOG_LEVEL
+#define ASSERT_MACROS_SYSLOG_LEVEL LOG_WARNING
+#endif
+
+#ifndef ASSERT_REQUIRE_FAILURE_HOOK
+#define ASSERT_REQUIRE_FAILURE_HOOK       do { } while (false)
+#endif
+
+#if HAVE_ASSERTMACROS_H
+ #include <AssertMacros.h>
+#else
+#include <stdio.h>
+#include <assert.h>
+#if ASSERT_MACROS_SQUELCH
+ #define assert_printf(fmt, ...) do { } while (0)
+ #define check_string(c, s)   do { } while (0)
+ #define require_action_string(c, l, a, s) \
+	do { if (!(c)) { \
+		ASSERT_REQUIRE_FAILURE_HOOK; \
+		a; \
+		goto l; \
+	} } while (0)
+#else
+ #if __AVR__
+  #define assert_printf(fmt, ...) \
+	fprintf_P(assert_error_stream, \
+			  PSTR(__FILE__ ":%d: " fmt "\n"), \
+			  __LINE__, \
+			  __VA_ARGS__)
+ #else
+  #if ASSERT_MACROS_USE_SYSLOG
+   #define assert_printf(fmt, ...) \
+	syslog(ASSERT_MACROS_SYSLOG_LEVEL, \
+		   __FILE__ ":%d: " fmt, \
+		   __LINE__, \
+		   __VA_ARGS__)
+  #elif ASSERT_MACROS_USE_VANILLA_PRINTF
+   #define assert_printf(fmt, ...) \
+	printf(__FILE__ ":%d: "fmt "\n", \
+		   __LINE__, \
+		   __VA_ARGS__)
+  #else
+   #define assert_printf(fmt, ...) \
+	fprintf(assert_error_stream, \
+			__FILE__ ":%d: " fmt "\n", \
+			__LINE__, \
+			__VA_ARGS__)
+  #endif
+ #endif
+ #define check_string(c, s) \
+	do { if (!(c)) { \
+		assert_printf("Check Failed (%s)", s); \
+		ASSERT_REQUIRE_FAILURE_HOOK; \
+	} } while (0)
+ #define require_action_string(c, l, a, s) \
+	do { if (!(c)) { \
+		assert_printf("Requirement Failed (%s)", s); \
+		ASSERT_REQUIRE_FAILURE_HOOK; \
+		a; \
+		goto l; \
+	} } while (0)
+#endif
+
+ #define __ASSERT_MACROS_check(c)   check_string(c, # c)
+#if !defined(__cplusplus)
+ #define check(c)   __ASSERT_MACROS_check(c)
+#endif
+ #define check_noerr(c)   __ASSERT_MACROS_check((c) == 0)
+ #define require_quiet(c, l)   do { if (!(c)) goto l; } while (0)
+ #define require(c, l)   require_action_string(c, l, {}, # c)
+
+ #define require_noerr(c, l)   require((c) == 0, l)
+ #define require_action(c, l, a)   require_action_string(c, l, a, # c)
+ #define require_noerr_action(c, l, a)   require_action((c) == 0, l, a)
+ #define require_string(c, l, s) \
+	require_action_string(c, l, \
+						  do {} while (0), s)
+#endif
+
+#if OVERRIDE_ASSERT_H
+#undef assert
+#undef __assert
+#define assert(e)  \
+	((void)((e) ? 0 : __assert(#e, __FILE__, __LINE__)))
+#define __assert(e, file, line) \
+	do { \
+		assert_printf("failed assertion `%s'", e) ; \
+		abort(); \
+	} while (0)
+#endif // OVERRIDE_ASSERT_H
+
+// Ignores return value from function 's'
+#define IGNORE_RETURN_VALUE(s)  do { if (s){} } while (0)
+
+#endif // __DARC_ASSERT_MACROS__
diff --git a/third_party/fgetln/LICENSE b/third_party/fgetln/LICENSE
new file mode 100644
index 0000000..5f85958
--- /dev/null
+++ b/third_party/fgetln/LICENSE
@@ -0,0 +1,26 @@
+MIT License
+===========
+
+Copyright (C) 2012 Robert Quattlebaum
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute,
+sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall
+be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/third_party/fgetln/README.google b/third_party/fgetln/README.google
new file mode 100644
index 0000000..c2da529
--- /dev/null
+++ b/third_party/fgetln/README.google
@@ -0,0 +1,12 @@
+URL: https://raw.githubusercontent.com/darconeous/smcp/47e863a8f91458ed01d133dc2a9b913defc56f6b/src/missing/fgetln.h
+Version: 47e863a8f91458ed01d133dc2a9b913defc56f6b
+License: MIT
+License File: LICENSE
+
+Description:
+Provides an implementation of the BSD `fgetln()` call for platforms
+where it is not present (like Linux).
+
+Local Modifications:
+LICENSE file has been created for compliance purposes. Not included
+in original distribution.
diff --git a/third_party/fgetln/fgetln.h b/third_party/fgetln/fgetln.h
new file mode 100644
index 0000000..7048eba
--- /dev/null
+++ b/third_party/fgetln/fgetln.h
@@ -0,0 +1,69 @@
+/*	@file fgetln.h
+**	@author Robert Quattlebaum <darco@deepdarc.com>
+**
+**	Copyright (C) 2012 Robert Quattlebaum
+**
+**	Permission is hereby granted, free of charge, to any person
+**	obtaining a copy of this software and associated
+**	documentation files (the "Software"), to deal in the
+**	Software without restriction, including without limitation
+**	the rights to use, copy, modify, merge, publish, distribute,
+**	sublicense, and/or sell copies of the Software, and to
+**	permit persons to whom the Software is furnished to do so,
+**	subject to the following conditions:
+**
+**	The above copyright notice and this permission notice shall
+**	be included in all copies or substantial portions of the
+**	Software.
+**
+**	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+**	KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+**	WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+**	PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+**	OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+**	OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+**	OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+**	SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+**
+**	-------------------------------------------------------------------
+**
+**	This is a simple implementation of the BSD function `fgetln()`,
+**	for use on operating systems which do not have a copy.
+**
+**	Man page URL: <http://www.openbsd.org/cgi-bin/man.cgi?query=fgetln>
+**
+**	NOTE: This implementation is *NOT* thread safe!
+*/
+
+#define	_WITH_GETLINE 1
+
+#include <stdio.h>
+
+#if !defined(HAVE_FGETLN)
+#define HAVE_FGETLN \
+	defined(__DARWIN_C_LEVEL) && \
+	(__DARWIN_C_LEVEL >= __DARWIN_C_FULL)
+#endif
+
+#if !defined(fgetln) && !HAVE_FGETLN
+static char *
+fgetln_(
+    FILE *stream, size_t *len
+    )
+{
+	static char* linep = NULL;
+	static size_t linecap = 0;
+	ssize_t length = getline(&linep, &linecap, stream);
+
+	if (length == -1) {
+		free(linep);
+		linep = NULL;
+		linecap = 0;
+		length = 0;
+	}
+	if (len)
+		*len = length;
+	return linep;
+}
+#define fgetln  fgetln_
+#endif
diff --git a/third_party/openthread/CONTRIBUTING.md b/third_party/openthread/CONTRIBUTING.md
new file mode 100644
index 0000000..2827b7d
--- /dev/null
+++ b/third_party/openthread/CONTRIBUTING.md
@@ -0,0 +1,27 @@
+Want to contribute? Great! First, read this page (including the small print at the end).
+
+### Before you contribute
+Before we can use your code, you must sign the
+[Google Individual Contributor License Agreement]
+(https://cla.developers.google.com/about/google-individual)
+(CLA), which you can do online. The CLA is necessary mainly because you own the
+copyright to your changes, even after your contribution becomes part of our
+codebase, so we need your permission to use and distribute your code. We also
+need to be sure of various other things—for instance that you'll tell us if you
+know that your code infringes on other people's patents. You don't have to sign
+the CLA until after you've submitted your code for review and a member has
+approved it, but you must do it before we can put your code into our codebase.
+Before you start working on a larger contribution, you should get in touch with
+us first through the issue tracker with your idea so that we can help out and
+possibly guide you. Coordinating up front makes it much easier to avoid
+frustration later on.
+
+### Code reviews
+All submissions, including submissions by project members, require review. We
+use Github pull requests for this purpose.
+
+### The small print
+Contributions made by corporations are covered by a different agreement than
+the one above, the
+[Software Grant and Corporate Contributor License Agreement]
+(https://cla.developers.google.com/about/google-corporate).
diff --git a/third_party/openthread/LICENSE b/third_party/openthread/LICENSE
new file mode 100644
index 0000000..c9f66de
--- /dev/null
+++ b/third_party/openthread/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2016, Nest Labs, Inc.
+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.
diff --git a/third_party/openthread/README b/third_party/openthread/README
new file mode 100644
index 0000000..6c1dad8
--- /dev/null
+++ b/third_party/openthread/README
@@ -0,0 +1,19 @@
+OpenThread is an open source implementation of the Thread 1.0.1 Final Specification.
+The Thread 1.0.1 Final Specification is promulgated by the Thread Group. The Thread
+Group is a non-profit organization formed for the purposes of defining one or
+more specifications, best practices, reference architectures, implementation
+guidelines and certification programs to promote the availability of compliant
+implementations of the Thread protocol. Information on becoming a Member, including
+information about the benefits thereof, can be found at http://threadgroup.org.
+
+OpenThread is not affiliated with or endorsed by the Thread Group. Implementation
+of this OpenThread code does not assure compliance with the Thread 1.0.1 Final
+Specification and does not convey the right to identify any final product as Thread
+certified. Members of the Thread Group may hold patents and other intellectual
+property rights relating to the Thread 1.0.1 Final Specification, ownership and
+licenses of which are subject to the Thread Group’s IP Policies, and not this license.
+
+The included copyright to the OpenThread code is subject to the license in the
+LICENSE file, and all other rights and licenses are expressly reserved.
+No warranty or assurance is made with respect to additional rights that may be
+required to implement this code.
diff --git a/third_party/openthread/README.google b/third_party/openthread/README.google
new file mode 100644
index 0000000..310ea88
--- /dev/null
+++ b/third_party/openthread/README.google
@@ -0,0 +1,13 @@
+URL: https://github.com/openthread/openthread/tree/4ff88e835
+Version: 4ff88e835
+License: BSD-3
+License File: LICENSE
+
+Description:
+OpenThread is an open-source implementation of the Thread networking
+protocol. With OpenThread, Nest is making the technology used in Nest
+products more broadly available to accelerate the development of
+products for the connected home.
+
+Local Modifications:
+Stripped all unused files.
diff --git a/third_party/openthread/README.md b/third_party/openthread/README.md
new file mode 100644
index 0000000..041dc0c
--- /dev/null
+++ b/third_party/openthread/README.md
@@ -0,0 +1,71 @@
+<a href="https://github.com/openthread/openthread">![Logo](doc/images/openthread_logo.png)</a>
+
+[![Build Status](https://travis-ci.org/openthread/openthread.svg?branch=master)](https://travis-ci.org/openthread/openthread)
+
+OpenThread is an open-source implementation of the [Thread](http://threadgroup.org/technology/ourtechnology) networking protocol. With OpenThread, Nest is making the technology used in Nest products more broadly available to accelerate the development of products for the connected home.
+
+The Thread specification defines an IPv6-based reliable, secure and low-power wireless device-to-device communication protocol for home applications. More information about Thread can be found on [threadgroup.org](http://www.threadgroup.org/).
+
+## Features
+- Highly portable: OS and platform agnostic, with a radio abstraction layer
+- Implements the End Device, Router, Leader & Border Router roles
+- Small memory footprint
+
+OpenThread implements all Thread networking layers, including IPv6, 6LoWPAN, IEEE 802.15.4 with MAC security, Mesh Link Establishment, and Mesh Routing.
+
+
+# Who is behind OpenThread
+
+![Logo](doc/images/openthread_contrib.png)
+
+Nest, along with ARM, Atmel, a subsidiary of Microchip Technology, Dialog Semiconductor,  Qualcomm Technologies, Inc., a subsidiary of Qualcomm Incorporated and Texas Instruments Incorporated are contributing to the ongoing development of OpenThread.
+
+
+# Getting Started
+
+The easiest way to get started is to run the CLI example in `/examples/cli`. See the [CLI example README](examples/cli/README.md) for more details.
+
+
+## What's included
+
+In the repo you'll find the following directories and files
+
+File/Folder	 | Provides
+-------|--------
+doc | Doxygen docs
+examples | Sample applications demonstrating various parts of OpenThread
+include | Includes header files for OpenThread API
+src | The core implementation of the Thread standard
+tests | Unit and Thread conformance tests
+third_party | Third-party code used by OpenThread
+
+
+## Docs
+The Doxygen reference docs are [hosted online](http://openthread.github.io/openthread/) and generated as part of the build.
+
+
+# Getting Help
+
+Submit bugs and feature requests to [issue tracker](https://github.com/openthread/openthread/issues). Usage questions? Post questions to [Stack Overflow](http://stackoverflow.com/) using the [[openthread] tag](http://stackoverflow.com/questions/tagged/openthread). We also use Google Groups for discussion and announcements:
+
+* [openthread-announce](https://groups.google.com/forum/#!forum/openthread-announce) - subscribe for release notes and new updates on OpenThread
+* [openthread-users](https://groups.google.com/forum/#!forum/openthread-users) - the best place for users to discuss OpenThread and interact with the OpenThread team
+* [openthread-devel](https://groups.google.com/forum/#!forum/openthread-devel) - team members discuss the on-going development of OpenThread
+
+
+# Versioning
+
+OpenThread follows [the Semantic Versioning guidelines](http://semver.org/) for release cycle transparency and to maintain backwards compatibility. OpenThread's versioning is independent of the Thread protocol specification version but will clearly indicate which version of the specification it currently supports.
+
+
+# Contributing
+
+See the [CONTRIBUTING.md](CONTRIBUTING.md) file for more information.
+
+
+# License
+
+OpenThread is released under the [BSD 3-Clause license](LICENSE).
+See the [LICENSE](LICENSE) file for more information.
+
+Please only use the OpenThread name and marks when accurately referencing this software distribution, and do not use these marks in a way that suggests you are endorsed by or otherwise affiliated with Nest, Google, or The Thread Group.
diff --git a/third_party/openthread/src/ncp/PROTOCOL.md b/third_party/openthread/src/ncp/PROTOCOL.md
new file mode 100644
index 0000000..fe8a95c
--- /dev/null
+++ b/third_party/openthread/src/ncp/PROTOCOL.md
@@ -0,0 +1,1260 @@
+Spinel Host Controller Interface
+================================
+
+Updated: 2016-05-18
+
+Written by: Robert Quattlebaum <rquattle@nestlabs.com>
+
+THIS DOCUMENT IS A WORK IN PROGRESS AND SUBJECT TO CHANGE.
+
+Copyright (c) 2016 Nest Labs, All Rights Reserved
+
+## 0. Abstract ##
+
+This document describes a general management protocol for enabling a host
+device to communicate with and manage a network co-processor(NCP).
+
+While initially designed to support Thread-based NCPs, the NCP protocol
+has been designed with a layered approach that allows it to be easily
+adapted to other network protocols.
+
+## 1. Definitions ##
+
+ *  **NCP**: Network Control Processor
+ *  **Host**: Computer or Micro-controller which controls the NCP.
+ *  **TID**: Transaction Identifier (0-15)
+ *  **IID**: Interface Identifier (0-3)
+
+## 2. Introduction ##
+
+This Network Co-Processor (NCP) protocol was designed to enable a host
+device to communicate with and manage a NCP while also achieving the
+following goals:
+
+ *  Adopt a layered approach to the protocol design, allowing future
+    support for other network protocols.
+ *  Minimize the number of required commands/methods by providing a
+    rich, property-based API.
+ *  Support NCPs capable of being connected to more than one network
+    at a time.
+ *  Gracefully handle the addition of new features and capabilities
+    without necessarily breaking backward compatibility.
+ *  Be as minimal and light-weight as possible without unnecessarily
+    sacrificing flexibility.
+
+On top of this core framework, we define the properties and commands
+to enable various features and network protocols.
+
+## 3. Frame Format ##
+
+A frame is defined simply as the concatenation of
+
+ *  A header byte
+ *  A command (up to three bytes)
+ *  An optional command payload
+
+    FRAME = HEADER CMD [CMD_PAYLOAD]
+
+Octets: |    1   | 1-3 |       *n*
+--------|--------|-----|-----------------
+Fields: | HEADER | CMD | *[CMD_PAYLOAD]*
+
+
+### 3.1. Header Format ###
+
+The header byte is broken down as follows:
+
+     0 1 2 3 4 5 6 7
+    +-+-+-+-+-+-+-+-+
+    |1|R|IID|  TID  |
+    +-+-+-+-+-+-+-+-+
+
+#### 3.1.1. Flag Bit ####
+
+The most significant header bit is always set to 1 to allow this
+protocol to be line compatible with BTLE HCI. By setting the first
+bit, we can disambiguate between Spinel frames and HCI frames (which
+always start with either `0x01` or `0x04`) without any additional
+framing overhead.
+
+#### 3.1.2. Reserved Bit ####
+
+The reserved bit (`R`) is reserved for future use. The sender MUST
+set this bit to zero, and the receiver MUST ignore frames with this
+bit set.
+
+#### 3.1.3. Interface Identifier (IID) ####
+
+The Interface Identifier (IID) is a number between 0 and 3 which
+identifies which subinterface the frame is intended for. This allows
+the protocol to support connecting to more than one network at once.
+The first subinterface (0) is considered the primary subinterface and
+MUST be supported. Support for all other subinterfaces is OPTIONAL.
+
+#### 3.1.4. Transaction Identifier (TID) ####
+
+The least significant bits of the header represent the Transaction
+Identifier(TID). The TID is used for correlating responses to the
+commands which generated them.
+
+When a command is sent from the host, any reply to that command sent
+by the NCP will use the same value for the TID. When the host receives
+a frame that matches the TID of the command it sent, it can easily
+recognize that frame as the actual response to that command.
+
+The TID value of zero (0) is used for commands to which a correlated
+response is not expected or needed, such as for unsolicited update
+commands sent to the host from the NCP.
+
+#### 3.2. Command Identifier (CMD) ####
+
+The command identifier is a 21-bit unsigned integer encoded in up to
+three bytes using the packed unsigned integer format described in
+section 7.2. This encoding allows for up to 2,097,152 individual
+commands, with the first 127 commands represented as a single byte.
+Command identifiers larger than 2,097,151 are explicitly forbidden.
+
+CID Range             | Description
+----------------------|------------------
+0 - 63                | Reserved for core commands
+64 - 15,359           | *UNALLOCATED*
+15,360 - 16,383       | Vendor-specific
+16,384 - 1,999,999    | *UNALLOCATED*
+2,000,000 - 2,097,151 | Experimental use only
+
+#### 3.3. Command Payload (Optional) ####
+
+Depending on the semantics of the command in question, a payload MAY
+be included in the frame. The exact composition and length of the
+payload is defined by the command identifier.
+
+## 4. Commands
+
+* CMD 0: (Host->NCP) `CMD_NOOP`
+* CMD 1: (Host->NCP) `CMD_RESET`
+* CMD 2: (Host->NCP) `CMD_PROP_VALUE_GET`
+* CMD 3: (Host->NCP) `CMD_PROP_VALUE_SET`
+* CMD 4: (Host->NCP) `CMD_PROP_VALUE_INSERT`
+* CMD 5: (Host->NCP) `CMD_PROP_VALUE_REMOVE`
+* CMD 6: (NCP->Host) `CMD_PROP_VALUE_IS`
+* CMD 7: (NCP->Host) `CMD_PROP_VALUE_INSERTED`
+* CMD 8: (NCP->Host) `CMD_PROP_VALUE_REMOVED`
+* CMD 9: (Host->NCP) `CMD_NET_SAVE` (See section B.1.1.)
+* CMD 10: (Host->NCP) `CMD_NET_CLEAR`  (See section B.1.2.)
+* CMD 11: (Host->NCP) `CMD_NET_RECALL`  (See section B.1.3.)
+* CMD 12: (NCP->Host) `CMD_HBO_OFFLOAD` (See section C.1.1.)
+* CMD 13: (NCP->Host) `CMD_HBO_RECLAIM` (See section C.1.2.)
+* CMD 14: (NCP->Host) `CMD_HBO_DROP` (See section C.1.3.)
+* CMD 15: (Host->NCP) `CMD_HBO_OFFLOADED` (See section C.1.4.)
+* CMD 16: (Host->NCP) `CMD_HBO_RECLAIMED` (See section C.1.5.)
+* CMD 17: (Host->NCP) `CMD_HBO_DROPPED` (See section C.1.6.)
+
+### 4.1. CMD 0: (Host->NCP) `CMD_NOOP`
+
+Octets: |    1   |     1
+--------|--------|----------
+Fields: | HEADER | CMD_NOOP
+
+No-Operation command. Induces the NCP to send a success status back to
+the host. This is primarily used for livliness checks.
+
+The command payload for this command SHOULD be empty. The receiver
+MUST ignore any non-empty command payload.
+
+There is no error condition for this command.
+
+
+
+### 4.2. CMD 1: (Host->NCP) `CMD_RESET`
+
+Octets: |    1   |     1
+--------|--------|----------
+Fields: | HEADER | CMD_RESET
+
+Reset NCP command. Causes the NCP to perform a software reset. Due to
+the nature of this command, the TID is ignored. The host should
+instead wait for a `CMD_PROP_VALUE_IS` command from the NCP indicating
+`PROP_LAST_STATUS` has been set to `STATUS_RESET_SOFTWARE`.
+
+The command payload for this command SHOULD be empty. The receiver
+MUST ignore any non-empty command payload.
+
+If an error occurs, the value of `PROP_LAST_STATUS` will be emitted
+instead with the value set to the generated status code for the error.
+
+
+
+### 4.3. CMD 2: (Host->NCP) `CMD_PROP_VALUE_GET`
+
+Octets: |    1   |          1         |   1-3
+--------|--------|--------------------|---------
+Fields: | HEADER | CMD_PROP_VALUE_GET | PROP_ID
+
+Get property value command. Causes the NCP to emit a
+`CMD_PROP_VALUE_IS` command for the given property identifier.
+
+The payload for this command is the property identifier encoded in the
+packed unsigned integer format described in section 7.2.
+
+If an error occurs, the value of `PROP_LAST_STATUS` will be emitted
+instead with the value set to the generated status code for the error.
+
+
+
+### 4.4. CMD 3: (Host->NCP) `CMD_PROP_VALUE_SET`
+
+Octets: |    1   |          1         |   1-3   |    *n*
+--------|--------|--------------------|---------|------------
+Fields: | HEADER | CMD_PROP_VALUE_SET | PROP_ID | PROP_VALUE
+
+Set property value command. Instructs the NCP to set the given
+property to the specific given value, replacing any previous value.
+
+The payload for this command is the property identifier encoded in the
+packed unsigned integer format described in section 7.2, followed by
+the property value. The exact format of the property value is defined
+by the property.
+
+If an error occurs, the value of `PROP_LAST_STATUS` will be emitted
+with the value set to the generated status code for the error.
+
+
+
+### 4.5. CMD 4: (Host->NCP) `CMD_PROP_VALUE_INSERT`
+
+Octets: |    1   |          1            |   1-3   |    *n*
+--------|--------|-----------------------|---------|------------
+Fields: | HEADER | CMD_PROP_VALUE_INSERT | PROP_ID | VALUE_TO_INSERT
+
+Insert value into property command. Instructs the NCP to insert the
+given value into a list-oriented property, without removing other
+items in the list. The resulting order of items in the list is defined
+by the individual property being operated on.
+
+The payload for this command is the property identifier encoded in the
+packed unsigned integer format described in section 7.2, followed by
+the value to be inserted. The exact format of the value is defined by
+the property.
+
+If an error occurs, the value of `PROP_LAST_STATUS` will be emitted
+with the value set to the generated status code for the error.
+
+
+
+### 4.6. CMD 5: (Host->NCP) `CMD_PROP_VALUE_REMOVE`
+
+Octets: |    1   |          1            |   1-3   |    *n*
+--------|--------|-----------------------|---------|------------
+Fields: | HEADER | CMD_PROP_VALUE_REMOVE | PROP_ID | VALUE_TO_REMOVE
+
+Remove value from property command. Instructs the NCP to remove the
+given value from a list-oriented property, without affecting other
+items in the list. The resulting order of items in the list is defined
+by the individual property being operated on.
+
+Note that this command operates *by value*, not by index!
+
+The payload for this command is the property identifier encoded in the
+packed unsigned integer format described in section 7.2, followed by
+the value to be inserted. The exact format of the value is defined by
+the property.
+
+If an error occurs, the value of `PROP_LAST_STATUS` will be emitted
+with the value set to the generated status code for the error.
+
+
+### 4.7. CMD 6: (NCP->Host) `CMD_PROP_VALUE_IS`
+
+Octets: |    1   |          1        |   1-3   |    *n*
+--------|--------|-------------------|---------|------------
+Fields: | HEADER | CMD_PROP_VALUE_IS | PROP_ID | PROP_VALUE
+
+Property value notification command. This command can be sent by the
+NCP in response to a previous command from the host, or it can be sent
+by the NCP in an unsolicited fashion to notify the host of various
+state changes asynchronously.
+
+The payload for this command is the property identifier encoded in the
+packed unsigned integer format described in section 7.2, followed by
+the current value of the given property.
+
+
+
+### 4.8. CMD 7: (NCP->Host) `CMD_PROP_VALUE_INSERTED`
+
+Octets: |    1   |            1            |   1-3   |    *n*
+--------|--------|-------------------------|---------|------------
+Fields: | HEADER | CMD_PROP_VALUE_INSERTED | PROP_ID | PROP_VALUE
+
+Property value insertion notification command. This command can be
+sent by the NCP in response to the `CMD_PROP_VALUE_INSERT` command, or
+it can be sent by the NCP in an unsolicited fashion to notify the host
+of various state changes asynchronously.
+
+The payload for this command is the property identifier encoded in the
+packed unsigned integer format described in section 7.2, followed by
+the value that was inserted into the given property.
+
+The resulting order of items in the list is defined by the given
+property.
+
+### 4.9. CMD 8: (NCP->Host) `CMD_PROP_VALUE_REMOVED`
+
+Octets: |    1   |            1           |   1-3   |    *n*
+--------|--------|------------------------|---------|------------
+Fields: | HEADER | CMD_PROP_VALUE_REMOVED | PROP_ID | PROP_VALUE
+
+Property value removal notification command. This command can be sent
+by the NCP in response to the `CMD_PROP_VALUE_REMOVE` command, or it
+can be sent by the NCP in an unsolicited fashion to notify the host of
+various state changes asynchronously.
+
+Note that this command operates *by value*, not by index!
+
+The payload for this command is the property identifier encoded in the
+packed unsigned integer format described in section 7.2, followed by
+the value that was removed from the given property.
+
+The resulting order of items in the list is defined by the given
+property.
+
+
+
+
+
+
+## 5. General Properties
+
+While the majority of the properties that allow the configuration
+of network connectivity are network protocol specific, there are
+several properties that are required in all implementations.
+
+* 0: `PROP_LAST_STATUS`
+* 1: `PROP_PROTOCOL_VERSION`
+* 2: `PROP_NCP_VERSION`
+* 3: `PROP_INTERFACE_TYPE`
+* 4: `PROP_VENDOR_ID`
+* 5: `PROP_CAPS`
+* 6: `PROP_INTERFACE_COUNT`
+* 7: `PROP_POWER_STATE`
+* 8: `PROP_HWADDR`
+* 9: `PROP_LOCK`
+* 10: `PROP_HBO_MEM_MAX` (See section C.2.1.)
+* 11: `PROP_HBO_BLOCK_MAX` (See section C.2.2.)
+* 112: `PROP_STREAM_DEBUG`
+* 113: `PROP_STREAM_RAW`
+* 114: `PROP_STREAM_NET`
+* 115: `PROP_STREAM_NET_INSECURE`
+
+Additionally, future property allocations SHALL be made from the
+following allocation plan:
+
+Property ID Range     | Description
+----------------------|------------------
+0 - 127               | Reserved for frequently-used core properties
+128 - 15,359          | *UNALLOCATED*
+15,360 - 16,383       | Vendor-specific
+16,384 - 1,999,999    | *UNALLOCATED*
+2,000,000 - 2,097,151 | Experimental use only
+
+### 5.1. PROP 0: `PROP_LAST_STATUS`
+
+* Type: Read-Only
+* Encoding: `i`
+
+Octets: |       1-3
+--------|------------------
+Fields: | PROP_LAST_STATUS
+
+Describes the status of the last operation. Encoded as a packed
+unsigned integer.
+
+This property is emitted often to indicate the result status of
+pretty much any Host-to-NCP operation.
+
+It is emitted automatically at NCP startup with a value indicating
+the reset reason.
+
+See section 6 for the complete list of status codes.
+
+### 5.2. PROP 1: `PROP_PROTOCOL_VERSION`
+
+* Type: Read-Only
+* Encoding: `ii`
+
+Octets: |       1-3      |      1-3
+--------|----------------|---------------
+Fields: |  MAJOR_VERSION | MINOR_VERSION
+
+Describes the protocol version information. This property contains
+four fields, each encoded as a packed unsigned integer:
+
+ *  Major Version Number
+ *  Minor Version Number
+
+
+#### 5.2.1 Major Version Number ####
+
+The major version number is used to identify large and incompatible
+differences between protocol versions.
+
+The host MUST enter a FAULT state if it does not explicitly support
+the given major version number.
+
+#### 5.2.2 Minor Version Number ####
+
+The minor version number is used to identify small but otherwise
+compatible differences between protocol versions. A mismatch between
+the advertised minor version number and the minor version that is
+supported by the host SHOULD NOT be fatal to the operation of the
+host.
+
+### 5.3. PROP 2: `PROP_NCP_VERSION`
+
+* Type: Read-Only
+* Packed-Encoding: `U`
+
+Octets: |       *n*
+--------|-------------------
+Fields: | `NCP_VESION_STRING`
+
+Contains a string which describes the firmware currently running on
+the NCP. Encoded as a zero-terminated UTF-8 string.
+
+The format of the string is not strictly defined, but it is intended
+to present similarly to the "User-Agent" string from HTTP. The
+RECOMMENDED format of the string is as follows:
+
+    <STACK-NAME>/<STACK-VERSION>[<BUILD_INFO>][; <OTHER_INFO>]; <BUILD_DATE_AND_TIME>
+
+Examples:
+
+ *  `OpenThread/1.0d26-25-gb684c7f; DEBUG; May 9 2016 18:22:04`
+ *  `MyThreadNCP/2.0b125 s1 ALPHA; Sept 24 2015 20:49:19`
+
+### 5.4. PROP 3: `PROP_INTERFACE_TYPE`
+
+* Type: Read-Only
+* Encoding: `i`
+
+Octets: |       1-3
+--------|----------------
+Fields: | INTERFACE_TYPE
+
+This integer identifies what the network protocol for this NCP.
+Currently defined values are:
+
+ *  1: ZigBee
+ *  2: ZigBeeIP
+ *  3: Thread
+ *  TBD: BlueTooth Low Energy (BTLE)
+
+The host MUST enter a FAULT state if it does not recognize the
+protocol given by the NCP.
+
+### 5.5. PROP 4: `PROP_INTERFACE_VENDOR_ID`
+
+* Type: Read-Only
+* Encoding: `i`
+
+Octets: |       1-3
+--------|----------------
+Fields: | VENDOR_ID
+
+Vendor identifier.
+
+### 5.6. PROP 2: `PROP_CAPS`
+
+* Type: Read-Only
+* Packed-Encoding: `A(i)`
+
+Octets: |  1-3  |  1-3  | ...
+--------|-------|-------|-----
+Fields: | CAP_1 | CAP_2 | ...
+
+Describes the supported capabilities of this NCP. Encoded as a list of
+packed unsigned integers.
+
+A capability is defined as a 21-bit integer that describes a subset of
+functionality which is supported by the NCP.
+
+Currently defined values are:
+
+ * 1: `CAP_LOCK`
+ * 2: `CAP_NET_SAVE`
+ * 3: `CAP_HBO`: Host Block Offload (See Section C.)
+ * 4: `CAP_POWER_SAVE`
+ * 16: `CAP_802_15_4_2003`
+ * 17: `CAP_802_15_4_2006`
+ * 18: `CAP_802_15_4_2011`
+ * 21: `CAP_802_15_4_PIB`
+ * 24: `CAP_802_15_4_2450MHZ_OQPSK`
+ * 25: `CAP_802_15_4_915MHZ_OQPSK`
+ * 26: `CAP_802_15_4_868MHZ_OQPSK`
+ * 27: `CAP_802_15_4_915MHZ_BPSK`
+ * 28: `CAP_802_15_4_868MHZ_BPSK`
+ * 29: `CAP_802_15_4_915MHZ_ASK`
+ * 30: `CAP_802_15_4_868MHZ_ASK`
+ * 48: `CAP_ROLE_ROUTER`
+ * 49: `CAP_ROLE_SLEEPY`
+ * 52: `CAP_NET_THREAD_1_0`
+
+Additionally, future capability allocations SHALL be made from the
+following allocation plan:
+
+Capability Range      | Description
+----------------------|------------------
+0 - 127               | Reserved for core capabilities
+128 - 15,359          | *UNALLOCATED*
+15,360 - 16,383       | Vendor-specific
+16,384 - 1,999,999    | *UNALLOCATED*
+2,000,000 - 2,097,151 | Experimental use only
+
+
+### 5.7. PROP 4: `PROP_INTERFACE_COUNT`
+
+* Type: Read-Only
+* Packed-Encoding: `C`
+
+Octets: |       1
+--------|-----------------
+Fields: | `INTERFACE_COUNT`
+
+Describes the number of concurrent interfaces supported by this NCP.
+Since the concurrent interface mechanism is still TBD, this value MUST
+always be one.
+
+This value is encoded as an unsigned 8-bit integer.
+
+### 5.8. PROP 5: `PROP_POWER_STATE`
+
+* Type: Read-Write
+* Packed-Encoding: `C`
+
+Octets: |        1
+--------|------------------
+Fields: | `PROP_POWER_STATE`
+
+Describes the current power state of the NCP. By writing to this
+property you can manage the lower state of the NCP. Enumeration is
+encoded as a single unsigned byte.
+
+Defined values are:
+
+ *  0: `POWER_STATE_OFFLINE`: NCP is physically powered off.
+    (Enumerated for completeness sake, not expected on the wire)
+ *  1: `POWER_STATE_DEEP_SLEEP`: Almost everything on the NCP is shut
+    down, but can still be resumed via a command or interrupt.
+ *  2: `POWER_STATE_STANDBY`: NCP is in the lowest power state that
+    can still be awoken by an event from the radio (e.g. waiting for
+    alarm)
+ *  3: `POWER_STATE_LOW_POWER`: NCP is responsive (and possibly
+    connected), but using less power. (e.g. "Sleepy" child node)
+ *  4: `POWER_STATE_ONLINE`: NCP is fully powered. (e.g. "Parent"
+    node)
+
+### 5.9. PROP 6: `PROP_HWADDR`
+
+* Type: Read-Only*
+* Packed-Encoding: `E`
+
+Octets: |    8
+--------|------------
+Fields: | PROP_HWADDR
+
+The static EUI64 address of the device. This value is read-only, but
+may be writable under certain vendor-defined circumstances.
+
+### 5.10. PROP 6: `PROP_LOCK`
+
+* Type: Read-Write
+* Packed-Encoding: `b`
+
+Octets: |    1
+--------|------------
+Fields: | PROP_LOCK
+
+Property lock. Used for grouping changes to several properties to
+take effect at once, or to temporarily prevent the automatic updating
+of property values. When this property is set, the execution of the
+NCP is effectively frozen until it is cleared.
+
+This property is only supported if the `CAP_LOCK` capability is present.
+
+### 5.11. PROP 112: `PROP_STREAM_DEBUG`
+
+* Type: Read-Only-Stream
+* Packed-Encoding: `U`
+
+Octets: |    *n*
+--------|------------
+Fields: | UTF8_DATA
+
+This property is a streaming property, meaning that you cannot explicitly
+fetch the value of this property. The stream provides human-readable debugging
+output which may be displayed in the host logs.
+
+To receive the debugging stream, you wait for `CMD_PROP_VALUE_IS` commands for
+this property from the NCP.
+
+### 5.12. PROP 113: `PROP_STREAM_RAW`
+
+* Type: Read-Write-Stream
+* Packed-Encoding: `DD`
+
+Octets: |        2       |     *n*    |       *n*
+--------|----------------|------------|----------------
+Fields: | FRAME_DATA_LEN | FRAME_DATA | FRAME_METADATA
+
+This stream provides the capability of sending and receiving raw packets
+to and from the radio. The exact format of the frame metadata and data is
+dependent on the MAC and PHY being used.
+
+This property is a streaming property, meaning that you cannot explicitly
+fetch the value of this property. To receive traffic, you wait for
+`CMD_PROP_VALUE_IS` commands with this property id from the NCP.
+
+Implementations may optionally support the ability to transmit arbitrary
+raw packets. If this capability is supported, you may call `CMD_PROP_VALUE_SET`
+on this property with the value of the raw packet.
+
+Any data past the end of `FRAME_DATA_LEN` is considered metadata. The format
+of the metadata is defined by the associated MAC and PHY being used.
+
+### 5.13. PROP 114: `PROP_STREAM_NET`
+
+* Type: Read-Write-Stream
+* Packed-Encoding: `DD`
+
+Octets: |        2       |     *n*    |       *n*
+--------|----------------|------------|----------------
+Fields: | FRAME_DATA_LEN | FRAME_DATA | FRAME_METADATA
+
+This stream provides the capability of sending and receiving data packets
+to and from the currently attached network. The exact format of the frame
+metadata and data is dependent on the network protocol being used.
+
+This property is a streaming property, meaning that you cannot explicitly
+fetch the value of this property. To receive traffic, you wait for
+`CMD_PROP_VALUE_IS` commands with this property id from the NCP.
+
+To send network packets, you call `CMD_PROP_VALUE_SET` on this property with
+the value of the packet.
+
+Any data past the end of `FRAME_DATA_LEN` is considered metadata. The format
+of the metadata is defined by the associated network protocol.
+
+### 5.14. PROP 114: `PROP_STREAM_NET_INSECURE`
+
+* Type: Read-Write-Stream
+* Packed-Encoding: `DD`
+
+Octets: |        2       |     *n*    |       *n*
+--------|----------------|------------|----------------
+Fields: | FRAME_DATA_LEN | FRAME_DATA | FRAME_METADATA
+
+This stream provides the capability of sending and receiving unencrypted
+and unauthenticated data packets to and from nearby devices for the
+purposes of device commissioning. The exact format of the frame
+metadata and data is dependent on the network protocol being used.
+
+This property is a streaming property, meaning that you cannot explicitly
+fetch the value of this property. To receive traffic, you wait for
+`CMD_PROP_VALUE_IS` commands with this property id from the NCP.
+
+To send network packets, you call `CMD_PROP_VALUE_SET` on this property with
+the value of the packet.
+
+Any data past the end of `FRAME_DATA_LEN` is considered metadata. The format
+of the metadata is defined by the associated network protocol.
+
+
+## 6. Status Codes
+
+ *  0: `STATUS_OK`: Operation has completed successfully.
+ *  1: `STATUS_FAILURE`: Operation has failed for some undefined
+    reason.
+ *  2: `STATUS_UNIMPLEMENTED`
+ *  3: `STATUS_INVALID_ARGUMENT`
+ *  4: `STATUS_INVALID_STATE`
+ *  5: `STATUS_INVALID_COMMAND`
+ *  6: `STATUS_INVALID_INTERFACE`
+ *  7: `STATUS_INTERNAL_ERROR`
+ *  8: `STATUS_SECURITY_ERROR`
+ *  9: `STATUS_PARSE_ERROR`
+ *  10: `STATUS_IN_PROGRESS`
+ *  11: `STATUS_NOMEM`
+ *  12: `STATUS_BUSY`
+ *  13: `STATUS_PROPERTY_NOT_FOUND`
+ *  14: `STATUS_PACKET_DROPPED`
+ *  15: `STATUS_EMPTY`
+ *  16-111: *RESERVED*
+ *  112-127: Reset Causes
+     *  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-127: *RESERVED-RESET-CODES*
+ *  128 - 15,359: *UNALLOCATED*
+ *  15,360 - 16,383: Vendor-specific
+ *  16,384 - 1,999,999: *UNALLOCATED*
+ *  2,000,000 - 2,097,151: Experimental Use Only (MUST NEVER be used
+    in production!)
+
+## 7. Data Packing
+
+Data serialization for properties is performed using a light-weight
+data packing format which was loosely inspired by D-Bus. The format of
+a serialization is defined by a specially formatted string.
+
+Goals:
+
+ *  Be very lightweight and favor direct representation of values.
+ *  Use an easily readable and memorable format string.
+ *  Support lists and structures.
+ *  Allow properties to be appended to structures while maintaining
+    backward compatibility.
+
+Each primitive datatype has an ASCII character associated with it.
+Structures can be represented as strings of these characters. For
+example:
+
+ *  `"C"`: A single unsigned byte.
+ *  `"C6U"`: A single unsigned byte, followed by a 128-bit IPv6
+    address, followed by a zero-terminated UTF8 string.
+ *  `"A(6)"`: An array of IPv6 addresses
+
+In each case, the data is represented exactly as described. For
+example, an array of 10 IPv6 address is stored as 160 bytes.
+
+### 7.1 Primitive Types
+
+ *  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)
+
+### 7.2 Packed Unsigned Integer
+
+For certain types of integers, such command or property identifiers,
+usually have a value on the wire that is less than 127. However, in
+order to not preclude the use of values larger than 256, we would need
+to add an extra byte. Doing this would add an extra byte to the vast
+majority of instances, which can add up in terms of bandwidth.
+
+The packed unsigned integer format is inspired by the encoding scheme
+in UTF8 identical to the unsigned integer format in EXI.
+
+For all values less than 127, the packed form of the number is simply
+a single byte which directly represents the number. For values larger
+than 127, the following process is used to encode the value:
+
+1.  The unsigned integer is broken up into *n* 7-bit chunks and placed
+    into *n* octets, leaving the most significant bit of each octet
+    unused.
+2.  Order the octets from least-significant to most-significant.
+    (Little-endian)
+3.  Clear the most significant bit of the most significant octet. Set
+    the least significant bit on all other octets.
+
+Where *n* is the smallest number of 7-bit chunks you can use to
+represent the given value.
+
+Take the value 1337, for example:
+
+    1337 => 0x0539
+         => [39 0A]
+         => [B9 0A]
+
+To decode the value, you collect the 7-bit chunks until you find an
+octet with the most significant bit clear.
+
+### 7.3 Data Blobs
+
+Data blobs are special datatypes in that the data that they contain
+does not inherently define the size of the data. This means that if
+the length of the data blob isn't *implied*, then the length of the
+blob must be prepended as a packed unsigned integer.
+
+The length of a data blob is *implied* only when it is the last
+datatype in a given buffer. This works because we already know the
+size of the buffer, and the length of the data is simply the rest of
+the size of the buffer.
+
+For example, let's say we have a buffer that is encoded with the
+datatype signature of `CLLD`. In this case, it is pretty easy to tell
+where the start and end of the data blob is: the start is 9 bytes from
+the start of the buffer, and its length is the length of the buffer
+minus 9. (9 is the number of bytes taken up by a byte and two longs)
+
+However, things are a little different with `CLDL`. Since our datablob
+is no longer the last item in the signature, the length must be
+prepended.
+
+If you are a little confused, keep reading. This theme comes up in a a
+few different ways in the following sections.
+
+When a length is prepended, the length is encoded as a little-endian
+unsigned 16-bit integer.
+
+### 7.4 Structured Data
+
+The structured data type is a way of bundling together a bunch of data
+into a single data structure. This may at first seem useless. What is
+the difference between `T(Cii)` and just `Cii`? The answer is, in that
+particular case, nothing: they are stored in exactly the same way.
+
+However, one case where the structure datatype makes a difference is
+when you compare `T(Cii)L` to `CiiL`: they end up being represented
+entirely differently. This is because the structured data type follows
+the exact same semantics as the data blob type: if it isn't the last
+datatype in a signature, *it must be prepended with a length*. This is
+useful because it allows for new datatypes to be appended to the
+structure's signature while remaining *backward parsing
+compatibility*.
+
+More explicitly, if you take data that was encoded with `T(Cii6)L`,
+you can still decode it as `T(Cii)L`.
+
+Let's take, for example, the property `PROP_IPv6_ADDR_TABLE`.
+Conceptually it is just a list of IPv6 addresses, so we can encode it
+as `A(6c)`. However, if we ever want to associate more data with the
+type (like flags), we break our backward compatibility if we add
+another member and use `A(6cC)`. To allow for data to be added without
+breaking backward compatibility, we use the structured data type from
+the start: `A(T(6c))`. Then when we add a new member to the structure
+(`A(T(6cC))`), we don't break backward compatibility.
+
+It's also worth noting that `T(Cii)L` also parses as `DL`. You could
+then take the resultant data blob and parse it as `Cii`.
+
+When a length is prepended, the length is encoded as a little-endian
+unsigned 16-bit integer.
+
+### 7.5 Arrays
+
+An array is simply a concatenated set of *n* data encodings. For example,
+the type `A(6)` is simply a list of IPv6 addresses---one after the other.
+
+Just like the data blob type and the structured data type, the length
+of the entire array must be prepended *unless* the array is the last
+type in a given signature. Thus, `A(C)` (An array of unsigned bytes)
+encodes identically to `D`.
+
+When a length is prepended, the length is encoded as a little-endian
+unsigned 16-bit integer.
+
+## A. Framing Protocol
+
+Since this NCP protocol is defined independently of framing, any
+number of framing protocols could be used successfully. However,
+in the interests of cross-compatibility, we recommend using
+HDLC-Lite for framing when using this NCP protocol with a UART.
+
+A SPI-specific framing mechanism is currently TBD.
+
+## B. Feature: Network Save
+
+The network save feature is an optional NCP capability that, when
+present, allows the host to save and recall network credentials and
+state to and from nonvolatile storage.
+
+The presence of this feature can be detected by checking for the
+presence of the `CAP_NET_SAVE` capability in `PROP_CAPS`.
+
+### B.1. Commands
+
+#### B.1.1. CMD 9: (Host->NCP) `CMD_NET_SAVE`
+
+Octets: |    1   |      1
+--------|--------|--------------
+Fields: | HEADER | CMD_NET_SAVE
+
+Save network state command. Saves any current network credentials and
+state necessary to reconnect to the current network to non-volatile
+memory.
+
+This operation affects non-volatile memory only. The current network
+information stored in volatile memory is unaffected.
+
+The response to this command is always a `CMD_PROP_VALUE_IS` for
+`PROP_LAST_STATUS`, indicating the result of the operation.
+
+This command is only available if the `CAP_NET_SAVE` capability is
+set.
+
+
+
+#### B.1.2. CMD 10: (Host->NCP) `CMD_NET_CLEAR`
+
+Octets: |    1   |      1
+--------|--------|---------------
+Fields: | HEADER | CMD_NET_CLEAR
+
+Clear saved network state command. Clears any previously saved network
+credentials and state previously stored by `CMD_NET_SAVE` from
+non-volatile memory.
+
+This operation affects non-volatile memory only. The current network
+information stored in volatile memory is unaffected.
+
+The response to this command is always a `CMD_PROP_VALUE_IS` for
+`PROP_LAST_STATUS`, indicating the result of the operation.
+
+This command is only available if the `CAP_NET_SAVE` capability is
+set.
+
+
+
+#### B.1.3. CMD 11: (Host->NCP) `CMD_NET_RECALL`
+
+Octets: |    1   |      1
+--------|--------|----------------
+Fields: | HEADER | CMD_NET_RECALL
+
+Recall saved network state command. Recalls any previously saved
+network credentials and state previously stored by `CMD_NET_SAVE` from
+non-volatile memory.
+
+This command will typically generated several unsolicited property
+updates as the network state is loaded. At the conclusion of loading,
+the authoritative response to this command is always a
+`CMD_PROP_VALUE_IS` for `PROP_LAST_STATUS`, indicating the result of
+the operation.
+
+This command is only available if the `CAP_NET_SAVE` capability is
+set.
+
+
+## C. Feature: Host Buffer Offload
+
+The memory on an NCP may be much more limited than the memory on
+the host processor. In such situations, it is sometimes useful
+for the NCP to offload buffers to the host processor temporarily
+so that it can perform other operations.
+
+Host buffer offload is an optional NCP capability that, when
+present, allows the NCP to store data buffers on the host processor
+that can be recalled at a later time.
+
+The presence of this feature can be detected by the host by
+checking for the presence of the `CAP_HBO`
+capability in `PROP_CAPS`.
+
+### C.1. Commands
+
+#### C.1.1. CMD 12: (NCP->Host) `CMD_HBO_OFFLOAD`
+
+* Argument-Encoding: `LscD`
+    * `OffloadId`: 32-bit unique block identifier
+    * `Expiration`: In seconds-from-now
+    * `Priority`: Critical, High, Medium, Low
+    * `Data`: Data to offload
+
+#### C.1.2. CMD 13: (NCP->Host) `CMD_HBO_RECLAIM`
+ *  Argument-Encoding: `Lb`
+     *  `OffloadId`: 32-bit unique block identifier
+     *  `KeepAfterReclaim`: If not set to true, the block will be
+        dropped by the host after it is sent to the NCP.
+
+#### C.1.3. CMD 14: (NCP->Host) `CMD_HBO_DROP`
+
+* Argument-Encoding: `L`
+    * `OffloadId`: 32-bit unique block identifier
+
+#### C.1.1. CMD 15: (Host->NCP) `CMD_HBO_OFFLOADED`
+
+* Argument-Encoding: `Li`
+    * `OffloadId`: 32-bit unique block identifier
+    * `Status`: Status code for the result of the operation.
+
+#### C.1.2. CMD 16: (Host->NCP) `CMD_HBO_RECLAIMED`
+
+* Argument-Encoding: `LiD`
+    * `OffloadId`: 32-bit unique block identifier
+    * `Status`: Status code for the result of the operation.
+    * `Data`: Data that was previously offloaded (if any)
+
+#### C.1.3. CMD 17: (Host->NCP) `CMD_HBO_DROPPED`
+
+* Argument-Encoding: `Li`
+    * `OffloadId`: 32-bit unique block identifier
+    * `Status`: Status code for the result of the operation.
+
+### C.2. Properties
+
+#### C.2.1. PROP 6: `PROP_HBO_MEM_MAX`
+
+* Type: Read-Write
+* Packed-Encoding: `L`
+
+Octets: |       4
+--------|-----------------
+Fields: | `PROP_HBO_MEM_MAX`
+
+Describes the number of bytes that may be offloaded from the NCP to
+the host. Default value is zero, so this property must be set by the
+host to a non-zero value before the NCP will begin offloading blocks.
+
+This value is encoded as an unsigned 32-bit integer.
+
+This property is only available if the `CAP_HBO`
+capability is present in `PROP_CAPS`.
+
+#### C.2.1. PROP 7: `PROP_HBO_BLOCK_MAX`
+
+* Type: Read-Write
+* Packed-Encoding: `S`
+
+Octets: |       2
+--------|-----------------
+Fields: | `PROP_HBO_BLOCK_MAX`
+
+Describes the number of blocks that may be offloaded from the NCP to
+the host. Default value is 32. Setting this value to zero will cause
+host block offload to be effectively disabled.
+
+This value is encoded as an unsigned 16-bit integer.
+
+This property is only available if the `CAP_HBO`
+capability is present in `PROP_CAPS`.
+
+
+
+
+## D. Protocol: Thread
+
+This section describes all of the properties and semantics required
+for managing a thread NCP.
+
+### D.1. PHY Properties
+#### D.1.1. PROP x: `PROP_PHY_ENABLED`
+* Type: Read-Only
+* Packed-Encoding: `b`
+
+#### D.1.4. PROP x: `PROP_PHY_CHAN`
+* Type: Read-Write
+* Packed-Encoding: `C`
+
+#### D.1.5. PROP x: `PROP_PHY_CHAN_SUPPORTED`
+* Type: Read-Only
+* Packed-Encoding: `A(C)`
+* Unit: List of channels
+
+#### D.1.3. PROP x: `PROP_PHY_FREQ`
+* Type: Read-Only
+* Packed-Encoding: `L`
+* Unit: Kilohertz
+
+#### D.1.6. PROP x: `PROP_PHY_CCA_THRESHOLD`
+* Type: Read-Write
+* Packed-Encoding: `c`
+* Unit: dBm
+
+#### D.1.7. PROP x: `PROP_PHY_TX_POWER`
+* Type: Read-Write
+* Packed-Encoding: `c`
+* Unit: dBm
+
+#### D.1.6. PROP x: `PROP_PHY_RSSI`
+* Type: Read-Only
+* Packed-Encoding: `c`
+* Unit: dBm
+
+#### D.1.7. PROP x: `PROP_PHY_RAW_STREAM_ENABLED`
+* Type: Read-Write
+* Packed-Encoding: `b`
+
+Set to true to enable raw frames to be emitted from `PROP_STREAM_RAW`.
+
+
+
+### D.2. MAC Properties
+
+
+#### D.2.1. PROP x: `PROP_MAC_SCAN_STATE`
+* Type: Read-Write
+* Packed-Encoding: `C`
+* Unit: Enumeration
+
+Possible Values:
+
+* 0: `SCAN_STATE_IDLE`
+* 1: `SCAN_STATE_BEACON`
+* 2: `SCAN_STATE_ENERGY`
+
+Set to `SCAN_STATE_BEACON` to start an active scan.
+Beacons will be emitted from `PROP_MAC_SCAN_BEACON`.
+
+Set to `SCAN_STATE_ENERGY` to start an energy scan.
+Channel energy will be reported by alternating emissions
+of `PROP_PHY_CHAN` and `PROP_PHY_RSSI`.
+
+Values switches to `SCAN_STATE_IDLE` when scan is complete.
+
+#### D.2.2. PROP x: `PROP_MAC_SCAN_MASK`
+* Type: Read-Write
+* Packed-Encoding: `A(C)`
+* Unit: List of channels to scan
+
+#### D.2.3. PROP x: `PROP_MAC_SCAN_BEACON`
+* Type: Read-Only-Stream
+* Packed-Encoding: `CcT(ESSC)T(i).`
+
+chan,rssi,(laddr,saddr,panid,lqi),(proto,xtra)
+
+chan,rssi,(laddr,saddr,panid,lqi),(proto,flags,networkid,xpanid) [CcT(ESSC)T(iCUD.).]
+
+#### D.2.4. PROP x: `PROP_MAC_15_4_LADDR`
+* Type: Read-Write
+* Packed-Encoding: `E`
+
+#### D.2.5. PROP x: `PROP_MAC_15_4_SADDR`
+* Type: Read-Write
+* Packed-Encoding: `S`
+
+#### D.2.6. PROP x: `PROP_MAC_15_4_PANID`
+* Type: Read-Write
+* Packed-Encoding: `S`
+
+
+
+
+
+### D.3. NET Properties
+
+#### D.3.1. PROP x: `PROP_NET_SAVED`
+* Type: Read-Only
+* Packed-Encoding: `b`
+
+#### D.3.2. PROP x: `PROP_NET_ENABLED`
+* Type: Read-Only
+* Packed-Encoding: `b`
+
+#### D.3.3. PROP x: `PROP_NET_STATE`
+* Type: Read-Write
+* Packed-Encoding: `C`
+* Unit: Enumeration
+
+Values:
+
+* 0: `NET_STATE_OFFLINE`
+* 1: `NET_STATE_DETACHED`
+* 2: `NET_STATE_ATTACHING`
+* 3: `NET_STATE_ATTACHED`
+
+#### D.3.4. PROP x: `PROP_NET_ROLE`
+* Type: Read-Write
+* Packed-Encoding: `C`
+* Unit: Enumeration
+
+Values:
+
+* 0: `NET_ROLE_NONE`
+* 1: `NET_ROLE_CHILD`
+* 2: `NET_ROLE_ROUTER`
+* 3: `NET_ROLE_LEADER`
+
+#### D.3.5. PROP x: `PROP_NET_NETWORK_NAME`
+* Type: Read-Write
+* Packed-Encoding: `U`
+
+#### D.3.6. PROP x: `PROP_NET_XPANID`
+* Type: Read-Write
+* Packed-Encoding: `D`
+
+#### D.3.7. PROP x: `PROP_NET_MASTER_KEY`
+* Type: Read-Write
+* Packed-Encoding: `D`
+
+#### D.3.8. PROP x: `PROP_NET_KEY_SEQUENCE`
+* Type: Read-Write
+* Packed-Encoding: `L`
+
+#### D.3.9. PROP x: `PROP_NET_PARTITION_ID`
+* Type: Read-Write
+* Packed-Encoding: `L`
+
+
+
+#### D.4. THREAD Properties
+
+
+#### D.4.1. PROP x: `PROP_THREAD_LEADER`
+* Type: Read-Write
+* Packed-Encoding: `6`
+
+#### D.4.2. PROP x: `PROP_THREAD_PARENT`
+* Type: Read-Write
+* Packed-Encoding: `ES`
+* LADDR, SADDR
+
+#### D.4.3. PROP x: `PROP_THREAD_CHILD_TABLE`
+* Type: Read-Write
+* Packed-Encoding: `A(T(ES))`
+* LADDR, SADDR
+
+
+
+### D.5. IPv6 Properties
+
+#### D.5.1. PROP x: `PROP_IPV6_LL_ADDR`
+* Type: Read-Only
+* Packed-Encoding: `6`
+
+IPv6 Address
+
+#### D.5.2. PROP x: `PROP_IPV6_ML_ADDR`
+* Type: Read-Only
+* Packed-Encoding: `6`
+
+IPv6 Address + Prefix Length
+
+#### D.5.2. PROP x: `PROP_IPV6_ML_PREFIX`
+* Type: Read-Write
+* Packed-Encoding: `6C`
+
+IPv6 Prefix + Prefix Length
+
+#### D.5.3. PROP x: `PROP_IPV6_ADDRESS_TABLE`
+
+* Type: Read-Write
+* Packed-Encoding: `A(T(6CLLC))`
+
+Array of structures containing:
+
+* `6`: IPv6 Address
+* `C`: Network Prefix Length
+* `L`: Valid Lifetime
+* `L`: Preferred Lifetime
+* `C`: Flags
+
+#### D.4.3. PROP x: `PROP_IPv6_ROUTE_TABLE`
+* Type: Read-Write
+* Packed-Encoding: `A(T(6C6))`
+
+Array of structures containing:
+
+* `6`: IPv6 Address
+* `C`: Network Prefix Length
+* `6`: Next Hop
diff --git a/third_party/openthread/src/ncp/spinel.c b/third_party/openthread/src/ncp/spinel.c
new file mode 100644
index 0000000..cd054ba
--- /dev/null
+++ b/third_party/openthread/src/ncp/spinel.c
@@ -0,0 +1,1179 @@
+/*
+ *    Copyright (c) 2016, Nest Labs, Inc.
+ *    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.
+ */
+
+/*
+ *  -------------------------------------------------------------------
+ *
+ *  ## Unit Test ##
+ *
+ *  This file includes its own unit test. To compile the unit test,
+ *  simply compile this file with the macro SPINEL_SELF_TEST set to 1.
+ *  For example:
+ *
+ *      cc spinel.c -Wall -DSPINEL_SELF_TEST=1 -o spinel
+ *
+ *  -------------------------------------------------------------------
+ */
+
+// ----------------------------------------------------------------------------
+// MARK: -
+// MARK: Headers
+
+#include "spinel.h"
+
+#include <assert.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <stdlib.h>
+
+// ----------------------------------------------------------------------------
+// MARK: -
+
+#ifndef assert_printf
+#define assert_printf(fmt, ...) \
+fprintf(stderr, \
+        __FILE__ ":%d: " fmt "\n", \
+        __LINE__, \
+        __VA_ARGS__)
+#endif
+
+#ifndef require_action
+#define require_action(c, l, a) \
+    do { if (!(c)) { \
+        assert_printf("Requirement Failed (%s)", # c); \
+        a; \
+        goto l; \
+    } } while (0)
+#endif
+
+#ifndef require
+#define require(c, l)   require_action(c, l, {})
+#endif
+
+// ----------------------------------------------------------------------------
+// MARK: -
+
+spinel_ssize_t
+spinel_packed_uint_decode(const uint8_t *bytes, spinel_size_t len, unsigned int *value_ptr)
+{
+    spinel_ssize_t ret = 0;
+    unsigned int value = 0;
+
+    int i = 0;
+
+    do
+    {
+        if (len < sizeof(uint8_t))
+        {
+            ret = -1;
+            break;
+        }
+
+        value |= ((bytes[0] & 0x7F) << i);
+        i += 7;
+        ret += sizeof(uint8_t);
+        bytes += sizeof(uint8_t);
+        len -= sizeof(uint8_t);
+    }
+    while ((bytes[-1] & 0x80) == 0x80);
+
+    if ((ret > 0) && (value_ptr != NULL))
+    {
+        *value_ptr = value;
+    }
+
+    return ret;
+}
+
+spinel_ssize_t
+spinel_packed_uint_size(unsigned int value)
+{
+    spinel_size_t ret;
+
+    if (value < (1 << 7))
+    {
+        ret = 1;
+    }
+    else if (value < (1 << 14))
+    {
+        ret = 2;
+    }
+    else if (value < (1 << 21))
+    {
+        ret = 3;
+    }
+    else if (value < (1 << 28))
+    {
+        ret = 4;
+    }
+    else
+    {
+        ret = 5;
+    }
+
+    return ret;
+}
+
+spinel_ssize_t
+spinel_packed_uint_encode(uint8_t *bytes, spinel_size_t len, unsigned int value)
+{
+    const spinel_ssize_t encoded_size = spinel_packed_uint_size(value);
+
+    if (len >= encoded_size)
+    {
+        spinel_size_t i;
+
+        for (i = 0; i != encoded_size - 1; ++i)
+        {
+            *bytes++ = (value & 0x7F) | 0x80;
+            value = (value >> 7);
+        }
+
+        *bytes++ = (value & 0x7F);
+    }
+
+    return encoded_size;
+}
+
+const char *
+spinel_next_packed_datatype(const char *pack_format)
+{
+    int depth = 0;
+
+    do
+    {
+        switch (*++pack_format)
+        {
+        case '(':
+            depth++;
+            break;
+
+        case ')':
+            depth--;
+
+            if (depth == 0)
+            {
+                pack_format++;
+            }
+
+            break;
+        }
+    }
+    while ((depth > 0) && *pack_format != 0);
+
+    return pack_format;
+}
+
+spinel_ssize_t
+spinel_datatype_unpack(const uint8_t *data_ptr, spinel_size_t data_len, const char *pack_format, ...)
+{
+    spinel_ssize_t ret;
+    va_list args;
+    va_start(args, pack_format);
+
+    ret = spinel_datatype_vunpack(data_ptr, data_len, pack_format, args);
+
+    va_end(args);
+    return ret;
+}
+
+spinel_ssize_t
+spinel_datatype_vunpack(const uint8_t *data_ptr, spinel_size_t data_len, const char *pack_format, va_list args)
+{
+    spinel_ssize_t ret = 0;
+
+    for (; *pack_format != 0; pack_format = spinel_next_packed_datatype(pack_format))
+    {
+        if (*pack_format == ')')
+        {
+            // Don't go past the end of a struct.
+            break;
+        }
+
+        switch ((spinel_datatype_t)pack_format[0])
+        {
+        case SPINEL_DATATYPE_BOOL_C:
+        {
+            bool *arg_ptr = va_arg(args, bool *);
+            require_action(data_len >= sizeof(uint8_t), bail, (ret = -1, errno = EOVERFLOW));
+
+            if (arg_ptr)
+            {
+                *arg_ptr = data_ptr[0];
+            }
+
+            ret += sizeof(uint8_t);
+            data_ptr += sizeof(uint8_t);
+            data_len -= sizeof(uint8_t);
+            break;
+        }
+
+        case SPINEL_DATATYPE_INT8_C:
+        case SPINEL_DATATYPE_UINT8_C:
+        {
+            uint8_t *arg_ptr = va_arg(args, uint8_t *);
+            require_action(data_len >= sizeof(uint8_t), bail, (ret = -1, errno = EOVERFLOW));
+
+            if (arg_ptr)
+            {
+                *arg_ptr = data_ptr[0];
+            }
+
+            ret += sizeof(uint8_t);
+            data_ptr += sizeof(uint8_t);
+            data_len -= sizeof(uint8_t);
+            break;
+        }
+
+        case SPINEL_DATATYPE_INT16_C:
+        case SPINEL_DATATYPE_UINT16_C:
+        {
+            uint16_t *arg_ptr = va_arg(args, uint16_t *);
+            require_action(data_len >= sizeof(uint16_t), bail, (ret = -1, errno = EOVERFLOW));
+
+            if (arg_ptr)
+            {
+                *arg_ptr = ((data_ptr[1] << 8) | data_ptr[0]);
+            }
+
+            ret += sizeof(uint16_t);
+            data_ptr += sizeof(uint16_t);
+            data_len -= sizeof(uint16_t);
+            break;
+        }
+
+        case SPINEL_DATATYPE_INT32_C:
+        case SPINEL_DATATYPE_UINT32_C:
+        {
+            uint32_t *arg_ptr = va_arg(args, uint32_t *);
+            require_action(data_len >= sizeof(uint32_t), bail, (ret = -1, errno = EOVERFLOW));
+
+            if (arg_ptr)
+            {
+                *arg_ptr = ((data_ptr[3] << 24) | (data_ptr[2] << 16) | (data_ptr[1] << 8) | data_ptr[0]);
+            }
+
+            ret += sizeof(uint32_t);
+            data_ptr += sizeof(uint32_t);
+            data_len -= sizeof(uint32_t);
+            break;
+        }
+
+        case SPINEL_DATATYPE_IPv6ADDR_C:
+        {
+            spinel_ipv6addr_t **arg_ptr = va_arg(args, spinel_ipv6addr_t **);
+            require_action(data_len >= sizeof(spinel_ipv6addr_t), bail, (ret = -1, errno = EOVERFLOW));
+
+            if (arg_ptr)
+            {
+                *arg_ptr = (spinel_ipv6addr_t *)data_ptr;
+            }
+
+            ret += sizeof(spinel_ipv6addr_t);
+            data_ptr += sizeof(spinel_ipv6addr_t);
+            data_len -= sizeof(spinel_ipv6addr_t);
+            break;
+        }
+
+        case SPINEL_DATATYPE_EUI64_C:
+        {
+            spinel_eui64_t **arg_ptr = va_arg(args, spinel_eui64_t **);
+            require_action(data_len >= sizeof(spinel_eui64_t), bail, (ret = -1, errno = EOVERFLOW));
+
+            if (arg_ptr)
+            {
+                *arg_ptr = (spinel_eui64_t *)data_ptr;
+            }
+
+            ret += sizeof(spinel_eui64_t);
+            data_ptr += sizeof(spinel_eui64_t);
+            data_len -= sizeof(spinel_eui64_t);
+            break;
+        }
+
+        case SPINEL_DATATYPE_EUI48_C:
+        {
+            spinel_eui48_t **arg_ptr = va_arg(args, spinel_eui48_t **);
+            require_action(data_len >= sizeof(spinel_eui48_t), bail, (ret = -1, errno = EOVERFLOW));
+
+            if (arg_ptr)
+            {
+                *arg_ptr = (spinel_eui48_t *)data_ptr;
+            }
+
+            ret += sizeof(spinel_eui48_t);
+            data_ptr += sizeof(spinel_eui48_t);
+            data_len -= sizeof(spinel_eui48_t);
+            break;
+        }
+
+        case SPINEL_DATATYPE_UINT_PACKED_C:
+        {
+            uint32_t *arg_ptr = va_arg(args, uint32_t *);
+            spinel_ssize_t pui_len = spinel_packed_uint_decode(data_ptr, data_len, arg_ptr);
+
+            require(pui_len > 0, bail);
+
+            require(pui_len <= data_len, bail);
+
+            ret += pui_len;
+            data_ptr += pui_len;
+            data_len -= pui_len;
+            break;
+        }
+
+        case SPINEL_DATATYPE_UTF8_C:
+        {
+            const char **arg_ptr = va_arg(args, const char **);
+            ssize_t len = strnlen((const char *)data_ptr, data_len) + 1;
+            require_action((len <= data_len) || (data_ptr[data_len - 1] != 0), bail, (ret = -1, errno = EOVERFLOW));
+
+            if (arg_ptr)
+            {
+                *arg_ptr = (const char *)data_ptr;
+            }
+
+            ret += len;
+            data_ptr += len;
+            data_len -= len;
+            break;
+        }
+
+        case SPINEL_DATATYPE_DATA_C:
+        {
+            spinel_ssize_t pui_len = 0;
+            uint16_t block_len = 0;
+            const uint8_t *block_ptr = data_ptr;
+            const uint8_t **block_ptr_ptr =  va_arg(args, const uint8_t **);
+            unsigned int *block_len_ptr = va_arg(args, unsigned int *);
+            char nextformat = *spinel_next_packed_datatype(pack_format);
+
+            if ((nextformat != 0) && (nextformat != ')'))
+            {
+                pui_len = spinel_datatype_unpack(data_ptr, data_len, SPINEL_DATATYPE_UINT16_S, &block_len);
+                //pui_len = spinel_packed_uint_decode(data_ptr, data_len, &block_len);
+                block_ptr += pui_len;
+
+                require(pui_len > 0, bail);
+                require(block_len < SPINEL_FRAME_MAX_SIZE, bail);
+            }
+            else
+            {
+                block_len = data_len;
+                pui_len = 0;
+            }
+
+            require_action(data_len >= (block_len + pui_len), bail, (ret = -1, errno = EOVERFLOW));
+
+            if (NULL != block_ptr_ptr)
+            {
+                *block_ptr_ptr = block_ptr;
+            }
+
+            if (NULL != block_len_ptr)
+            {
+                *block_len_ptr = block_len;
+            }
+
+            block_len += pui_len;
+            ret += block_len;
+            data_ptr += block_len;
+            data_len -= block_len;
+            break;
+        }
+
+        case SPINEL_DATATYPE_STRUCT_C:
+        {
+            spinel_ssize_t pui_len = 0;
+            uint16_t block_len = 0;
+            unsigned int actual_len = 0;
+            const uint8_t *block_ptr = data_ptr;
+            char nextformat = *spinel_next_packed_datatype(pack_format);
+
+            if ((nextformat != 0) && (nextformat != ')'))
+            {
+                pui_len = spinel_datatype_unpack(data_ptr, data_len, SPINEL_DATATYPE_UINT16_S, &block_len);
+                block_ptr += pui_len;
+
+                require(pui_len > 0, bail);
+                require(block_len < SPINEL_FRAME_MAX_SIZE, bail);
+            }
+            else
+            {
+                block_len = data_len;
+                pui_len = 0;
+            }
+
+            require_action(data_len >= (block_len + pui_len), bail, (ret = -1, errno = EOVERFLOW));
+
+            actual_len = spinel_datatype_vunpack(block_ptr, block_len, pack_format + 2, args);
+
+            require_action((int)actual_len > -1, bail, (ret = -1, errno = EOVERFLOW));
+
+            if (pui_len)
+            {
+                block_len += pui_len;
+            }
+            else
+            {
+                block_len = actual_len;
+            }
+
+            ret += block_len;
+            data_ptr += block_len;
+            data_len -= block_len;
+            break;
+        }
+
+        case '.':
+            // Skip.
+            break;
+
+        case SPINEL_DATATYPE_ARRAY_C:
+        default:
+            // Unsupported Type!
+            ret = -1;
+            errno = EINVAL;
+            goto bail;
+        }
+    }
+
+    return ret;
+
+bail:
+    return ret;
+}
+
+spinel_ssize_t
+spinel_datatype_pack(uint8_t *data_ptr, spinel_size_t data_len_max, const char *pack_format, ...)
+{
+    int ret;
+    va_list args;
+    va_start(args, pack_format);
+
+    ret = spinel_datatype_vpack(data_ptr, data_len_max, pack_format, args);
+
+    va_end(args);
+    return ret;
+}
+
+spinel_ssize_t
+spinel_datatype_vpack(uint8_t *data_ptr, spinel_size_t data_len_max, const char *pack_format, va_list args)
+{
+    spinel_ssize_t ret = 0;
+
+    for (; *pack_format != 0; pack_format = spinel_next_packed_datatype(pack_format))
+    {
+        if (*pack_format == ')')
+        {
+            // Don't go past the end of a struct.
+            break;
+        }
+
+        switch ((spinel_datatype_t)*pack_format)
+        {
+        case SPINEL_DATATYPE_BOOL_C:
+        {
+            bool arg = va_arg(args, int);
+            ret += sizeof(uint8_t);
+
+            if (data_len_max >= sizeof(uint8_t))
+            {
+                data_ptr[0] = (arg != false);
+                data_ptr += sizeof(uint8_t);
+                data_len_max -= sizeof(uint8_t);
+            }
+            else
+            {
+                data_len_max = 0;
+            }
+
+            break;
+        }
+
+        case SPINEL_DATATYPE_INT8_C:
+        case SPINEL_DATATYPE_UINT8_C:
+        {
+            uint8_t arg = va_arg(args, int);
+            ret += sizeof(uint8_t);
+
+            if (data_len_max >= sizeof(uint8_t))
+            {
+                data_ptr[0] = arg;
+                data_ptr += sizeof(uint8_t);
+                data_len_max -= sizeof(uint8_t);
+            }
+            else
+            {
+                data_len_max = 0;
+            }
+
+            break;
+        }
+
+        case SPINEL_DATATYPE_INT16_C:
+        case SPINEL_DATATYPE_UINT16_C:
+        {
+            uint16_t arg = va_arg(args, int);
+            ret += sizeof(uint16_t);
+
+            if (data_len_max >= sizeof(uint16_t))
+            {
+                data_ptr[1] = (arg >> 8);
+                data_ptr[0] = (arg >> 0);
+                data_ptr += sizeof(uint16_t);
+                data_len_max -= sizeof(uint16_t);
+            }
+            else
+            {
+                data_len_max = 0;
+            }
+
+            break;
+        }
+
+        case SPINEL_DATATYPE_INT32_C:
+        case SPINEL_DATATYPE_UINT32_C:
+        {
+            uint32_t arg = va_arg(args, int);
+            ret += sizeof(uint32_t);
+
+            if (data_len_max >= sizeof(uint32_t))
+            {
+                data_ptr[3] = (arg >> 24);
+                data_ptr[2] = (arg >> 16);
+                data_ptr[1] = (arg >> 8);
+                data_ptr[0] = (arg >> 0);
+                data_ptr += sizeof(uint32_t);
+                data_len_max -= sizeof(uint32_t);
+            }
+            else
+            {
+                data_len_max = 0;
+            }
+
+            break;
+        }
+
+        case SPINEL_DATATYPE_IPv6ADDR_C:
+        {
+            spinel_ipv6addr_t *arg = va_arg(args, spinel_ipv6addr_t *);
+            ret += sizeof(spinel_ipv6addr_t);
+
+            if (data_len_max >= sizeof(spinel_ipv6addr_t))
+            {
+                *(spinel_ipv6addr_t *)data_ptr = *arg;
+                data_ptr += sizeof(spinel_ipv6addr_t);
+                data_len_max -= sizeof(spinel_ipv6addr_t);
+            }
+            else
+            {
+                data_len_max = 0;
+            }
+
+            break;
+        }
+
+        case SPINEL_DATATYPE_EUI48_C:
+        {
+            spinel_eui48_t *arg = va_arg(args, spinel_eui48_t *);
+            ret += sizeof(spinel_eui48_t);
+
+            if (data_len_max >= sizeof(spinel_eui48_t))
+            {
+                *(spinel_eui48_t *)data_ptr = *arg;
+                data_ptr += sizeof(spinel_eui48_t);
+                data_len_max -= sizeof(spinel_eui48_t);
+            }
+            else
+            {
+                data_len_max = 0;
+            }
+
+            break;
+        }
+
+        case SPINEL_DATATYPE_EUI64_C:
+        {
+            spinel_eui64_t *arg = va_arg(args, spinel_eui64_t *);
+            ret += sizeof(spinel_eui64_t);
+
+            if (data_len_max >= sizeof(spinel_eui64_t))
+            {
+                *(spinel_eui64_t *)data_ptr = *arg;
+                data_ptr += sizeof(spinel_eui64_t);
+                data_len_max -= sizeof(spinel_eui64_t);
+            }
+            else
+            {
+                data_len_max = 0;
+            }
+
+            break;
+        }
+
+        case SPINEL_DATATYPE_UINT_PACKED_C:
+        {
+            uint32_t arg = va_arg(args, uint32_t);
+            spinel_ssize_t encoded_size = spinel_packed_uint_encode(data_ptr, data_len_max, arg);
+            ret += encoded_size;
+
+            if (data_len_max >= encoded_size)
+            {
+                data_ptr += encoded_size;
+                data_len_max -= encoded_size;
+            }
+            else
+            {
+                data_len_max = 0;
+            }
+
+            break;
+        }
+
+        case SPINEL_DATATYPE_UTF8_C:
+        {
+            const char *string_arg = va_arg(args, const char *);
+            size_t string_arg_len = 0;
+
+            if (string_arg)
+            {
+                string_arg_len = strlen(string_arg) + 1;
+            }
+            else
+            {
+                string_arg = "";
+                string_arg_len = 1;
+            }
+
+            ret += string_arg_len;
+
+            if (data_len_max >= string_arg_len)
+            {
+                memcpy(data_ptr, string_arg, string_arg_len);
+
+                data_ptr += string_arg_len;
+                data_len_max -= string_arg_len;
+            }
+            else
+            {
+                data_len_max = 0;
+            }
+
+            break;
+        }
+
+        case SPINEL_DATATYPE_DATA_C:
+        {
+            uint8_t *arg = va_arg(args, uint8_t *);
+            uint32_t data_size_arg = va_arg(args, uint32_t);
+            spinel_ssize_t size_len = 0;
+            char nextformat = *spinel_next_packed_datatype(pack_format);
+
+            if (nextformat != 0 && nextformat != ')')
+            {
+                size_len = spinel_datatype_pack(data_ptr, data_len_max, SPINEL_DATATYPE_UINT16_S, data_size_arg);
+                require_action(size_len > 0, bail, {ret = -1; errno = EINVAL;});
+            }
+
+            ret += size_len + data_size_arg;
+
+            if (data_len_max >= size_len + data_size_arg)
+            {
+                data_ptr += size_len;
+                data_len_max -= size_len;
+
+                memcpy(data_ptr, arg, data_size_arg);
+
+                data_ptr += data_size_arg;
+                data_len_max -= data_size_arg;
+            }
+            else
+            {
+                data_len_max = 0;
+            }
+
+            break;
+        }
+
+        case SPINEL_DATATYPE_STRUCT_C:
+        {
+            spinel_ssize_t struct_len = 0;
+            spinel_ssize_t size_len = 0;
+            char nextformat = *spinel_next_packed_datatype(pack_format);
+
+            require_action(pack_format[1] == '(', bail, {ret = -1; errno = EINVAL;});
+
+            // First we figure out the size of the struct
+            {
+                va_list subargs;
+                va_copy(subargs, args);
+                struct_len = spinel_datatype_vpack(NULL, 0, pack_format + 2, subargs);
+                va_end(subargs);
+            }
+
+            if (nextformat != 0 && nextformat != ')')
+            {
+                size_len = spinel_datatype_pack(data_ptr, data_len_max, SPINEL_DATATYPE_UINT16_S, struct_len);
+                require_action(size_len > 0, bail, {ret = -1; errno = EINVAL;});
+            }
+
+            ret += size_len + struct_len;
+
+            if (struct_len + size_len <= data_len_max)
+            {
+                data_ptr += size_len;
+                data_len_max -= size_len;
+
+                struct_len = spinel_datatype_vpack(data_ptr, data_len_max, pack_format + 2, args);
+
+                data_ptr += struct_len;
+                data_len_max -= struct_len;
+            }
+            else
+            {
+                data_len_max = 0;
+            }
+
+            break;
+        }
+
+        case '.':
+            // Skip.
+            break;
+
+        default:
+            // Unsupported Type!
+            ret = -1;
+            errno = EINVAL;
+            goto bail;
+
+        }
+    }
+
+bail:
+    return ret;
+}
+
+// ----------------------------------------------------------------------------
+// MARK: -
+
+const char *
+spinel_prop_key_to_cstr(spinel_prop_key_t prop_key)
+{
+    const char *ret = "UNKNOWN";
+
+    switch (prop_key)
+    {
+    case SPINEL_PROP_LAST_STATUS:
+        ret = "PROP_LAST_STATUS";
+        break;
+
+    case SPINEL_PROP_PROTOCOL_VERSION:
+        ret = "PROP_PROTOCOL_VERSION";
+        break;
+
+    case SPINEL_PROP_INTERFACE_TYPE:
+        ret = "PROP_INTERFACE_TYPE";
+        break;
+
+    case SPINEL_PROP_VENDOR_ID:
+        ret = "PROP_VENDOR_ID";
+        break;
+
+    case SPINEL_PROP_CAPS:
+        ret = "PROP_CAPS";
+        break;
+
+    case SPINEL_PROP_NCP_VERSION:
+        ret = "PROP_NCP_VERSION";
+        break;
+
+    case SPINEL_PROP_INTERFACE_COUNT:
+        ret = "PROP_INTERFACE_COUNT";
+        break;
+
+    case SPINEL_PROP_POWER_STATE:
+        ret = "PROP_POWER_STATE";
+        break;
+
+    case SPINEL_PROP_HWADDR:
+        ret = "PROP_HWADDR";
+        break;
+
+    case SPINEL_PROP_LOCK:
+        ret = "PROP_LOCK";
+        break;
+
+    case SPINEL_PROP_HBO_MEM_MAX:
+        ret = "PROP_HBO_MEM_MAX";
+        break;
+
+    case SPINEL_PROP_HBO_BLOCK_MAX:
+        ret = "PROP_HBO_BLOCK_MAX";
+        break;
+
+    case SPINEL_PROP_STREAM_DEBUG:
+        ret = "PROP_STREAM_DEBUG";
+        break;
+
+    case SPINEL_PROP_STREAM_RAW:
+        ret = "PROP_STREAM_RAW";
+        break;
+
+    case SPINEL_PROP_STREAM_NET:
+        ret = "PROP_STREAM_NET";
+        break;
+
+    case SPINEL_PROP_STREAM_NET_INSECURE:
+        ret = "PROP_STREAM_NET_INSECURE";
+        break;
+
+    case SPINEL_PROP_PHY_ENABLED:
+        ret = "PROP_PHY_ENABLED";
+        break;
+
+    case SPINEL_PROP_PHY_CHAN:
+        ret = "PROP_PHY_CHAN";
+        break;
+
+    case SPINEL_PROP_PHY_CHAN_SUPPORTED:
+        ret = "PROP_PHY_CHAN_SUPPORTED";
+        break;
+
+    case SPINEL_PROP_PHY_FREQ:
+        ret = "PROP_PHY_FREQ";
+        break;
+
+    case SPINEL_PROP_PHY_CCA_THRESHOLD:
+        ret = "PROP_PHY_CCA_THRESHOLD";
+        break;
+
+    case SPINEL_PROP_PHY_TX_POWER:
+        ret = "PROP_PHY_TX_POWER";
+        break;
+
+    case SPINEL_PROP_PHY_RSSI:
+        ret = "PROP_PHY_RSSI";
+        break;
+
+    case SPINEL_PROP_PHY_RAW_STREAM_ENABLED:
+        ret = "PROP_PHY_RAW_STREAM_ENABLED";
+        break;
+
+    case SPINEL_PROP_PHY_MODE:
+        ret = "PROP_PHY_MODE";
+        break;
+
+    case SPINEL_PROP_MAC_SCAN_STATE:
+        ret = "PROP_MAC_SCAN_STATE";
+        break;
+
+    case SPINEL_PROP_MAC_SCAN_MASK:
+        ret = "PROP_MAC_SCAN_MASK";
+        break;
+
+    case SPINEL_PROP_MAC_SCAN_BEACON:
+        ret = "PROP_MAC_SCAN_BEACON";
+        break;
+
+    case SPINEL_PROP_MAC_SCAN_PERIOD:
+        ret = "PROP_MAC_SCAN_PERIOD";
+        break;
+
+    case SPINEL_PROP_MAC_15_4_LADDR:
+        ret = "PROP_MAC_15_4_LADDR";
+        break;
+
+    case SPINEL_PROP_MAC_15_4_SADDR:
+        ret = "PROP_MAC_15_4_SADDR";
+        break;
+
+    case SPINEL_PROP_MAC_15_4_PANID:
+        ret = "PROP_MAC_15_4_PANID";
+        break;
+
+    case SPINEL_PROP_NET_SAVED:
+        ret = "PROP_NET_SAVED";
+        break;
+
+    case SPINEL_PROP_NET_ENABLED:
+        ret = "PROP_NET_ENABLED";
+        break;
+
+    case SPINEL_PROP_NET_STATE:
+        ret = "PROP_NET_STATE";
+        break;
+
+    case SPINEL_PROP_NET_ROLE:
+        ret = "PROP_NET_ROLE";
+        break;
+
+    case SPINEL_PROP_NET_NETWORK_NAME:
+        ret = "PROP_NET_NETWORK_NAME";
+        break;
+
+    case SPINEL_PROP_NET_XPANID:
+        ret = "PROP_NET_XPANID";
+        break;
+
+    case SPINEL_PROP_NET_MASTER_KEY:
+        ret = "PROP_NET_MASTER_KEY";
+        break;
+
+    case SPINEL_PROP_NET_KEY_SEQUENCE:
+        ret = "PROP_NET_KEY_SEQUENCE";
+        break;
+
+    case SPINEL_PROP_NET_PARTITION_ID:
+        ret = "PROP_NET_PARTITION_ID";
+        break;
+
+    case SPINEL_PROP_THREAD_LEADER_ADDR:
+        ret = "PROP_THREAD_LEADER_ADDR";
+        break;
+
+    case SPINEL_PROP_THREAD_LEADER_RID:
+        ret = "PROP_THREAD_LEADER_RID";
+        break;
+
+    case SPINEL_PROP_THREAD_LEADER_WEIGHT:
+        ret = "PROP_THREAD_LEADER_WEIGHT";
+        break;
+
+    case SPINEL_PROP_THREAD_LOCAL_LEADER_WEIGHT:
+        ret = "PROP_THREAD_LOCAL_LEADER_WEIGHT";
+        break;
+
+    case SPINEL_PROP_THREAD_NETWORK_DATA_VERSION:
+        ret = "PROP_THREAD_NETWORK_DATA_VERSION";
+        break;
+
+    case SPINEL_PROP_THREAD_STABLE_NETWORK_DATA_VERSION:
+        ret = "PROP_THREAD_STABLE_NETWORK_DATA_VERSION";
+        break;
+
+    case SPINEL_PROP_THREAD_NETWORK_DATA:
+        ret = "PROP_THREAD_NETWORK_DATA";
+        break;
+
+    case SPINEL_PROP_THREAD_CHILD_TABLE:
+        ret = "PROP_THREAD_CHILD_TABLE";
+        break;
+
+    case SPINEL_PROP_IPV6_LL_ADDR:
+        ret = "PROP_IPV6_LL_ADDR";
+        break;
+
+    case SPINEL_PROP_IPV6_ML_ADDR:
+        ret = "PROP_IPV6_ML_ADDR";
+        break;
+
+    case SPINEL_PROP_IPV6_ML_PREFIX:
+        ret = "PROP_IPV6_ML_PREFIX";
+        break;
+
+    case SPINEL_PROP_IPV6_ADDRESS_TABLE:
+        ret = "PROP_IPV6_ADDRESS_TABLE";
+        break;
+
+    case SPINEL_PROP_IPV6_ROUTE_TABLE:
+        ret = "PROP_IPV6_ROUTE_TABLE";
+        break;
+
+    case SPINEL_PROP_THREAD_PARENT:
+        ret = "SPINEL_PROP_THREAD_PARENT";
+        break;
+
+    case SPINEL_PROP_THREAD_STABLE_NETWORK_DATA:
+        ret = "SPINEL_PROP_THREAD_STABLE_NETWORK_DATA";
+        break;
+
+    case SPINEL_PROP_THREAD_ON_MESH_NETS:
+        ret = "SPINEL_PROP_THREAD_ON_MESH_NETS";
+        break;
+
+    case SPINEL_PROP_THREAD_LOCAL_ROUTES:
+        ret = "PROP_THREAD_LOCAL_ROUTES";
+        break;
+
+    case SPINEL_PROP_THREAD_ASSISTING_PORTS:
+        ret = "PROP_THREAD_ASSISTING_PORTS";
+        break;
+
+    default:
+        break;
+    }
+
+    return ret;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+#if SPINEL_SELF_TEST
+
+#include <stdlib.h>
+#include <string.h>
+
+
+int
+main(void)
+{
+    int ret = -1;
+
+    const char static_string[] = "static_string";
+    uint8_t buffer[1024];
+    ssize_t len;
+
+    len = spinel_datatype_pack(buffer, sizeof(buffer), "CiiLU", 0x88, 9, 0xA3, 0xDEADBEEF, static_string);
+
+    if (len != 22)
+    {
+        printf("error:%d: len != 22; (%d)\n", __LINE__, (int)len);
+        goto bail;
+    }
+
+    {
+        uint8_t c = 0;
+        unsigned int i1 = 0;
+        unsigned int i2 = 0;
+        uint32_t l = 0;
+        const char *str = NULL;
+
+        len = spinel_datatype_unpack(buffer, (spinel_size_t)len, "CiiLU", &c, &i1, &i2, &l, &str);
+
+        if (len != 22)
+        {
+            printf("error:%d: len != 22; (%d)\n", __LINE__, (int)len);
+            goto bail;
+        }
+
+        if (c != 0x88)
+        {
+            printf("error: x != 0x88; (%d)\n", c);
+            goto bail;
+        }
+
+        if (i1 != 9)
+        {
+            printf("error: i1 != 9; (%d)\n", i1);
+            goto bail;
+        }
+
+        if (i2 != 0xA3)
+        {
+            printf("error: i2 != 0xA3; (0x%02X)\n", i2);
+            goto bail;
+        }
+
+        if (l != 0xDEADBEEF)
+        {
+            printf("error: l != 0xDEADBEEF; (0x%08X)\n", l);
+            goto bail;
+        }
+
+        if (strcmp(str, static_string) != 0)
+        {
+            printf("error:%d: strcmp(str,static_string) != 0\n", __LINE__);
+            goto bail;
+        }
+    }
+
+    // -----------------------------------
+
+    memset(buffer, 0xAA, sizeof(buffer));
+
+    len = spinel_datatype_pack(buffer, sizeof(buffer), "CiT(iL)U", 0x88, 9, 0xA3, 0xDEADBEEF, static_string);
+
+    if (len != 24)
+    {
+        printf("error:%d: len != 24; (%d)\n", __LINE__, (int)len);
+        goto bail;
+    }
+
+
+
+    {
+        uint8_t c = 0;
+        unsigned int i1 = 0;
+        unsigned int i2 = 0;
+        uint32_t l = 0;
+        const char *str = NULL;
+
+        len = spinel_datatype_unpack(buffer, (spinel_size_t)len, "CiT(iL)U", &c, &i1, &i2, &l, &str);
+
+        if (len != 24)
+        {
+            printf("error:%d: len != 24; (%d)\n", __LINE__, (int)len);
+            goto bail;
+        }
+
+        if (c != 0x88)
+        {
+            printf("error: x != 0x88; (%d)\n", c);
+            goto bail;
+        }
+
+        if (i1 != 9)
+        {
+            printf("error: i1 != 9; (%d)\n", i1);
+            goto bail;
+        }
+
+        if (i2 != 0xA3)
+        {
+            printf("error: i2 != 0xA3; (0x%02X)\n", i2);
+            goto bail;
+        }
+
+        if (l != 0xDEADBEEF)
+        {
+            printf("error: l != 0xDEADBEEF; (0x%08X)\n", l);
+            goto bail;
+        }
+
+        if (strcmp(str, static_string) != 0)
+        {
+            printf("error:%d: strcmp(str,static_string) != 0\n", __LINE__);
+            goto bail;
+        }
+    }
+
+
+
+    printf("OK\n");
+    ret = 0;
+    return ret;
+
+bail:
+    printf("FAILURE\n");
+    return ret;
+}
+
+#endif // #if SPINEL_SELF_TEST
diff --git a/third_party/openthread/src/ncp/spinel.h b/third_party/openthread/src/ncp/spinel.h
new file mode 100644
index 0000000..1e19415
--- /dev/null
+++ b/third_party/openthread/src/ncp/spinel.h
@@ -0,0 +1,447 @@
+/*
+ *    Copyright (c) 2016, Nest Labs, Inc.
+ *    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.
+ */
+
+#ifndef SPINEL_HEADER_INCLUDED
+#define SPINEL_HEADER_INCLUDED 1
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+
+__BEGIN_DECLS
+
+// ----------------------------------------------------------------------------
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+# if defined(__GNUC__) && !SPINEL_EMBEDDED
+#  define SPINEL_API_EXTERN              extern __attribute__ ((visibility ("default")))
+#  define SPINEL_API_NONNULL_ALL         __attribute__((nonnull))
+#  define SPINEL_API_WARN_UNUSED_RESULT  __attribute__((warn_unused_result))
+# endif // ifdef __GNUC__
+#endif // ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+#ifndef SPINEL_API_EXTERN
+# define SPINEL_API_EXTERN               extern
+#endif
+
+#ifndef SPINEL_API_NONNULL_ALL
+# define SPINEL_API_NONNULL_ALL
+#endif
+
+#ifndef SPINEL_API_WARN_UNUSED_RESULT
+# define SPINEL_API_WARN_UNUSED_RESULT
+#endif
+
+// ----------------------------------------------------------------------------
+
+#define SPINEL_PROTOCOL_VERSION_THREAD_MAJOR    4
+#define SPINEL_PROTOCOL_VERSION_THREAD_MINOR    0
+
+#define SPINEL_FRAME_MAX_SIZE                   1300
+
+// ----------------------------------------------------------------------------
+
+typedef enum
+{
+    SPINEL_STATUS_OK                = 0, ///< Operation has completed successfully.
+    SPINEL_STATUS_FAILURE           = 1, ///< Operation has failed for some undefined reason.
+
+    SPINEL_STATUS_UNIMPLEMENTED     = 2, ///< Given operation has not been implemented.
+    SPINEL_STATUS_INVALID_ARGUMENT  = 3, ///< An argument to the operation is invalid.
+    SPINEL_STATUS_INVALID_STATE     = 4, ///< This operation is invalid for the current device state.
+    SPINEL_STATUS_INVALID_COMMAND   = 5, ///< This command is not recognized.
+    SPINEL_STATUS_INVALID_INTERFACE = 6, ///< This interface is not supported.
+    SPINEL_STATUS_INTERNAL_ERROR    = 7, ///< An internal runtime error has occured.
+    SPINEL_STATUS_SECURITY_ERROR    = 8, ///< A security/authentication error has occured.
+    SPINEL_STATUS_PARSE_ERROR       = 9, ///< A error has occured while parsing the command.
+    SPINEL_STATUS_IN_PROGRESS       = 10, ///< This operation is in progress.
+    SPINEL_STATUS_NOMEM             = 11, ///< Operation prevented due to memory pressure.
+    SPINEL_STATUS_BUSY              = 12, ///< The device is currently performing an mutually exclusive operation
+    SPINEL_STATUS_PROP_NOT_FOUND    = 12, ///< The given property is not recognized.
+    SPINEL_STATUS_DROPPED           = 14, ///< A/The packet was dropped.
+    SPINEL_STATUS_EMPTY             = 15, ///< The result of the operation is empty.
+    SPINEL_STATUS_CMD_TOO_BIG       = 16, ///< The command was too large to fit in the internal buffer.
+    SPINEL_STATUS_NO_ACK            = 17, ///< The packet was not acknowledged.
+    SPINEL_STATUS_CCA_FAILURE       = 18, ///< The packet was not sent due to a CCA failure.
+
+    SPINEL_STATUS_RESET__BEGIN      = 112,
+    SPINEL_STATUS_RESET_POWER_ON    = SPINEL_STATUS_RESET__BEGIN + 0,
+    SPINEL_STATUS_RESET_EXTERNAL    = SPINEL_STATUS_RESET__BEGIN + 1,
+    SPINEL_STATUS_RESET_SOFTWARE    = SPINEL_STATUS_RESET__BEGIN + 2,
+    SPINEL_STATUS_RESET_FAULT       = SPINEL_STATUS_RESET__BEGIN + 3,
+    SPINEL_STATUS_RESET_CRASH       = SPINEL_STATUS_RESET__BEGIN + 4,
+    SPINEL_STATUS_RESET_ASSERT      = SPINEL_STATUS_RESET__BEGIN + 5,
+    SPINEL_STATUS_RESET_OTHER       = SPINEL_STATUS_RESET__BEGIN + 6,
+    SPINEL_STATUS_RESET__END        = 128,
+
+
+    SPINEL_STATUS_VENDOR__BEGIN     = 15360,
+    SPINEL_STATUS_VENDOR__END       = 16384,
+
+    SPINEL_STATUS_EXPERIMENTAL__BEGIN = 2000000,
+    SPINEL_STATUS_EXPERIMENTAL__END   = 2097152,
+} spinel_status_t;
+
+typedef enum
+{
+    SPINEL_NET_STATE_OFFLINE   = 0,
+    SPINEL_NET_STATE_DETACHED  = 1,
+    SPINEL_NET_STATE_ATTACHING = 2,
+    SPINEL_NET_STATE_ATTACHED  = 3,
+} spinel_net_state_t;
+
+typedef enum
+{
+    SPINEL_NET_ROLE_NONE       = 0,
+    SPINEL_NET_ROLE_CHILD      = 1,
+    SPINEL_NET_ROLE_ROUTER     = 2,
+    SPINEL_NET_ROLE_LEADER     = 3,
+} spinel_net_role_t;
+
+typedef enum
+{
+    SPINEL_SCAN_STATE_IDLE     = 0,
+    SPINEL_SCAN_STATE_BEACON   = 1,
+    SPINEL_SCAN_STATE_ENERGY   = 2,
+} spinel_scan_state_t;
+
+typedef enum
+{
+    SPINEL_POWER_STATE_OFFLINE    = 0,
+    SPINEL_POWER_STATE_DEEP_SLEEP = 1,
+    SPINEL_POWER_STATE_STANDBY    = 2,
+    SPINEL_POWER_STATE_LOW_POWER  = 3,
+    SPINEL_POWER_STATE_ONLINE     = 4,
+} spinel_power_state_t;
+
+enum
+{
+    SPINEL_PROTOCOL_TYPE_ZIGBEE    = 1,
+    SPINEL_PROTOCOL_TYPE_ZIGBEE_IP = 2,
+    SPINEL_PROTOCOL_TYPE_THREAD    = 3,
+};
+
+enum
+{
+    SPINEL_PHY_MODE_NORMAL       = 0, ///< Normal PHY filtering is in place.
+    SPINEL_PHY_MODE_PROMISCUOUS  = 1, ///< All PHY packets matching network are passed up the stack.
+    SPINEL_PHY_MODE_MONITOR      = 2, ///< All decoded PHY packets are passed up the stack.
+};
+
+typedef struct
+{
+    uint8_t bytes[8];
+} spinel_eui64_t;
+
+typedef struct
+{
+    uint8_t bytes[8];
+} spinel_net_xpanid_t;
+
+typedef struct
+{
+    uint8_t bytes[6];
+} spinel_eui48_t;
+
+typedef struct in6_addr spinel_ipv6addr_t;
+typedef int spinel_ssize_t;
+typedef unsigned int spinel_size_t;
+typedef uint8_t spinel_tid_t;
+typedef unsigned int spinel_cid_t;
+
+enum
+{
+    SPINEL_CMD_NOOP                 = 0,
+    SPINEL_CMD_RESET                = 1,
+    SPINEL_CMD_PROP_VALUE_GET       = 2,
+    SPINEL_CMD_PROP_VALUE_SET       = 3,
+    SPINEL_CMD_PROP_VALUE_INSERT    = 4,
+    SPINEL_CMD_PROP_VALUE_REMOVE    = 5,
+    SPINEL_CMD_PROP_VALUE_IS        = 6,
+    SPINEL_CMD_PROP_VALUE_INSERTED  = 7,
+    SPINEL_CMD_PROP_VALUE_REMOVED   = 8,
+
+    SPINEL_CMD_NET_SAVE             = 9,
+    SPINEL_CMD_NET_CLEAR            = 10,
+    SPINEL_CMD_NET_RECALL           = 11,
+
+    SPINEL_CMD_HBO_OFFLOAD          = 12,
+    SPINEL_CMD_HBO_RECLAIM          = 13,
+    SPINEL_CMD_HBO_DROP             = 14,
+    SPINEL_CMD_HBO_OFFLOADED        = 15,
+    SPINEL_CMD_HBO_RECLAIMED        = 16,
+    SPINEL_CMD_HBO_DROPED           = 17,
+
+    SPINEL_CMD_NEST__BEGIN          = 15296,
+    SPINEL_CMD_NEST__END            = 15360,
+
+    SPINEL_CMD_VENDOR__BEGIN        = 15360,
+    SPINEL_CMD_VENDOR__END          = 16384,
+
+    SPINEL_CMD_EXPERIMENTAL__BEGIN  = 2000000,
+    SPINEL_CMD_EXPERIMENTAL__END    = 2097152,
+};
+
+enum
+{
+    SPINEL_CAP_LOCK                  = 1,
+    SPINEL_CAP_NET_SAVE              = 2,
+    SPINEL_CAP_HBO                   = 3,
+    SPINEL_CAP_POWER_SAVE            = 4,
+
+    SPINEL_CAP_802_15_4__BEGIN       = 16,
+    SPINEL_CAP_802_15_4_2003         = (SPINEL_CAP_802_15_4__BEGIN + 0),
+    SPINEL_CAP_802_15_4_2006         = (SPINEL_CAP_802_15_4__BEGIN + 1),
+    SPINEL_CAP_802_15_4_2011         = (SPINEL_CAP_802_15_4__BEGIN + 2),
+    SPINEL_CAP_802_15_4_PIB          = (SPINEL_CAP_802_15_4__BEGIN + 5),
+    SPINEL_CAP_802_15_4_2450MHZ_OQPSK = (SPINEL_CAP_802_15_4__BEGIN + 8),
+    SPINEL_CAP_802_15_4_915MHZ_OQPSK = (SPINEL_CAP_802_15_4__BEGIN + 9),
+    SPINEL_CAP_802_15_4_868MHZ_OQPSK = (SPINEL_CAP_802_15_4__BEGIN + 10),
+    SPINEL_CAP_802_15_4_915MHZ_BPSK  = (SPINEL_CAP_802_15_4__BEGIN + 11),
+    SPINEL_CAP_802_15_4_868MHZ_BPSK  = (SPINEL_CAP_802_15_4__BEGIN + 12),
+    SPINEL_CAP_802_15_4_915MHZ_ASK   = (SPINEL_CAP_802_15_4__BEGIN + 13),
+    SPINEL_CAP_802_15_4_868MHZ_ASK   = (SPINEL_CAP_802_15_4__BEGIN + 14),
+    SPINEL_CAP_802_15_4__END         = 32,
+
+    SPINEL_CAP_ROLE__BEGIN           = 48,
+    SPINEL_CAP_ROLE_ROUTER           = (SPINEL_CAP_ROLE__BEGIN + 0),
+    SPINEL_CAP_ROLE_SLEEPY           = (SPINEL_CAP_ROLE__BEGIN + 1),
+    SPINEL_CAP_ROLE__END             = 52,
+
+    SPINEL_CAP_NET__BEGIN            = 52,
+    SPINEL_CAP_NET_THREAD_1_0        = (SPINEL_CAP_NET__BEGIN + 0),
+    SPINEL_CAP_NET__END              = 64,
+
+    SPINEL_CAP_NEST__BEGIN           = 15296,
+    SPINEL_CAP_NEST_LEGACY_INTERFACE = (SPINEL_CAP_NEST__BEGIN + 0),
+    SPINEL_CAP_NEST_LEGACY_NET_WAKE  = (SPINEL_CAP_NEST__BEGIN + 1),
+    SPINEL_CAP_NEST_TRANSMIT_HOOK    = (SPINEL_CAP_NEST__BEGIN + 2),
+    SPINEL_CAP_NEST__END             = 15360,
+
+    SPINEL_CAP_VENDOR__BEGIN         = 15360,
+    SPINEL_CAP_VENDOR__END           = 16384,
+
+    SPINEL_CAP_EXPERIMENTAL__BEGIN   = 2000000,
+    SPINEL_CAP_EXPERIMENTAL__END     = 2097152,
+};
+
+typedef enum
+{
+    SPINEL_PROP_LAST_STATUS             = 0,        ///< status [i]
+    SPINEL_PROP_PROTOCOL_VERSION        = 1,        ///< major, minor [i,i]
+    SPINEL_PROP_NCP_VERSION             = 2,        ///< version string [U]
+    SPINEL_PROP_INTERFACE_TYPE          = 3,        ///< [i]
+    SPINEL_PROP_VENDOR_ID               = 4,        ///< [i]
+    SPINEL_PROP_CAPS                    = 5,        ///< capability list [A(i)]
+    SPINEL_PROP_INTERFACE_COUNT         = 6,        ///< Interface count [C]
+    SPINEL_PROP_POWER_STATE             = 7,        ///< PowerState [C]
+    SPINEL_PROP_HWADDR                  = 8,        ///< PermEUI64 [E]
+    SPINEL_PROP_LOCK                    = 9,        ///< PropLock [b]
+    SPINEL_PROP_HBO_MEM_MAX             = 10,       ///< Max offload mem [S]
+    SPINEL_PROP_HBO_BLOCK_MAX           = 11,       ///< Max offload block [S]
+
+    SPINEL_PROP_PHY__BEGIN              = 0x20,
+    SPINEL_PROP_PHY_ENABLED             = SPINEL_PROP_PHY__BEGIN + 0, ///< [b]
+    SPINEL_PROP_PHY_CHAN                = SPINEL_PROP_PHY__BEGIN + 1, ///< [C]
+    SPINEL_PROP_PHY_CHAN_SUPPORTED      = SPINEL_PROP_PHY__BEGIN + 2, ///< [A(C)]
+    SPINEL_PROP_PHY_FREQ                = SPINEL_PROP_PHY__BEGIN + 3, ///< kHz [L]
+    SPINEL_PROP_PHY_CCA_THRESHOLD       = SPINEL_PROP_PHY__BEGIN + 4, ///< dBm [c]
+    SPINEL_PROP_PHY_TX_POWER            = SPINEL_PROP_PHY__BEGIN + 5, ///< [c]
+    SPINEL_PROP_PHY_RSSI                = SPINEL_PROP_PHY__BEGIN + 6, ///< dBm [c]
+    SPINEL_PROP_PHY_RAW_STREAM_ENABLED  = SPINEL_PROP_PHY__BEGIN + 7, ///< [C]
+    SPINEL_PROP_PHY_MODE                = SPINEL_PROP_PHY__BEGIN + 8, ///< [C]
+    SPINEL_PROP_PHY__END                = 0x30,
+
+    SPINEL_PROP_MAC__BEGIN           = 0x30,
+    SPINEL_PROP_MAC_SCAN_STATE       = SPINEL_PROP_MAC__BEGIN + 0, ///< [C]
+    SPINEL_PROP_MAC_SCAN_MASK        = SPINEL_PROP_MAC__BEGIN + 1, ///< [A(C)]
+    SPINEL_PROP_MAC_SCAN_PERIOD      = SPINEL_PROP_MAC__BEGIN + 2, ///< ms-per-channel [S]
+    SPINEL_PROP_MAC_SCAN_BEACON      = SPINEL_PROP_MAC__BEGIN + 3, ///< chan,rssi,(laddr,saddr,panid,lqi),(proto,xtra) [CcT(ESSC.)T(i).]
+    SPINEL_PROP_MAC_15_4_LADDR       = SPINEL_PROP_MAC__BEGIN + 4, ///< [E]
+    SPINEL_PROP_MAC_15_4_SADDR       = SPINEL_PROP_MAC__BEGIN + 5, ///< [S]
+    SPINEL_PROP_MAC_15_4_PANID       = SPINEL_PROP_MAC__BEGIN + 6, ///< [S]
+    SPINEL_PROP_MAC__END             = 0x40,
+
+    SPINEL_PROP_NET__BEGIN           = 0x40,
+    SPINEL_PROP_NET_SAVED            = SPINEL_PROP_NET__BEGIN + 0, ///< [b]
+    SPINEL_PROP_NET_ENABLED          = SPINEL_PROP_NET__BEGIN + 1, ///< [b]
+    SPINEL_PROP_NET_STATE            = SPINEL_PROP_NET__BEGIN + 2, ///< [C]
+    SPINEL_PROP_NET_ROLE             = SPINEL_PROP_NET__BEGIN + 3, ///< [C]
+    SPINEL_PROP_NET_NETWORK_NAME     = SPINEL_PROP_NET__BEGIN + 4, ///< [U]
+    SPINEL_PROP_NET_XPANID           = SPINEL_PROP_NET__BEGIN + 5, ///< [D]
+    SPINEL_PROP_NET_MASTER_KEY       = SPINEL_PROP_NET__BEGIN + 6, ///< [D]
+    SPINEL_PROP_NET_KEY_SEQUENCE     = SPINEL_PROP_NET__BEGIN + 7, ///< [L]
+    SPINEL_PROP_NET_PARTITION_ID     = SPINEL_PROP_NET__BEGIN + 8, ///< [L]
+    SPINEL_PROP_NET__END             = 0x50,
+
+    SPINEL_PROP_THREAD__BEGIN          = 0x50,
+    SPINEL_PROP_THREAD_LEADER_ADDR     = SPINEL_PROP_THREAD__BEGIN + 0, ///< [6]
+    SPINEL_PROP_THREAD_PARENT          = SPINEL_PROP_THREAD__BEGIN + 1, ///< LADDR, SADDR [ES]
+    SPINEL_PROP_THREAD_CHILD_TABLE     = SPINEL_PROP_THREAD__BEGIN + 2, ///< [A(T(ES))]
+    SPINEL_PROP_THREAD_LEADER_RID      = SPINEL_PROP_THREAD__BEGIN + 3, ///< [C]
+    SPINEL_PROP_THREAD_LEADER_WEIGHT   = SPINEL_PROP_THREAD__BEGIN + 4, ///< [6]
+    SPINEL_PROP_THREAD_LOCAL_LEADER_WEIGHT
+                                       = SPINEL_PROP_THREAD__BEGIN + 5, ///< [6]
+    SPINEL_PROP_THREAD_NETWORK_DATA    = SPINEL_PROP_THREAD__BEGIN + 6, ///< [D]
+    SPINEL_PROP_THREAD_NETWORK_DATA_VERSION
+                                       = SPINEL_PROP_THREAD__BEGIN + 7, ///< [S]
+    SPINEL_PROP_THREAD_STABLE_NETWORK_DATA
+                                       = SPINEL_PROP_THREAD__BEGIN + 8, ///< [D]
+    SPINEL_PROP_THREAD_STABLE_NETWORK_DATA_VERSION
+                                       = SPINEL_PROP_THREAD__BEGIN + 9,  ///< [S]
+    SPINEL_PROP_THREAD_ON_MESH_NETS    = SPINEL_PROP_THREAD__BEGIN + 10, ///< array(ipv6prefix,prefixlen,stable,flags) [A(T(6CbC))]
+    SPINEL_PROP_THREAD_LOCAL_ROUTES    = SPINEL_PROP_THREAD__BEGIN + 11, ///< array(ipv6prefix,prefixlen,stable,flags) [A(T(6CbC))]
+    SPINEL_PROP_THREAD_ASSISTING_PORTS = SPINEL_PROP_THREAD__BEGIN + 12, ///< array(portn) [A(S)]
+    SPINEL_PROP_THREAD__END            = 0x60,
+
+    SPINEL_PROP_IPV6__BEGIN          = 0x60,
+    SPINEL_PROP_IPV6_LL_ADDR         = SPINEL_PROP_IPV6__BEGIN + 0, ///< [6]
+    SPINEL_PROP_IPV6_ML_ADDR         = SPINEL_PROP_IPV6__BEGIN + 1, ///< [6C]
+    SPINEL_PROP_IPV6_ML_PREFIX       = SPINEL_PROP_IPV6__BEGIN + 2, ///< [6C]
+    SPINEL_PROP_IPV6_ADDRESS_TABLE   = SPINEL_PROP_IPV6__BEGIN + 3, ///< array(ipv6addr,prefixlen,flags) [A(T(6CL))]
+    SPINEL_PROP_IPV6_ROUTE_TABLE     = SPINEL_PROP_IPV6__BEGIN + 4, ///< array(ipv6prefix,prefixlen,iface,flags) [A(T(6CCL))]
+    SPINEL_PROP_IPV6__END            = 0x70,
+
+    SPINEL_PROP_STREAM__BEGIN       = 0x70,
+    SPINEL_PROP_STREAM_DEBUG        = SPINEL_PROP_STREAM__BEGIN + 0, ///< [U]
+    SPINEL_PROP_STREAM_RAW          = SPINEL_PROP_STREAM__BEGIN + 1, ///< [D]
+    SPINEL_PROP_STREAM_NET          = SPINEL_PROP_STREAM__BEGIN + 2, ///< [D]
+    SPINEL_PROP_STREAM_NET_INSECURE = SPINEL_PROP_STREAM__BEGIN + 3, ///< [D]
+    SPINEL_PROP_STREAM__END         = 0x80,
+
+    SPINEL_PROP_15_4_PIB__BEGIN     = 1024,
+    // For direct access to the 802.15.4 PID.
+    // Individual registers are fetched using
+    // `SPINEL_PROP_15_4_PIB__BEGIN+[PIB_IDENTIFIER]`
+    // Only supported if SPINEL_CAP_15_4_PIB is set.
+    SPINEL_PROP_15_4_PIB__END       = 1280,
+
+    SPINEL_PROP_NEST__BEGIN         = 15296,
+    SPINEL_PROP_NEST__END           = 15360,
+
+    SPINEL_PROP_VENDOR__BEGIN       = 15360,
+    SPINEL_PROP_VENDOR__END         = 16384,
+
+    SPINEL_PROP_EXPERIMENTAL__BEGIN = 2000000,
+    SPINEL_PROP_EXPERIMENTAL__END   = 2097152,
+} spinel_prop_key_t;
+
+// ----------------------------------------------------------------------------
+
+#define SPINEL_HEADER_FLAG               0x80
+
+#define SPINEL_HEADER_TID_SHIFT          0
+#define SPINEL_HEADER_TID_MASK           (15 << SPINEL_HEADER_TID_SHIFT)
+
+#define SPINEL_HEADER_IID_SHIFT          4
+#define SPINEL_HEADER_IID_MASK           (3 << SPINEL_HEADER_IID_SHIFT)
+
+#define SPINEL_HEADER_IID_0              (0 << SPINEL_HEADER_IID_SHIFT)
+#define SPINEL_HEADER_IID_1              (1 << SPINEL_HEADER_IID_SHIFT)
+#define SPINEL_HEADER_IID_2              (2 << SPINEL_HEADER_IID_SHIFT)
+#define SPINEL_HEADER_IID_3              (3 << SPINEL_HEADER_IID_SHIFT)
+
+#define SPINEL_HEADER_GET_IID(x)            (((x) & SPINEL_HEADER_IID_MASK) >> SPINEL_HEADER_IID_SHIFT)
+#define SPINEL_HEADER_GET_TID(x)            (spinel_tid_t)(((x)&SPINEL_HEADER_TID_MASK)>>SPINEL_HEADER_TID_SHIFT)
+
+#define SPINEL_GET_NEXT_TID(x)      (spinel_tid_t)((x)>=0xF?1:(x)+1)
+
+#define SPINEL_BEACON_THREAD_FLAG_VERSION_SHIFT 4
+#define SPINEL_BEACON_THREAD_FLAG_VERSION_MASK  (0xf << SPINEL_BEACON_THREAD_FLAG_VERSION_SHIFT)
+#define SPINEL_BEACON_THREAD_FLAG_JOINABLE      (1 << 0)
+#define SPINEL_BEACON_THREAD_FLAG_NATIVE        (1 << 3)
+
+// ----------------------------------------------------------------------------
+
+enum
+{
+    SPINEL_DATATYPE_NULL_C        = 0,
+    SPINEL_DATATYPE_VOID_C        = '.',
+    SPINEL_DATATYPE_BOOL_C        = 'b',
+    SPINEL_DATATYPE_UINT8_C       = 'C',
+    SPINEL_DATATYPE_INT8_C        = 'c',
+    SPINEL_DATATYPE_UINT16_C      = 'S',
+    SPINEL_DATATYPE_INT16_C       = 's',
+    SPINEL_DATATYPE_UINT32_C      = 'L',
+    SPINEL_DATATYPE_INT32_C       = 'l',
+    SPINEL_DATATYPE_UINT_PACKED_C = 'i',
+    SPINEL_DATATYPE_IPv6ADDR_C    = '6',
+    SPINEL_DATATYPE_EUI64_C       = 'E',
+    SPINEL_DATATYPE_EUI48_C       = 'e',
+    SPINEL_DATATYPE_DATA_C        = 'D',
+    SPINEL_DATATYPE_UTF8_C        = 'U', //!< Zero-Terminated UTF8-Encoded String
+    SPINEL_DATATYPE_STRUCT_C      = 'T',
+    SPINEL_DATATYPE_ARRAY_C       = 'A',
+};
+
+typedef char spinel_datatype_t;
+
+#define SPINEL_DATATYPE_NULL_S        ""
+#define SPINEL_DATATYPE_VOID_S        "."
+#define SPINEL_DATATYPE_BOOL_S        "b"
+#define SPINEL_DATATYPE_UINT8_S       "C"
+#define SPINEL_DATATYPE_INT8_S        "c"
+#define SPINEL_DATATYPE_UINT16_S      "S"
+#define SPINEL_DATATYPE_INT16_S       "s"
+#define SPINEL_DATATYPE_UINT32_S      "L"
+#define SPINEL_DATATYPE_INT32_S       "l"
+#define SPINEL_DATATYPE_UINT_PACKED_S "i"
+#define SPINEL_DATATYPE_IPv6ADDR_S    "6"
+#define SPINEL_DATATYPE_EUI64_S       "E"
+#define SPINEL_DATATYPE_EUI48_S       "e"
+#define SPINEL_DATATYPE_DATA_S        "D"
+#define SPINEL_DATATYPE_UTF8_S        "U" //!< Zero-Terminated UTF8-Encoded String
+#define SPINEL_DATATYPE_STRUCT_S      "T"
+#define SPINEL_DATATYPE_ARRAY_S       "A"
+
+SPINEL_API_EXTERN spinel_ssize_t spinel_datatype_pack(uint8_t *data_out, spinel_size_t data_len,
+                                                      const char *pack_format, ...);
+SPINEL_API_EXTERN spinel_ssize_t spinel_datatype_vpack(uint8_t *data_out, spinel_size_t data_len,
+                                                       const char *pack_format, va_list args);
+SPINEL_API_EXTERN spinel_ssize_t spinel_datatype_unpack(const uint8_t *data_in, spinel_size_t data_len,
+                                                        const char *pack_format, ...);
+SPINEL_API_EXTERN spinel_ssize_t spinel_datatype_vunpack(const uint8_t *data_in, spinel_size_t data_len,
+                                                         const char *pack_format, va_list args);
+
+SPINEL_API_EXTERN spinel_ssize_t spinel_packed_uint_decode(const uint8_t *bytes, spinel_size_t len,
+                                                           unsigned int *value);
+SPINEL_API_EXTERN spinel_ssize_t spinel_packed_uint_encode(uint8_t *bytes, spinel_size_t len, unsigned int value);
+SPINEL_API_EXTERN spinel_ssize_t spinel_packed_uint_size(unsigned int value);
+
+SPINEL_API_EXTERN const char *spinel_next_packed_datatype(const char *pack_format);
+
+// ----------------------------------------------------------------------------
+
+SPINEL_API_EXTERN const char *spinel_prop_key_to_cstr(spinel_prop_key_t prop_key);
+
+// ----------------------------------------------------------------------------
+
+__END_DECLS
+
+#endif /* defined(SPINEL_HEADER_INCLUDED) */
diff --git a/third_party/pt/LICENSE b/third_party/pt/LICENSE
new file mode 100644
index 0000000..4b9d77f
--- /dev/null
+++ b/third_party/pt/LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2004-2005, Swedish Institute of Computer Science.
+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 Institute 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 INSTITUTE 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 INSTITUTE 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.
+
+Author: Adam Dunkels <adam@sics.se>
diff --git a/third_party/pt/Makefile b/third_party/pt/Makefile
new file mode 100644
index 0000000..ff573ef
--- /dev/null
+++ b/third_party/pt/Makefile
@@ -0,0 +1,9 @@
+CFLAGS=-O -Wuninitialized -Werror
+
+all: example-codelock example-buffer example-small
+
+example-codelock: example-codelock.c pt.h lc.h
+
+example-buffer: example-buffer.c pt.h lc.h
+
+example-small: example-small.c pt.h lc.h
diff --git a/third_party/pt/README b/third_party/pt/README
new file mode 100644
index 0000000..54d8627
--- /dev/null
+++ b/third_party/pt/README
@@ -0,0 +1,51 @@
+Protothreads are extremely lightweight stackless threads designed for
+severely memory constrained systems such as small embedded systems or
+sensor network nodes. Protothreads can be used with or without an
+underlying operating system.
+
+Protothreads provides a blocking context on top of an event-driven
+system, without the overhead of per-thread stacks. The purpose of
+protothreads is to implement sequential flow of control without
+complex state machines or full multi-threading.
+
+Main features:
+
+    * No machine specific code - the protothreads library is pure C
+    * Does not use error-prone functions such as longjmp()
+    * Very small RAM overhead - only two bytes per protothread
+    * Can be used with or without an OS
+    * Provides blocking wait without full multi-threading or
+      stack-switching
+    * Freely available under a BSD-like open source license
+
+Example applications:
+
+    * Memory constrained systems
+    * Event-driven protocol stacks
+    * Small embedded systems
+    * Sensor network nodes
+
+The protothreads library is released under an open source BSD-style
+license that allows for both non-commercial and commercial usage. The
+only requirement is that credit is given.
+
+The protothreads library was written by Adam Dunkels <adam@sics.se>
+with support from Oliver Schmidt <ol.sc@web.de>.
+
+More information and new versions can be found at the protothreads
+homepage:
+		     http://www.sics.se/~adam/pt/
+
+Documentation can be found in the doc/ subdirectory.
+
+Two example programs illustrating the use of protothreads can be found
+in this directory:
+
+   example-small.c         A small example showing how to use protothreads
+   example-buffer.c        The bounded buffer problem with protothreads
+   example-codelock.c	   A code lock with simulated key input
+
+To compile the examples, simply run "make".
+
+
+Adam Dunkels, 3 June 2006
diff --git a/third_party/pt/README-VISUAL-C++.txt b/third_party/pt/README-VISUAL-C++.txt
new file mode 100644
index 0000000..3b09a69
--- /dev/null
+++ b/third_party/pt/README-VISUAL-C++.txt
@@ -0,0 +1,5 @@
+Protothreads can in some cases fail to compile under Visual C++
+version 6.0 due to a bug in the compiler. See the following page for a
+solution to the problem:
+
+http://support.microsoft.com/default.aspx?scid=kb;en-us;199057
diff --git a/third_party/pt/README.google b/third_party/pt/README.google
new file mode 100644
index 0000000..79082a5
--- /dev/null
+++ b/third_party/pt/README.google
@@ -0,0 +1,17 @@
+URL: http://dunkels.com/adam/download/pt-1.4.tar.gz
+Version: 1.4
+License: BSD-3
+License File: LICENSE
+
+Description:
+Protothreads are extremely lightweight stackless threads designed for severely
+memory constrained systems, such as small embedded systems or wireless sensor
+network nodes. Protothreads provide linear code execution for event-driven
+systems implemented in C. Protothreads can be used with or without an underlying
+operating system to provide blocking event-handlers. Protothreads provide
+sequential flow of control without complex state machines or full
+multi-threading.
+
+Local Modifications:
+LICENSE file has been created for compliance purposes. Not included in original
+distribution. Removed html and pt-refman.pdf from documentation folder.
diff --git a/third_party/pt/doc/Doxyfile b/third_party/pt/doc/Doxyfile
new file mode 100644
index 0000000..27b4b75
--- /dev/null
+++ b/third_party/pt/doc/Doxyfile
@@ -0,0 +1,229 @@
+# Doxyfile 1.4.6
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME           = "The Protothreads Library 1.4"
+PROJECT_NUMBER         =
+OUTPUT_DIRECTORY       = .
+CREATE_SUBDIRS         = NO
+OUTPUT_LANGUAGE        = English
+USE_WINDOWS_ENCODING   = NO
+BRIEF_MEMBER_DESC      = YES
+REPEAT_BRIEF           = YES
+ABBREVIATE_BRIEF       =
+ALWAYS_DETAILED_SEC    = NO
+INLINE_INHERITED_MEMB  = NO
+FULL_PATH_NAMES        = YES
+STRIP_FROM_PATH        = ../
+STRIP_FROM_INC_PATH    =
+SHORT_NAMES            = YES
+JAVADOC_AUTOBRIEF      = YES
+MULTILINE_CPP_IS_BRIEF = NO
+DETAILS_AT_TOP         = YES
+INHERIT_DOCS           = YES
+SEPARATE_MEMBER_PAGES  = NO
+TAB_SIZE               = 8
+ALIASES                =
+OPTIMIZE_OUTPUT_FOR_C  = YES
+OPTIMIZE_OUTPUT_JAVA   = NO
+BUILTIN_STL_SUPPORT    = NO
+DISTRIBUTE_GROUP_DOC   = NO
+SUBGROUPING            = YES
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL            = NO
+EXTRACT_PRIVATE        = NO
+EXTRACT_STATIC         = NO
+EXTRACT_LOCAL_CLASSES  = NO
+EXTRACT_LOCAL_METHODS  = NO
+HIDE_UNDOC_MEMBERS     = YES
+HIDE_UNDOC_CLASSES     = YES
+HIDE_FRIEND_COMPOUNDS  = NO
+HIDE_IN_BODY_DOCS      = NO
+INTERNAL_DOCS          = NO
+CASE_SENSE_NAMES       = YES
+HIDE_SCOPE_NAMES       = NO
+SHOW_INCLUDE_FILES     = YES
+INLINE_INFO            = YES
+SORT_MEMBER_DOCS       = YES
+SORT_BRIEF_DOCS        = NO
+SORT_BY_SCOPE_NAME     = NO
+GENERATE_TODOLIST      = YES
+GENERATE_TESTLIST      = YES
+GENERATE_BUGLIST       = NO
+GENERATE_DEPRECATEDLIST= NO
+ENABLED_SECTIONS       =
+MAX_INITIALIZER_LINES  = 30
+SHOW_USED_FILES        = NO
+SHOW_DIRECTORIES       = NO
+FILE_VERSION_FILTER    =
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET                  = NO
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = YES
+WARN_IF_DOC_ERROR      = YES
+WARN_NO_PARAMDOC       = NO
+WARN_FORMAT            = "$file:$line: $text"
+WARN_LOGFILE           =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT                  = pt-mainpage.txt \
+                         pt-doc.txt \
+                         ../pt.h \
+                         ../pt-sem.h \
+                         ../lc.h \
+                         ../lc-switch.h \
+                         ../lc-addrlabels.h
+FILE_PATTERNS          =
+RECURSIVE              = NO
+EXCLUDE                =
+EXCLUDE_SYMLINKS       = NO
+EXCLUDE_PATTERNS       =
+EXAMPLE_PATH           = ..
+EXAMPLE_PATTERNS       =
+EXAMPLE_RECURSIVE      = NO
+IMAGE_PATH             =
+INPUT_FILTER           =
+FILTER_PATTERNS        =
+FILTER_SOURCE_FILES    = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER         = YES
+INLINE_SOURCES         = YES
+STRIP_CODE_COMMENTS    = NO
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION    = YES
+USE_HTAGS              = NO
+VERBATIM_HEADERS       = YES
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX     = NO
+COLS_IN_ALPHA_INDEX    = 5
+IGNORE_PREFIX          =
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML          = YES
+HTML_OUTPUT            = html
+HTML_FILE_EXTENSION    = .html
+HTML_HEADER            =
+HTML_FOOTER            =
+HTML_STYLESHEET        =
+HTML_ALIGN_MEMBERS     = YES
+GENERATE_HTMLHELP      = YES
+CHM_FILE               =
+HHC_LOCATION           =
+GENERATE_CHI           = NO
+BINARY_TOC             = NO
+TOC_EXPAND             = NO
+DISABLE_INDEX          = NO
+ENUM_VALUES_PER_LINE   = 4
+GENERATE_TREEVIEW      = YES
+TREEVIEW_WIDTH         = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX         = YES
+LATEX_OUTPUT           = latex
+LATEX_CMD_NAME         = latex
+MAKEINDEX_CMD_NAME     = makeindex
+COMPACT_LATEX          = YES
+PAPER_TYPE             = a4
+EXTRA_PACKAGES         =
+LATEX_HEADER           = header.tex
+PDF_HYPERLINKS         = YES
+USE_PDFLATEX           = YES
+LATEX_BATCHMODE        = NO
+LATEX_HIDE_INDICES     = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF           = NO
+RTF_OUTPUT             = rtf
+COMPACT_RTF            = NO
+RTF_HYPERLINKS         = NO
+RTF_STYLESHEET_FILE    =
+RTF_EXTENSIONS_FILE    =
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN           = NO
+MAN_OUTPUT             = man
+MAN_EXTENSION          = .3
+MAN_LINKS              = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML           = NO
+XML_OUTPUT             = xml
+XML_SCHEMA             =
+XML_DTD                =
+XML_PROGRAMLISTING     = YES
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF   = NO
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD       = NO
+PERLMOD_LATEX          = NO
+PERLMOD_PRETTY         = YES
+PERLMOD_MAKEVAR_PREFIX =
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING   = YES
+MACRO_EXPANSION        = NO
+EXPAND_ONLY_PREDEF     = NO
+SEARCH_INCLUDES        = YES
+INCLUDE_PATH           =
+INCLUDE_FILE_PATTERNS  =
+PREDEFINED             = DOXYGEN
+EXPAND_AS_DEFINED      =
+SKIP_FUNCTION_MACROS   = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+TAGFILES               =
+GENERATE_TAGFILE       =
+ALLEXTERNALS           = NO
+EXTERNAL_GROUPS        = YES
+PERL_PATH              = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS         = NO
+HIDE_UNDOC_RELATIONS   = NO
+HAVE_DOT               = NO
+CLASS_GRAPH            = NO
+COLLABORATION_GRAPH    = YES
+GROUP_GRAPHS           = YES
+UML_LOOK               = NO
+TEMPLATE_RELATIONS     = NO
+INCLUDE_GRAPH          = NO
+INCLUDED_BY_GRAPH      = YES
+CALL_GRAPH             = YES
+GRAPHICAL_HIERARCHY    = YES
+DIRECTORY_GRAPH        = YES
+DOT_IMAGE_FORMAT       = png
+DOT_PATH               =
+DOTFILE_DIRS           =
+MAX_DOT_GRAPH_WIDTH    = 1024
+MAX_DOT_GRAPH_HEIGHT   = 1024
+MAX_DOT_GRAPH_DEPTH    = 0
+DOT_TRANSPARENT        = NO
+DOT_MULTI_TARGETS      = NO
+GENERATE_LEGEND        = YES
+DOT_CLEANUP            = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE           = NO
diff --git a/third_party/pt/doc/Makefile b/third_party/pt/doc/Makefile
new file mode 100644
index 0000000..39328b4
--- /dev/null
+++ b/third_party/pt/doc/Makefile
@@ -0,0 +1,7 @@
+dox:
+	doxygen Doxyfile
+
+
+pdf:	dox
+	(cd latex; $(MAKE) refman.pdf)
+	mv latex/refman.pdf pt-refman.pdf
diff --git a/third_party/pt/doc/header.tex b/third_party/pt/doc/header.tex
new file mode 100644
index 0000000..5899653
--- /dev/null
+++ b/third_party/pt/doc/header.tex
@@ -0,0 +1,52 @@
+\documentclass[a4paper]{article}
+\usepackage{a4wide}
+\usepackage{makeidx}
+\usepackage{fancyhdr}
+\usepackage{graphicx}
+\usepackage{multicol}
+\usepackage{float}
+\usepackage{textcomp}
+\usepackage{alltt}
+\usepackage{times}
+\usepackage{epsfig}
+\ifx\pdfoutput\undefined
+\usepackage[ps2pdf,
+            pagebackref=true,
+            colorlinks=true,
+            linkcolor=blue
+           ]{hyperref}
+\usepackage{pspicture}
+\else
+\usepackage[pdftex,
+            pagebackref=true,
+            colorlinks=true,
+            linkcolor=blue
+           ]{hyperref}
+\fi
+\usepackage{doxygen}
+\makeindex
+\setcounter{tocdepth}{1}
+\renewcommand{\footrulewidth}{0.4pt}
+\begin{document}
+\begin{titlepage}
+\vspace*{5cm}
+\begin{center}
+{\Huge Protothreads}\\
+\vspace*{1cm}
+{\LARGE The Protothreads Library 1.3 Reference Manual}\\
+\vspace*{3cm}
+{\Large June 2006}\\
+\vspace*{2cm}
+\includegraphics[width=6cm]{../sicslogo.pdf}\\
+\vspace*{1cm}
+{\Large Adam Dunkels}\\
+{\Large \texttt{adam@sics.se}}\\
+\vspace*{1cm}
+{\LARGE Swedish Institute of Computer Science}\\
+\vspace*{0.5cm}
+
+\end{center}
+\end{titlepage}
+\pagenumbering{roman}
+\tableofcontents
+\pagenumbering{arabic}
diff --git a/third_party/pt/doc/pt-doc.txt b/third_party/pt/doc/pt-doc.txt
new file mode 100644
index 0000000..b0885aa
--- /dev/null
+++ b/third_party/pt/doc/pt-doc.txt
@@ -0,0 +1,58 @@
+/**
+\defgroup pt Protothreads
+@{
+Protothreads are implemented in a single header file, pt.h, which
+includes the local continuations header file, lc.h. This file in turn
+includes the actual implementation of local continuations, which
+typically also is contained in a single header file.
+
+*/
+
+/** @} */
+
+/**
+\defgroup examples Examples
+@{
+
+\section example-small A small example
+
+This first example shows a very simple program: two protothreads
+waiting for each other to toggle two flags. The code illustrates how
+to write protothreads code, how to initialize protothreads, and how to
+schedule them.
+
+\include example-small.c
+
+
+\section example-code-lock A code-lock
+This example shows how to implement a simple code lock - the kind of
+device that is placed next to doors and that you have to push a four
+digit number into in order to unlock the door.
+
+The code lock waits for key presses from a numeric keyboard and if the
+correct code is entered, the lock is unlocked. There is a maximum time
+of one second between each key press, and after the correct code has
+been entered, no more keys must be pressed for 0.5 seconds before the
+lock is opened.
+
+\include example-codelock.c
+
+\section example-buffer The bounded buffer with protothread semaphores
+
+The following example shows how to implement the bounded buffer
+problem using the protothreads semaphore library. The example uses
+three protothreads: one producer() protothread that produces items,
+one consumer() protothread that consumes items, and one
+driver_thread() that schedules the producer and consumer protothreads.
+
+Note that there is no need for a mutex to guard the add_to_buffer()
+and get_from_buffer() functions because of the implicit locking
+semantics of protothreads - a protothread will never be preempted and
+will never block except in an explicit PT_WAIT statement.
+
+\include example-buffer.c
+
+*/
+
+
+/** @} */
diff --git a/third_party/pt/doc/pt-mainpage.txt b/third_party/pt/doc/pt-mainpage.txt
new file mode 100644
index 0000000..269570b
--- /dev/null
+++ b/third_party/pt/doc/pt-mainpage.txt
@@ -0,0 +1,156 @@
+/**
+
+\mainpage The Protothreads Library
+
+\author Adam Dunkels <adam@sics.se>
+
+Protothreads are a type of lightweight stackless threads designed for
+severly memory constrained systems such as deeply embedded systems or
+sensor network nodes. Protothreads provides linear code execution for
+event-driven systems implemented in C. Protothreads can be used with
+or without an RTOS.
+
+Protothreads are a extremely lightweight, stackless type of threads
+that provides a blocking context on top of an event-driven system,
+without the overhead of per-thread stacks. The purpose of protothreads
+is to implement sequential flow of control without complex state
+machines or full multi-threading. Protothreads provides conditional
+blocking inside C functions.
+
+Main features:
+
+    - No machine specific code - the protothreads library is pure C
+
+    - Does not use error-prone functions such as longjmp()
+
+    - Very small RAM overhead - only two bytes per protothread
+
+    - Can be used with or without an OS
+
+    - Provides blocking wait without full multi-threading or
+      stack-switching
+
+Examples applications:
+
+    - Memory constrained systems
+
+    - Event-driven protocol stacks
+
+    - Deeply embedded systems
+
+    - Sensor network nodes
+
+
+\sa \ref examples "Example programs"
+\sa \ref pt "Protothreads API documentation"
+
+The protothreads library is released under a BSD-style license that
+allows for both non-commercial and commercial usage. The only
+requirement is that credit is given.
+
+More information and new version of the code can be found at the
+Protothreads homepage:
+
+		     http://www.sics.se/~adam/pt/
+
+\section authors Authors
+
+The protothreads library was written by Adam Dunkels <adam@sics.se>
+with support from Oliver Schmidt <ol.sc@web.de>.
+
+\section using Using protothreads
+
+Using protothreads in a project is easy: simply copy the files pt.h,
+lc.h and lc-switch.h into the include files directory of the project,
+and \#include "pt.h" in all files that should use protothreads.
+
+\section pt-desc Protothreads
+
+Protothreads are a extremely lightweight, stackless threads that
+provides a blocking context on top of an event-driven system, without
+the overhead of per-thread stacks. The purpose of protothreads is to
+implement sequential flow of control without using complex state
+machines or full multi-threading. Protothreads provides conditional
+blocking inside a C function.
+
+In memory constrained systems, such as deeply embedded systems,
+traditional multi-threading may have a too large memory overhead. In
+traditional multi-threading, each thread requires its own stack, that
+typically is over-provisioned. The stacks may use large parts of the
+available memory.
+
+The main advantage of protothreads over ordinary threads is that
+protothreads are very lightweight: a protothread does not require its
+own stack. Rather, all protothreads run on the same stack and context
+switching is done by stack rewinding. This is advantageous in memory
+constrained systems, where a stack for a thread might use a large part
+of the available memory. A protothread only requires only two bytes of
+memory per protothread. Moreover, protothreads are implemented in pure
+C and do not require any machine-specific assembler code.
+
+A protothread runs within a single C function and cannot span over
+other functions. A protothread may call normal C functions, but cannot
+block inside a called function. Blocking inside nested function calls
+is instead made by spawning a separate protothread for each
+potentially blocking function. The advantage of this approach is that
+blocking is explicit: the programmer knows exactly which functions
+that block that which functions the never blocks.
+
+Protothreads are similar to asymmetric co-routines. The main
+difference is that co-routines uses a separate stack for each
+co-routine, whereas protothreads are stackless. The most similar
+mechanism to protothreads are Python generators. These are also
+stackless constructs, but have a different purpose. Protothreads
+provides blocking contexts inside a C function, whereas Python
+generators provide multiple exit points from a generator function.
+
+\section pt-autovars Local variables
+
+\note
+Because protothreads do not save the stack context across a blocking
+call, local variables are not preserved when the protothread
+blocks. This means that local variables should be used with utmost
+care - if in doubt, do not use local variables inside a protothread!
+
+\section pt-scheduling Scheduling
+
+A protothread is driven by repeated calls to the function in which the
+protothread is running. Each time the function is called, the
+protothread will run until it blocks or exits. Thus the scheduling of
+protothreads is done by the application that uses protothreads.
+
+\section pt-impl Implementation
+
+Protothreads are implemented using local continuations. A local
+continuation represents the current state of execution at a particular
+place in the program, but does not provide any call history or local
+variables. A local continuation can be set in a specific function to
+capture the state of the function. After a local continuation has been
+set can be resumed in order to restore the state of the function at
+the point where the local continuation was set.
+
+
+Local continuations can be implemented in a variety of ways:
+
+   -# by using machine specific assembler code,
+   -# by using standard C constructs, or
+   -# by using compiler extensions.
+
+The first way works by saving and restoring the processor state,
+except for stack pointers, and requires between 16 and 32 bytes of
+memory per protothread. The exact amount of memory required depends on
+the architecture.
+
+The standard C implementation requires only two bytes of state per
+protothread and utilizes the C switch() statement in a non-obvious way
+that is similar to Duff's device. This implementation does, however,
+impose a slight restriction to the code that uses protothreads: a
+protothread cannot perform a blocking wait (PT_WAIT_UNTIL() or
+PT_YIELD()) inside a switch() statement.
+
+Certain compilers has C extensions that can be used to implement
+protothreads. GCC supports label pointers that can be used for this
+purpose. With this implementation, protothreads require 4 bytes of RAM
+per protothread.
+
+*/
diff --git a/third_party/pt/doc/sicslogo.pdf b/third_party/pt/doc/sicslogo.pdf
new file mode 100644
index 0000000..239a1bf
--- /dev/null
+++ b/third_party/pt/doc/sicslogo.pdf
Binary files differ
diff --git a/third_party/pt/example-buffer.c b/third_party/pt/example-buffer.c
new file mode 100644
index 0000000..3845824
--- /dev/null
+++ b/third_party/pt/example-buffer.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2004-2005, Swedish Institute of Computer Science.
+ * 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
+ *
+ * This file is part of the protothreads library.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ * $Id: example-buffer.c,v 1.5 2005/10/07 05:21:33 adam Exp $
+ */
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+#include <stdio.h>
+
+#include "pt-sem.h"
+
+#define NUM_ITEMS 32
+#define BUFSIZE 8
+
+static int buffer[BUFSIZE];
+static int bufptr;
+
+static void
+add_to_buffer(int item)
+{
+  printf("Item %d added to buffer at place %d\n", item, bufptr);
+  buffer[bufptr] = item;
+  bufptr = (bufptr + 1) % BUFSIZE;
+}
+static int
+get_from_buffer(void)
+{
+  int item;
+  item = buffer[bufptr];
+  printf("Item %d retrieved from buffer at place %d\n",
+	 item, bufptr);
+  bufptr = (bufptr + 1) % BUFSIZE;
+  return item;
+}
+
+static int
+produce_item(void)
+{
+  static int item = 0;
+  printf("Item %d produced\n", item);
+  return item++;
+}
+
+static void
+consume_item(int item)
+{
+  printf("Item %d consumed\n", item);
+}
+
+static struct pt_sem full, empty;
+
+static
+PT_THREAD(producer(struct pt *pt))
+{
+  static int produced;
+
+  PT_BEGIN(pt);
+
+  for(produced = 0; produced < NUM_ITEMS; ++produced) {
+
+    PT_SEM_WAIT(pt, &full);
+
+    add_to_buffer(produce_item());
+
+    PT_SEM_SIGNAL(pt, &empty);
+  }
+
+  PT_END(pt);
+}
+
+static
+PT_THREAD(consumer(struct pt *pt))
+{
+  static int consumed;
+
+  PT_BEGIN(pt);
+
+  for(consumed = 0; consumed < NUM_ITEMS; ++consumed) {
+
+    PT_SEM_WAIT(pt, &empty);
+
+    consume_item(get_from_buffer());
+
+    PT_SEM_SIGNAL(pt, &full);
+  }
+
+  PT_END(pt);
+}
+
+static
+PT_THREAD(driver_thread(struct pt *pt))
+{
+  static struct pt pt_producer, pt_consumer;
+
+  PT_BEGIN(pt);
+
+  PT_SEM_INIT(&empty, 0);
+  PT_SEM_INIT(&full, BUFSIZE);
+
+  PT_INIT(&pt_producer);
+  PT_INIT(&pt_consumer);
+
+  PT_WAIT_THREAD(pt, producer(&pt_producer) &
+		     consumer(&pt_consumer));
+
+  PT_END(pt);
+}
+
+
+int
+main(void)
+{
+  struct pt driver_pt;
+
+  PT_INIT(&driver_pt);
+
+  while(PT_SCHEDULE(driver_thread(&driver_pt))) {
+
+    /*
+     * When running this example on a multitasking system, we must
+     * give other processes a chance to run too and therefore we call
+     * usleep() resp. Sleep() here. On a dedicated embedded system,
+     * we usually do not need to do this.
+     */
+#ifdef _WIN32
+    Sleep(0);
+#else
+    usleep(10);
+#endif
+  }
+  return 0;
+}
diff --git a/third_party/pt/example-codelock.c b/third_party/pt/example-codelock.c
new file mode 100644
index 0000000..6df6fb0
--- /dev/null
+++ b/third_party/pt/example-codelock.c
@@ -0,0 +1,414 @@
+/*
+ * Copyright (c) 2004-2005, Swedish Institute of Computer Science.
+ * 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
+ *
+ * This file is part of the protothreads library.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ * $Id: example-codelock.c,v 1.5 2005/10/06 07:57:08 adam Exp $
+ */
+
+/*
+ *
+ * This example shows how to implement a simple code lock. The code
+ * lock waits for key presses from a numeric keyboard and if the
+ * correct code is entered, the lock is unlocked. There is a maximum
+ * time of one second between each key press, and after the correct
+ * code has been entered, no more keys must be pressed for 0.5 seconds
+ * before the lock is opened.
+ *
+ * This is an example that shows two things:
+ * - how to implement a code lock key input mechanism, and
+ * - how to implement a sequential timed routine.
+ *
+ * The program consists of two protothreads, one that implements the
+ * code lock reader and one that implements simulated keyboard input.
+ *
+ *
+ */
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <sys/time.h>
+#endif
+#include <stdio.h>
+
+#include "pt.h"
+
+/*---------------------------------------------------------------------------*/
+/*
+ * The following definitions are just for the simple timer library
+ * used in this example. The actual implementation of the functions
+ * can be found at the end of this file.
+ */
+struct timer { int start, interval; };
+static int  timer_expired(struct timer *t);
+static void timer_set(struct timer *t, int usecs);
+/*---------------------------------------------------------------------------*/
+/*
+ * This example uses two timers: one for the code lock protothread and
+ * one for the simulated key input protothread.
+ */
+static struct timer codelock_timer, input_timer;
+/*---------------------------------------------------------------------------*/
+/*
+ * This is the code that has to be entered.
+ */
+static const char code[4] = {'1', '4', '2', '3'};
+/*---------------------------------------------------------------------------*/
+/*
+ * This example has two protothread and therefor has two protothread
+ * control structures of type struct pt. These are initialized with
+ * PT_INIT() in the main() function below.
+ */
+static struct pt codelock_pt, input_pt;
+/*---------------------------------------------------------------------------*/
+/*
+ * The following code implements a simple key input. Input is made
+ * with the press_key() function, and the function key_pressed()
+ * checks if a key has been pressed. The variable "key" holds the
+ * latest key that was pressed. The variable "key_pressed_flag" is set
+ * when a key is pressed and cleared when a key press is checked.
+ */
+static char key, key_pressed_flag;
+
+static void
+press_key(char k)
+{
+  printf("--- Key '%c' pressed\n", k);
+  key = k;
+  key_pressed_flag = 1;
+}
+
+static int
+key_pressed(void)
+{
+  if(key_pressed_flag != 0) {
+    key_pressed_flag = 0;
+    return 1;
+  }
+  return 0;
+}
+/*---------------------------------------------------------------------------*/
+/*
+ * Declaration of the protothread function implementing the code lock
+ * logic. The protothread function is declared using the PT_THREAD()
+ * macro. The function is declared with the "static" keyword since it
+ * is local to this file. The name of the function is codelock_thread
+ * and it takes one argument, pt, of the type struct pt.
+ *
+ */
+static
+PT_THREAD(codelock_thread(struct pt *pt))
+{
+  /* This is a local variable that holds the number of keys that have
+   * been pressed. Note that it is declared with the "static" keyword
+   * to make sure that the variable is *not* allocated on the stack.
+   */
+  static int keys;
+
+  /*
+   * Declare the beginning of the protothread.
+   */
+  PT_BEGIN(pt);
+
+  /*
+   * We'll let the protothread loop until the protothread is
+   * expliticly exited with PT_EXIT().
+   */
+  while(1) {
+
+    /*
+     * We'll be reading key presses until we get the right amount of
+     * correct keys.
+     */
+    for(keys = 0; keys < sizeof(code); ++keys) {
+
+      /*
+       * If we haven't gotten any keypresses, we'll simply wait for one.
+       */
+      if(keys == 0) {
+
+	/*
+	 * The PT_WAIT_UNTIL() function will block until the condition
+	 * key_pressed() is true.
+	 */
+	PT_WAIT_UNTIL(pt, key_pressed());
+      } else {
+
+	/*
+	 * If the "key" variable was larger than zero, we have already
+	 * gotten at least one correct key press. If so, we'll not
+	 * only wait for the next key, but we'll also set a timer that
+	 * expires in one second. This gives the person pressing the
+	 * keys one second to press the next key in the code.
+	 */
+	timer_set(&codelock_timer, 1000);
+
+	/*
+	 * The following statement shows how complex blocking
+	 * conditions can be easily expressed with protothreads and
+	 * the PT_WAIT_UNTIL() function.
+	 */
+	PT_WAIT_UNTIL(pt, key_pressed() || timer_expired(&codelock_timer));
+
+	/*
+	 * If the timer expired, we should break out of the for() loop
+	 * and start reading keys from the beginning of the while(1)
+	 * loop instead.
+	 */
+	if(timer_expired(&codelock_timer)) {
+	  printf("Code lock timer expired.\n");
+
+	  /*
+	   * Break out from the for() loop and start from the
+	   * beginning of the while(1) loop.
+	   */
+	  break;
+	}
+      }
+
+      /*
+       * Check if the pressed key was correct.
+       */
+      if(key != code[keys]) {
+	printf("Incorrect key '%c' found\n", key);
+	/*
+	 * Break out of the for() loop since the key was incorrect.
+	 */
+	break;
+      } else {
+	printf("Correct key '%c' found\n", key);
+      }
+    }
+
+    /*
+     * Check if we have gotten all keys.
+     */
+    if(keys == sizeof(code)) {
+      printf("Correct code entered, waiting for 500 ms before unlocking.\n");
+
+      /*
+       * Ok, we got the correct code. But to make sure that the code
+       * was not just a fluke of luck by an intruder, but the correct
+       * code entered by a person that knows the correct code, we'll
+       * wait for half a second before opening the lock. If another
+       * key is pressed during this time, we'll assume that it was a
+       * fluke of luck that the correct code was entered the first
+       * time.
+       */
+      timer_set(&codelock_timer, 500);
+      PT_WAIT_UNTIL(pt, key_pressed() || timer_expired(&codelock_timer));
+
+      /*
+       * If we continued from the PT_WAIT_UNTIL() statement without
+       * the timer expired, we don't open the lock.
+       */
+      if(!timer_expired(&codelock_timer)) {
+	printf("Key pressed during final wait, code lock locked again.\n");
+      } else {
+
+	/*
+	 * If the timer expired, we'll open the lock and exit from the
+	 * protothread.
+	 */
+	printf("Code lock unlocked.\n");
+	PT_EXIT(pt);
+      }
+    }
+  }
+
+  /*
+   * Finally, we'll mark the end of the protothread.
+   */
+  PT_END(pt);
+}
+/*---------------------------------------------------------------------------*/
+/*
+ * This is the second protothread in this example. It implements a
+ * simulated user pressing the keys. This illustrates how a linear
+ * sequence of timed instructions can be implemented with
+ * protothreads.
+ */
+static
+PT_THREAD(input_thread(struct pt *pt))
+{
+  PT_BEGIN(pt);
+
+  printf("Waiting 1 second before entering first key.\n");
+
+  timer_set(&input_timer, 1000);
+  PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
+
+  press_key('1');
+
+  timer_set(&input_timer, 100);
+  PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
+
+  press_key('2');
+
+  timer_set(&input_timer, 100);
+  PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
+
+  press_key('3');
+
+  timer_set(&input_timer, 2000);
+  PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
+
+  press_key('1');
+
+  timer_set(&input_timer, 200);
+  PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
+
+  press_key('4');
+
+  timer_set(&input_timer, 200);
+  PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
+
+  press_key('2');
+
+  timer_set(&input_timer, 2000);
+  PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
+
+  press_key('3');
+
+  timer_set(&input_timer, 200);
+  PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
+
+  press_key('1');
+
+  timer_set(&input_timer, 200);
+  PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
+
+  press_key('4');
+
+  timer_set(&input_timer, 200);
+  PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
+
+  press_key('2');
+
+  timer_set(&input_timer, 100);
+  PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
+
+  press_key('3');
+
+  timer_set(&input_timer, 100);
+  PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
+
+  press_key('4');
+
+  timer_set(&input_timer, 1500);
+  PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
+
+  press_key('1');
+
+  timer_set(&input_timer, 300);
+  PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
+
+  press_key('4');
+
+  timer_set(&input_timer, 400);
+  PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
+
+  press_key('2');
+
+  timer_set(&input_timer, 500);
+  PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
+
+  press_key('3');
+
+  timer_set(&input_timer, 2000);
+  PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
+
+  PT_END(pt);
+}
+/*---------------------------------------------------------------------------*/
+/*
+ * This is the main function. It initializes the two protothread
+ * control structures and schedules the two protothreads. The main
+ * function returns when the protothread the runs the code lock exits.
+ */
+int
+main(void)
+{
+  /*
+   * Initialize the two protothread control structures.
+   */
+  PT_INIT(&input_pt);
+  PT_INIT(&codelock_pt);
+
+  /*
+   * Schedule the two protothreads until the codelock_thread() exits.
+   */
+  while(PT_SCHEDULE(codelock_thread(&codelock_pt))) {
+    PT_SCHEDULE(input_thread(&input_pt));
+
+    /*
+     * When running this example on a multitasking system, we must
+     * give other processes a chance to run too and therefore we call
+     * usleep() resp. Sleep() here. On a dedicated embedded system,
+     * we usually do not need to do this.
+     */
+#ifdef _WIN32
+    Sleep(0);
+#else
+    usleep(10);
+#endif
+  }
+
+  return 0;
+}
+/*---------------------------------------------------------------------------*/
+/*
+ * Finally, the implementation of the simple timer library follows.
+ */
+#ifdef _WIN32
+
+static int clock_time(void)
+{ return (int)GetTickCount(); }
+
+#else /* _WIN32 */
+
+static int clock_time(void)
+{
+  struct timeval tv;
+  struct timezone tz;
+  gettimeofday(&tv, &tz);
+  return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+
+#endif /* _WIN32 */
+
+static int timer_expired(struct timer *t)
+{ return (int)(clock_time() - t->start) >= (int)t->interval; }
+
+static void timer_set(struct timer *t, int interval)
+{ t->interval = interval; t->start = clock_time(); }
+/*---------------------------------------------------------------------------*/
diff --git a/third_party/pt/example-small.c b/third_party/pt/example-small.c
new file mode 100644
index 0000000..f65836c
--- /dev/null
+++ b/third_party/pt/example-small.c
@@ -0,0 +1,97 @@
+/**
+ * This is a very small example that shows how to use
+ * protothreads. The program consists of two protothreads that wait
+ * for each other to toggle a variable.
+ */
+
+/* We must always include pt.h in our protothreads code. */
+#include "pt.h"
+
+#include <stdio.h> /* For printf(). */
+
+/* Two flags that the two protothread functions use. */
+static int protothread1_flag, protothread2_flag;
+
+/**
+ * The first protothread function. A protothread function must always
+ * return an integer, but must never explicitly return - returning is
+ * performed inside the protothread statements.
+ *
+ * The protothread function is driven by the main loop further down in
+ * the code.
+ */
+static int
+protothread1(struct pt *pt)
+{
+  /* A protothread function must begin with PT_BEGIN() which takes a
+     pointer to a struct pt. */
+  PT_BEGIN(pt);
+
+  /* We loop forever here. */
+  while(1) {
+    /* Wait until the other protothread has set its flag. */
+    PT_WAIT_UNTIL(pt, protothread2_flag != 0);
+    printf("Protothread 1 running\n");
+
+    /* We then reset the other protothread's flag, and set our own
+       flag so that the other protothread can run. */
+    protothread2_flag = 0;
+    protothread1_flag = 1;
+
+    /* And we loop. */
+  }
+
+  /* All protothread functions must end with PT_END() which takes a
+     pointer to a struct pt. */
+  PT_END(pt);
+}
+
+/**
+ * The second protothread function. This is almost the same as the
+ * first one.
+ */
+static int
+protothread2(struct pt *pt)
+{
+  PT_BEGIN(pt);
+
+  while(1) {
+    /* Let the other protothread run. */
+    protothread2_flag = 1;
+
+    /* Wait until the other protothread has set its flag. */
+    PT_WAIT_UNTIL(pt, protothread1_flag != 0);
+    printf("Protothread 2 running\n");
+
+    /* We then reset the other protothread's flag. */
+    protothread1_flag = 0;
+
+    /* And we loop. */
+  }
+  PT_END(pt);
+}
+
+/**
+ * Finally, we have the main loop. Here is where the protothreads are
+ * initialized and scheduled. First, however, we define the
+ * protothread state variables pt1 and pt2, which hold the state of
+ * the two protothreads.
+ */
+static struct pt pt1, pt2;
+int
+main(void)
+{
+  /* Initialize the protothread state variables with PT_INIT(). */
+  PT_INIT(&pt1);
+  PT_INIT(&pt2);
+
+  /*
+   * Then we schedule the two protothreads by repeatedly calling their
+   * protothread functions and passing a pointer to the protothread
+   * state variables as arguments.
+   */
+  while(1) {
+    protothread1(&pt1);
+    protothread2(&pt2);
+  }
+}
diff --git a/third_party/pt/lc-addrlabels.h b/third_party/pt/lc-addrlabels.h
new file mode 100644
index 0000000..3e6474e
--- /dev/null
+++ b/third_party/pt/lc-addrlabels.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2004-2005, Swedish Institute of Computer Science.
+ * 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
+ *
+ * This file is part of the Contiki operating system.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ * $Id: lc-addrlabels.h,v 1.4 2006/06/03 11:29:43 adam Exp $
+ */
+
+/**
+ * \addtogroup lc
+ * @{
+ */
+
+/**
+ * \file
+ * Implementation of local continuations based on the "Labels as
+ * values" feature of gcc
+ * \author
+ * Adam Dunkels <adam@sics.se>
+ *
+ * This implementation of local continuations is based on a special
+ * feature of the GCC C compiler called "labels as values". This
+ * feature allows assigning pointers with the address of the code
+ * corresponding to a particular C label.
+ *
+ * For more information, see the GCC documentation:
+ * http://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html
+ *
+ */
+
+#ifndef __LC_ADDRLABELS_H__
+#define __LC_ADDRLABELS_H__
+
+/** \hideinitializer */
+typedef void * lc_t;
+
+#define LC_INIT(s) s = NULL
+
+#define LC_RESUME(s)				\
+  do {						\
+    if(s != NULL) {				\
+      goto *s;					\
+    }						\
+  } while(0)
+
+#define LC_CONCAT2(s1, s2) s1##s2
+#define LC_CONCAT(s1, s2) LC_CONCAT2(s1, s2)
+
+#define LC_SET(s)				\
+  do {						\
+    LC_CONCAT(LC_LABEL, __LINE__):   	        \
+    (s) = &&LC_CONCAT(LC_LABEL, __LINE__);	\
+  } while(0)
+
+#define LC_END(s)
+
+#endif /* __LC_ADDRLABELS_H__ */
+/** @} */
diff --git a/third_party/pt/lc-switch.h b/third_party/pt/lc-switch.h
new file mode 100644
index 0000000..dbdde01
--- /dev/null
+++ b/third_party/pt/lc-switch.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2004-2005, Swedish Institute of Computer Science.
+ * 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
+ *
+ * This file is part of the Contiki operating system.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ * $Id: lc-switch.h,v 1.4 2006/06/03 11:29:43 adam Exp $
+ */
+
+/**
+ * \addtogroup lc
+ * @{
+ */
+
+/**
+ * \file
+ * Implementation of local continuations based on switch() statment
+ * \author Adam Dunkels <adam@sics.se>
+ *
+ * This implementation of local continuations uses the C switch()
+ * statement to resume execution of a function somewhere inside the
+ * function's body. The implementation is based on the fact that
+ * switch() statements are able to jump directly into the bodies of
+ * control structures such as if() or while() statmenets.
+ *
+ * This implementation borrows heavily from Simon Tatham's coroutines
+ * implementation in C:
+ * http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
+ */
+
+#ifndef __LC_SWITCH_H__
+#define __LC_SWITCH_H__
+
+/* WARNING! lc implementation using switch() does not work if an
+   LC_SET() is done within another switch() statement! */
+
+/** \hideinitializer */
+typedef unsigned short lc_t;
+
+#define LC_INIT(s) s = 0;
+
+#define LC_RESUME(s) switch(s) { case 0:
+
+#define LC_SET(s) s = __LINE__; case __LINE__:
+
+#define LC_END(s) }
+
+#endif /* __LC_SWITCH_H__ */
+
+/** @} */
diff --git a/third_party/pt/lc.h b/third_party/pt/lc.h
new file mode 100644
index 0000000..0cf57e0
--- /dev/null
+++ b/third_party/pt/lc.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2004-2005, Swedish Institute of Computer Science.
+ * 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
+ *
+ * This file is part of the protothreads library.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ * $Id: lc.h,v 1.2 2005/02/24 10:36:59 adam Exp $
+ */
+
+/**
+ * \addtogroup pt
+ * @{
+ */
+
+/**
+ * \defgroup lc Local continuations
+ * @{
+ *
+ * Local continuations form the basis for implementing protothreads. A
+ * local continuation can be <i>set</i> in a specific function to
+ * capture the state of the function. After a local continuation has
+ * been set can be <i>resumed</i> in order to restore the state of the
+ * function at the point where the local continuation was set.
+ *
+ *
+ */
+
+/**
+ * \file lc.h
+ * Local continuations
+ * \author
+ * Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#ifdef DOXYGEN
+/**
+ * Initialize a local continuation.
+ *
+ * This operation initializes the local continuation, thereby
+ * unsetting any previously set continuation state.
+ *
+ * \hideinitializer
+ */
+#define LC_INIT(lc)
+
+/**
+ * Set a local continuation.
+ *
+ * The set operation saves the state of the function at the point
+ * where the operation is executed. As far as the set operation is
+ * concerned, the state of the function does <b>not</b> include the
+ * call-stack or local (automatic) variables, but only the program
+ * counter and such CPU registers that needs to be saved.
+ *
+ * \hideinitializer
+ */
+#define LC_SET(lc)
+
+/**
+ * Resume a local continuation.
+ *
+ * The resume operation resumes a previously set local continuation, thus
+ * restoring the state in which the function was when the local
+ * continuation was set. If the local continuation has not been
+ * previously set, the resume operation does nothing.
+ *
+ * \hideinitializer
+ */
+#define LC_RESUME(lc)
+
+/**
+ * Mark the end of local continuation usage.
+ *
+ * The end operation signifies that local continuations should not be
+ * used any more in the function. This operation is not needed for
+ * most implementations of local continuation, but is required by a
+ * few implementations.
+ *
+ * \hideinitializer
+ */
+#define LC_END(lc)
+
+/**
+ * \var typedef lc_t;
+ *
+ * The local continuation type.
+ *
+ * \hideinitializer
+ */
+#endif /* DOXYGEN */
+
+#ifndef __LC_H__
+#define __LC_H__
+
+
+#ifdef LC_INCLUDE
+#include LC_INCLUDE
+#else
+#include "lc-switch.h"
+#endif /* LC_INCLUDE */
+
+#endif /* __LC_H__ */
+
+/** @} */
+/** @} */
diff --git a/third_party/pt/pt-sem.h b/third_party/pt/pt-sem.h
new file mode 100644
index 0000000..5180dc6
--- /dev/null
+++ b/third_party/pt/pt-sem.h
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2004, Swedish Institute of Computer Science.
+ * 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
+ *
+ * This file is part of the protothreads library.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ * $Id: pt-sem.h,v 1.2 2005/02/24 10:36:59 adam Exp $
+ */
+
+/**
+ * \addtogroup pt
+ * @{
+ */
+
+/**
+ * \defgroup ptsem Protothread semaphores
+ * @{
+ *
+ * This module implements counting semaphores on top of
+ * protothreads. Semaphores are a synchronization primitive that
+ * provide two operations: "wait" and "signal". The "wait" operation
+ * checks the semaphore counter and blocks the thread if the counter
+ * is zero. The "signal" operation increases the semaphore counter but
+ * does not block. If another thread has blocked waiting for the
+ * semaphore that is signalled, the blocked thread will become
+ * runnable again.
+ *
+ * Semaphores can be used to implement other, more structured,
+ * synchronization primitives such as monitors and message
+ * queues/bounded buffers (see below).
+ *
+ * The following example shows how the producer-consumer problem, also
+ * known as the bounded buffer problem, can be solved using
+ * protothreads and semaphores. Notes on the program follow after the
+ * example.
+ *
+ \code
+#include "pt-sem.h"
+
+#define NUM_ITEMS 32
+#define BUFSIZE 8
+
+static struct pt_sem mutex, full, empty;
+
+PT_THREAD(producer(struct pt *pt))
+{
+  static int produced;
+
+  PT_BEGIN(pt);
+
+  for(produced = 0; produced < NUM_ITEMS; ++produced) {
+
+    PT_SEM_WAIT(pt, &full);
+
+    PT_SEM_WAIT(pt, &mutex);
+    add_to_buffer(produce_item());
+    PT_SEM_SIGNAL(pt, &mutex);
+
+    PT_SEM_SIGNAL(pt, &empty);
+  }
+
+  PT_END(pt);
+}
+
+PT_THREAD(consumer(struct pt *pt))
+{
+  static int consumed;
+
+  PT_BEGIN(pt);
+
+  for(consumed = 0; consumed < NUM_ITEMS; ++consumed) {
+
+    PT_SEM_WAIT(pt, &empty);
+
+    PT_SEM_WAIT(pt, &mutex);
+    consume_item(get_from_buffer());
+    PT_SEM_SIGNAL(pt, &mutex);
+
+    PT_SEM_SIGNAL(pt, &full);
+  }
+
+  PT_END(pt);
+}
+
+PT_THREAD(driver_thread(struct pt *pt))
+{
+  static struct pt pt_producer, pt_consumer;
+
+  PT_BEGIN(pt);
+
+  PT_SEM_INIT(&empty, 0);
+  PT_SEM_INIT(&full, BUFSIZE);
+  PT_SEM_INIT(&mutex, 1);
+
+  PT_INIT(&pt_producer);
+  PT_INIT(&pt_consumer);
+
+  PT_WAIT_THREAD(pt, producer(&pt_producer) &
+		     consumer(&pt_consumer));
+
+  PT_END(pt);
+}
+ \endcode
+ *
+ * The program uses three protothreads: one protothread that
+ * implements the consumer, one thread that implements the producer,
+ * and one protothread that drives the two other protothreads. The
+ * program uses three semaphores: "full", "empty" and "mutex". The
+ * "mutex" semaphore is used to provide mutual exclusion for the
+ * buffer, the "empty" semaphore is used to block the consumer is the
+ * buffer is empty, and the "full" semaphore is used to block the
+ * producer is the buffer is full.
+ *
+ * The "driver_thread" holds two protothread state variables,
+ * "pt_producer" and "pt_consumer". It is important to note that both
+ * these variables are declared as <i>static</i>. If the static
+ * keyword is not used, both variables are stored on the stack. Since
+ * protothreads do not store the stack, these variables may be
+ * overwritten during a protothread wait operation. Similarly, both
+ * the "consumer" and "producer" protothreads declare their local
+ * variables as static, to avoid them being stored on the stack.
+ *
+ *
+ */
+
+/**
+ * \file
+ * Couting semaphores implemented on protothreads
+ * \author
+ * Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#ifndef __PT_SEM_H__
+#define __PT_SEM_H__
+
+#include "pt.h"
+
+struct pt_sem {
+  unsigned int count;
+};
+
+/**
+ * Initialize a semaphore
+ *
+ * This macro initializes a semaphore with a value for the
+ * counter. Internally, the semaphores use an "unsigned int" to
+ * represent the counter, and therefore the "count" argument should be
+ * within range of an unsigned int.
+ *
+ * \param s (struct pt_sem *) A pointer to the pt_sem struct
+ * representing the semaphore
+ *
+ * \param c (unsigned int) The initial count of the semaphore.
+ * \hideinitializer
+ */
+#define PT_SEM_INIT(s, c) (s)->count = c
+
+/**
+ * Wait for a semaphore
+ *
+ * This macro carries out the "wait" operation on the semaphore. The
+ * wait operation causes the protothread to block while the counter is
+ * zero. When the counter reaches a value larger than zero, the
+ * protothread will continue.
+ *
+ * \param pt (struct pt *) A pointer to the protothread (struct pt) in
+ * which the operation is executed.
+ *
+ * \param s (struct pt_sem *) A pointer to the pt_sem struct
+ * representing the semaphore
+ *
+ * \hideinitializer
+ */
+#define PT_SEM_WAIT(pt, s)	\
+  do {						\
+    PT_WAIT_UNTIL(pt, (s)->count > 0);		\
+    --(s)->count;				\
+  } while(0)
+
+/**
+ * Signal a semaphore
+ *
+ * This macro carries out the "signal" operation on the semaphore. The
+ * signal operation increments the counter inside the semaphore, which
+ * eventually will cause waiting protothreads to continue executing.
+ *
+ * \param pt (struct pt *) A pointer to the protothread (struct pt) in
+ * which the operation is executed.
+ *
+ * \param s (struct pt_sem *) A pointer to the pt_sem struct
+ * representing the semaphore
+ *
+ * \hideinitializer
+ */
+#define PT_SEM_SIGNAL(pt, s) ++(s)->count
+
+#endif /* __PT_SEM_H__ */
+
+/** @} */
+/** @} */
diff --git a/third_party/pt/pt.h b/third_party/pt/pt.h
new file mode 100644
index 0000000..92856cb
--- /dev/null
+++ b/third_party/pt/pt.h
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 2004-2005, Swedish Institute of Computer Science.
+ * 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
+ *
+ * This file is part of the Contiki operating system.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ * $Id: pt.h,v 1.7 2006/10/02 07:52:56 adam Exp $
+ */
+
+/**
+ * \addtogroup pt
+ * @{
+ */
+
+/**
+ * \file
+ * Protothreads implementation.
+ * \author
+ * Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#ifndef __PT_H__
+#define __PT_H__
+
+#include "lc.h"
+
+struct pt {
+  lc_t lc;
+};
+
+#define PT_WAITING 0
+#define PT_YIELDED 1
+#define PT_EXITED  2
+#define PT_ENDED   3
+
+/**
+ * \name Initialization
+ * @{
+ */
+
+/**
+ * Initialize a protothread.
+ *
+ * Initializes a protothread. Initialization must be done prior to
+ * starting to execute the protothread.
+ *
+ * \param pt A pointer to the protothread control structure.
+ *
+ * \sa PT_SPAWN()
+ *
+ * \hideinitializer
+ */
+#define PT_INIT(pt)   LC_INIT((pt)->lc)
+
+/** @} */
+
+/**
+ * \name Declaration and definition
+ * @{
+ */
+
+/**
+ * Declaration of a protothread.
+ *
+ * This macro is used to declare a protothread. All protothreads must
+ * be declared with this macro.
+ *
+ * \param name_args The name and arguments of the C function
+ * implementing the protothread.
+ *
+ * \hideinitializer
+ */
+#define PT_THREAD(name_args) char name_args
+
+/**
+ * Declare the start of a protothread inside the C function
+ * implementing the protothread.
+ *
+ * This macro is used to declare the starting point of a
+ * protothread. It should be placed at the start of the function in
+ * which the protothread runs. All C statements above the PT_BEGIN()
+ * invokation will be executed each time the protothread is scheduled.
+ *
+ * \param pt A pointer to the protothread control structure.
+ *
+ * \hideinitializer
+ */
+#define PT_BEGIN(pt) { char PT_YIELD_FLAG = 1; LC_RESUME((pt)->lc)
+
+/**
+ * Declare the end of a protothread.
+ *
+ * This macro is used for declaring that a protothread ends. It must
+ * always be used together with a matching PT_BEGIN() macro.
+ *
+ * \param pt A pointer to the protothread control structure.
+ *
+ * \hideinitializer
+ */
+#define PT_END(pt) LC_END((pt)->lc); PT_YIELD_FLAG = 0; \
+                   PT_INIT(pt); return PT_ENDED; }
+
+/** @} */
+
+/**
+ * \name Blocked wait
+ * @{
+ */
+
+/**
+ * Block and wait until condition is true.
+ *
+ * This macro blocks the protothread until the specified condition is
+ * true.
+ *
+ * \param pt A pointer to the protothread control structure.
+ * \param condition The condition.
+ *
+ * \hideinitializer
+ */
+#define PT_WAIT_UNTIL(pt, condition)	        \
+  do {						\
+    LC_SET((pt)->lc);				\
+    if(!(condition)) {				\
+      return PT_WAITING;			\
+    }						\
+  } while(0)
+
+/**
+ * Block and wait while condition is true.
+ *
+ * This function blocks and waits while condition is true. See
+ * PT_WAIT_UNTIL().
+ *
+ * \param pt A pointer to the protothread control structure.
+ * \param cond The condition.
+ *
+ * \hideinitializer
+ */
+#define PT_WAIT_WHILE(pt, cond)  PT_WAIT_UNTIL((pt), !(cond))
+
+/** @} */
+
+/**
+ * \name Hierarchical protothreads
+ * @{
+ */
+
+/**
+ * Block and wait until a child protothread completes.
+ *
+ * This macro schedules a child protothread. The current protothread
+ * will block until the child protothread completes.
+ *
+ * \note The child protothread must be manually initialized with the
+ * PT_INIT() function before this function is used.
+ *
+ * \param pt A pointer to the protothread control structure.
+ * \param thread The child protothread with arguments
+ *
+ * \sa PT_SPAWN()
+ *
+ * \hideinitializer
+ */
+#define PT_WAIT_THREAD(pt, thread) PT_WAIT_WHILE((pt), PT_SCHEDULE(thread))
+
+/**
+ * Spawn a child protothread and wait until it exits.
+ *
+ * This macro spawns a child protothread and waits until it exits. The
+ * macro can only be used within a protothread.
+ *
+ * \param pt A pointer to the protothread control structure.
+ * \param child A pointer to the child protothread's control structure.
+ * \param thread The child protothread with arguments
+ *
+ * \hideinitializer
+ */
+#define PT_SPAWN(pt, child, thread)		\
+  do {						\
+    PT_INIT((child));				\
+    PT_WAIT_THREAD((pt), (thread));		\
+  } while(0)
+
+/** @} */
+
+/**
+ * \name Exiting and restarting
+ * @{
+ */
+
+/**
+ * Restart the protothread.
+ *
+ * This macro will block and cause the running protothread to restart
+ * its execution at the place of the PT_BEGIN() call.
+ *
+ * \param pt A pointer to the protothread control structure.
+ *
+ * \hideinitializer
+ */
+#define PT_RESTART(pt)				\
+  do {						\
+    PT_INIT(pt);				\
+    return PT_WAITING;			\
+  } while(0)
+
+/**
+ * Exit the protothread.
+ *
+ * This macro causes the protothread to exit. If the protothread was
+ * spawned by another protothread, the parent protothread will become
+ * unblocked and can continue to run.
+ *
+ * \param pt A pointer to the protothread control structure.
+ *
+ * \hideinitializer
+ */
+#define PT_EXIT(pt)				\
+  do {						\
+    PT_INIT(pt);				\
+    return PT_EXITED;			\
+  } while(0)
+
+/** @} */
+
+/**
+ * \name Calling a protothread
+ * @{
+ */
+
+/**
+ * Schedule a protothread.
+ *
+ * This function shedules a protothread. The return value of the
+ * function is non-zero if the protothread is running or zero if the
+ * protothread has exited.
+ *
+ * \param f The call to the C function implementing the protothread to
+ * be scheduled
+ *
+ * \hideinitializer
+ */
+#define PT_SCHEDULE(f) ((f) < PT_EXITED)
+
+/** @} */
+
+/**
+ * \name Yielding from a protothread
+ * @{
+ */
+
+/**
+ * Yield from the current protothread.
+ *
+ * This function will yield the protothread, thereby allowing other
+ * processing to take place in the system.
+ *
+ * \param pt A pointer to the protothread control structure.
+ *
+ * \hideinitializer
+ */
+#define PT_YIELD(pt)				\
+  do {						\
+    PT_YIELD_FLAG = 0;				\
+    LC_SET((pt)->lc);				\
+    if(PT_YIELD_FLAG == 0) {			\
+      return PT_YIELDED;			\
+    }						\
+  } while(0)
+
+/**
+ * \brief      Yield from the protothread until a condition occurs.
+ * \param pt   A pointer to the protothread control structure.
+ * \param cond The condition.
+ *
+ *             This function will yield the protothread, until the
+ *             specified condition evaluates to true.
+ *
+ *
+ * \hideinitializer
+ */
+#define PT_YIELD_UNTIL(pt, cond)		\
+  do {						\
+    PT_YIELD_FLAG = 0;				\
+    LC_SET((pt)->lc);				\
+    if((PT_YIELD_FLAG == 0) || !(cond)) {	\
+      return PT_YIELDED;			\
+    }						\
+  } while(0)
+
+/** @} */
+
+#endif /* __PT_H__ */
+
+/** @} */