[RFC v2 08/16] net: import picotcp from github

Antony Pavlov antonynpavlov at gmail.com
Sun Jul 19 13:07:15 PDT 2015


Original repo: https://github.com/tass-belgium/picotcp

The last original repo commit is v1.5.0

    commit f6d139147d86d74f9a53ba3c02b2a5b276858d95
    Author: laurens <laurens at localhost.localdomain>
    Date:   Thu Jun 25 12:54:38 2015 +0100

        Remove old check from fragments module

from development branch

Import mini-howto:

    $ cd barebox.git
    $ rm -rf net/picotcp
    $ git clone --branch ${branch_name} ${PICOTCP_REPO} net/picotcp
    $ rm -rf net/picotcp/*
    $ ( cd net/picotcp; git checkout .gitignore COPYING LICENSE README.md )
    $ ( cd net/picotcp; mkdir include modules stack )
    $ ( cd net/picotcp/include; git checkout heap.h pico_addressing.h pico_config.h pico_constants.h pico_device.h pico_eth.h pico_frame.h pico_module_eth.h pico_protocol.h pico_queue.h pico_socket.h pico_socket_multicast.h pico_stack.h pico_tree.h )
    $ ( cd net/picotcp/modules; git checkout pico_aodv.h pico_arp.c pico_arp.h pico_dev_loop.c pico_dev_loop.h pico_dev_mock.h pico_dev_null.c pico_dev_null.h pico_dhcp_client.c pico_dhcp_client.h pico_dhcp_common.c pico_dhcp_common.h pico_dns_client.h pico_dns_common.h pico_dns_sd.h pico_fragments.c pico_fragments.h pico_icmp4.c pico_icmp4.h pico_icmp6.h pico_igmp.h pico_ipfilter.h pico_ipv4.c pico_ipv4.h pico_ipv6.h pico_ipv6_nd.h pico_mm.h pico_nat.h pico_olsr.h pico_socket_tcp.h pico_socket_udp.c pico_socket_udp.h pico_tcp.h pico_udp.c pico_udp.h )
    $ ( cd net/picotcp/stack; git checkout pico_device.c pico_frame.c pico_protocol.c pico_socket.c pico_socket_multicast.c pico_stack.c pico_tree.c )
    $ rm -rf net/picotcp/.git
    $ git add net/picotcp/
    $ git commit -s

Signed-off-by: Antony Pavlov <antonynpavlov at gmail.com>
---
 net/picotcp/.gitignore                      |   24 +
 net/picotcp/COPYING                         |    8 +
 net/picotcp/LICENSE                         |  339 ++++
 net/picotcp/README.md                       |   33 +
 net/picotcp/include/heap.h                  |   83 +
 net/picotcp/include/pico_addressing.h       |   52 +
 net/picotcp/include/pico_config.h           |  229 +++
 net/picotcp/include/pico_constants.h        |   54 +
 net/picotcp/include/pico_device.h           |   54 +
 net/picotcp/include/pico_eth.h              |   21 +
 net/picotcp/include/pico_frame.h            |  122 ++
 net/picotcp/include/pico_module_eth.h       |   33 +
 net/picotcp/include/pico_protocol.h         |   95 ++
 net/picotcp/include/pico_queue.h            |  166 ++
 net/picotcp/include/pico_socket.h           |  268 ++++
 net/picotcp/include/pico_socket_multicast.h |   10 +
 net/picotcp/include/pico_stack.h            |   87 ++
 net/picotcp/include/pico_tree.h             |   93 ++
 net/picotcp/modules/pico_aodv.h             |  131 ++
 net/picotcp/modules/pico_arp.c              |  531 +++++++
 net/picotcp/modules/pico_arp.h              |   32 +
 net/picotcp/modules/pico_dev_loop.c         |   66 +
 net/picotcp/modules/pico_dev_loop.h         |   15 +
 net/picotcp/modules/pico_dev_mock.h         |   47 +
 net/picotcp/modules/pico_dev_null.c         |   60 +
 net/picotcp/modules/pico_dev_null.h         |   15 +
 net/picotcp/modules/pico_dhcp_client.c      |  960 ++++++++++++
 net/picotcp/modules/pico_dhcp_client.h      |   30 +
 net/picotcp/modules/pico_dhcp_common.c      |  189 +++
 net/picotcp/modules/pico_dhcp_common.h      |  186 +++
 net/picotcp/modules/pico_dns_client.h       |   46 +
 net/picotcp/modules/pico_dns_common.h       |  521 +++++++
 net/picotcp/modules/pico_dns_sd.h           |   90 ++
 net/picotcp/modules/pico_fragments.c        |  345 +++++
 net/picotcp/modules/pico_fragments.h        |   11 +
 net/picotcp/modules/pico_icmp4.c            |  395 +++++
 net/picotcp/modules/pico_icmp4.h            |  162 ++
 net/picotcp/modules/pico_icmp6.h            |  259 ++++
 net/picotcp/modules/pico_igmp.h             |   26 +
 net/picotcp/modules/pico_ipfilter.h         |   29 +
 net/picotcp/modules/pico_ipv4.c             | 1574 +++++++++++++++++++
 net/picotcp/modules/pico_ipv4.h             |  124 ++
 net/picotcp/modules/pico_ipv6.h             |  153 ++
 net/picotcp/modules/pico_ipv6_nd.h          |   26 +
 net/picotcp/modules/pico_mm.h               |   98 ++
 net/picotcp/modules/pico_nat.h              |   90 ++
 net/picotcp/modules/pico_olsr.h             |   32 +
 net/picotcp/modules/pico_socket_tcp.h       |   33 +
 net/picotcp/modules/pico_socket_udp.c       |  250 +++
 net/picotcp/modules/pico_socket_udp.h       |   19 +
 net/picotcp/modules/pico_tcp.h              |  102 ++
 net/picotcp/modules/pico_udp.c              |  216 +++
 net/picotcp/modules/pico_udp.h              |   45 +
 net/picotcp/stack/pico_device.c             |  408 +++++
 net/picotcp/stack/pico_frame.c              |  286 ++++
 net/picotcp/stack/pico_protocol.c           |  214 +++
 net/picotcp/stack/pico_socket.c             | 2213 +++++++++++++++++++++++++++
 net/picotcp/stack/pico_socket_multicast.c   |  956 ++++++++++++
 net/picotcp/stack/pico_stack.c              | 1157 ++++++++++++++
 net/picotcp/stack/pico_tree.c               |  575 +++++++
 60 files changed, 14488 insertions(+)

diff --git a/net/picotcp/.gitignore b/net/picotcp/.gitignore
new file mode 100644
index 0000000..88593da
--- /dev/null
+++ b/net/picotcp/.gitignore
@@ -0,0 +1,24 @@
+*.d
+*.o
+*.a
+*.out
+*.swp
+tags
+build
+UNIT_*
+core
+core.*
+.DS_Store
+cscope.files
+cscope.out
+*.so
+*.aux
+*.pdf
+*.toc
+*.gz
+*.log
+*.pyc
+*.elf
+*.gcov
+*.gcda
+*.gcno
diff --git a/net/picotcp/COPYING b/net/picotcp/COPYING
new file mode 100644
index 0000000..8a5fe4b
--- /dev/null
+++ b/net/picotcp/COPYING
@@ -0,0 +1,8 @@
+PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems.
+
+Released under the GNU General Public License, version 2.
+See LICENSE for details.
+
+Different licensing models may exist, at the sole discretion of
+the Copyright holders.
+
diff --git a/net/picotcp/LICENSE b/net/picotcp/LICENSE
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/net/picotcp/LICENSE
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/net/picotcp/README.md b/net/picotcp/README.md
new file mode 100644
index 0000000..9e32eb7
--- /dev/null
+++ b/net/picotcp/README.md
@@ -0,0 +1,33 @@
+picoTCP
+
+---------------
+
+Welcome to the one and only <font color=ff00f0>picoTCP repository</font>. 
+
+picoTCP is a small-footprint, modular TCP/IP stack designed for embedded systems and the Internet of Things. It's actively being developed by *[Altran Intelligent Systems](http://intelligent-systems.altran.com/)*.
+
+This code is released under the terms of GNU GPL v2 only. Some rights reserved.
+Other licenses may apply at the sole discretion of the copyright holders.
+
+Learn how to use picoTCP in your project by going through the Getting Started guide on our [GitHub wiki](https://github.com/tass-belgium/picotcp/wiki).
+
+For more information visit the [picoTCP website](http://www.picotcp.com), send us an email or contact us on [Twitter](https://twitter.com/picotcp), [Facebook](https://www.facebook.com/picoTCP) or [Reddit](http://www.reddit.com/r/picotcp/).
+
+Wondering about picoTCP's code quality? Check [our TiCS score](http://tics.picotcp.com:42506/TIOBEPortal/TICS/treeviewer?)
+
+
+---------------
+
+Continuous integration
+
+Jenkins Functional tests: 
+[![Jenkins autotest](http://jenkins.picotcp.com:8080/buildStatus/icon?job=PicoTCP_rel_autotest)](http://jenkins.picotcp.com:8080/job/PicoTCP_rel_autotest)
+
+Jenkins Unit tests      : 
+[![Jenkins unit tests](http://jenkins.picotcp.com:8080/buildStatus/icon?job=PicoTCP_rel_unit_tests)](http://jenkins.picotcp.com:8080/job/PicoTCP_rel_unit_tests)
+
+Jenkins RFC compliance  :
+[![Jenkins RFC Compliance](http://jenkins.picotcp.com:8080/buildStatus/icon?job=PicoTCP_rel_RF_mbed)](http://jenkins.picotcp.com:8080/job/PicoTCP_rel_RF_mbed)
+
+Jenkins TICS quality    :
+[![Jenkins TICS](http://jenkins.picotcp.com:8080/buildStatus/icon?job=PicoTCP_rel_TICS)](http://jenkins.picotcp.com:8080/job/PicoTCP_rel_TICS/)
diff --git a/net/picotcp/include/heap.h b/net/picotcp/include/heap.h
new file mode 100644
index 0000000..88f495d
--- /dev/null
+++ b/net/picotcp/include/heap.h
@@ -0,0 +1,83 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+ *********************************************************************/
+
+#define DECLARE_HEAP(type, orderby) \
+    struct heap_ ## type {   \
+        uint32_t size;    \
+        uint32_t n;       \
+        type *top;        \
+    }; \
+    typedef struct heap_ ## type heap_ ## type; \
+    static inline int heap_insert(struct heap_ ## type *heap, type * el) \
+    { \
+        uint32_t i; \
+        type *newTop; \
+        if (++heap->n >= heap->size) {                                                \
+            newTop = PICO_ZALLOC((heap->n + 1) * sizeof(type)); \
+            if(!newTop) { \
+                heap->n--; \
+                return -1; \
+            } \
+            if (heap->top)  { \
+                memcpy(newTop, heap->top, heap->n * sizeof(type)); \
+                PICO_FREE(heap->top); \
+            } \
+            heap->top = newTop;             \
+            heap->size++;                                                               \
+        }                                                                             \
+        if (heap->n == 1) {                                                       \
+            memcpy(&heap->top[1], el, sizeof(type));                                    \
+            return 0;                                                                   \
+        }                                                                             \
+        for (i = heap->n; ((i > 1) && (heap->top[i / 2].orderby > el->orderby)); i /= 2) {        \
+            memcpy(&heap->top[i], &heap->top[i / 2], sizeof(type));                     \
+        }             \
+        memcpy(&heap->top[i], el, sizeof(type));                                      \
+        return 0;                                                                     \
+    } \
+    static inline int heap_peek(struct heap_ ## type *heap, type * first) \
+    { \
+        type *last;           \
+        uint32_t i, child;        \
+        if(heap->n == 0) {    \
+            return -1;          \
+        }                     \
+        memcpy(first, &heap->top[1], sizeof(type));   \
+        last = &heap->top[heap->n--];                 \
+        for(i = 1; (i * 2u) <= heap->n; i = child) {   \
+            child = 2u * i;                              \
+            if ((child != heap->n) &&                   \
+                (heap->top[child + 1]).orderby          \
+                < (heap->top[child]).orderby)           \
+                child++;                                \
+            if (last->orderby >                         \
+                heap->top[child].orderby)               \
+                memcpy(&heap->top[i], &heap->top[child], \
+                       sizeof(type));                  \
+            else                                        \
+                break;                                  \
+        }                                             \
+        memcpy(&heap->top[i], last, sizeof(type));    \
+        return 0;                                     \
+    } \
+    static inline type *heap_first(heap_ ## type * heap)  \
+    { \
+        if (heap->n == 0)     \
+            return NULL;        \
+        return &heap->top[1];  \
+    } \
+    static inline heap_ ## type *heap_init(void) \
+    { \
+        heap_ ## type * p = (heap_ ## type *)PICO_ZALLOC(sizeof(heap_ ## type));  \
+        return p;     \
+    } \
+    /*static inline void heap_destroy(heap_ ## type * h) \
+       { \
+        PICO_FREE(h->top); \
+        PICO_FREE(h); \
+       } \*/
+
+
diff --git a/net/picotcp/include/pico_addressing.h b/net/picotcp/include/pico_addressing.h
new file mode 100644
index 0000000..de62281
--- /dev/null
+++ b/net/picotcp/include/pico_addressing.h
@@ -0,0 +1,52 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+ *********************************************************************/
+#ifndef INCLUDE_PICO_ADDRESSING
+#define INCLUDE_PICO_ADDRESSING
+
+#include "pico_config.h"
+
+PACKED_STRUCT_DEF pico_ip4
+{
+    uint32_t addr;
+};
+
+PACKED_STRUCT_DEF pico_ip6
+{
+    uint8_t addr[16];
+};
+
+union pico_address
+{
+    struct pico_ip4 ip4;
+    struct pico_ip6 ip6;
+};
+
+PACKED_STRUCT_DEF pico_eth
+{
+    uint8_t addr[6];
+    uint8_t padding[2];
+};
+
+extern const uint8_t PICO_ETHADDR_ALL[];
+
+
+PACKED_STRUCT_DEF pico_trans
+{
+    uint16_t sport;
+    uint16_t dport;
+
+};
+
+/* Here are some protocols. */
+#define PICO_PROTO_IPV4   0
+#define PICO_PROTO_ICMP4  1
+#define PICO_PROTO_IGMP  2
+#define PICO_PROTO_TCP    6
+#define PICO_PROTO_UDP    17
+#define PICO_PROTO_IPV6   41
+#define PICO_PROTO_ICMP6  58
+
+#endif
diff --git a/net/picotcp/include/pico_config.h b/net/picotcp/include/pico_config.h
new file mode 100644
index 0000000..6b9c040
--- /dev/null
+++ b/net/picotcp/include/pico_config.h
@@ -0,0 +1,229 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+ *********************************************************************/
+#include "pico_defines.h"
+#ifndef INCLUDE_PICO_CONFIG
+#define INCLUDE_PICO_CONFIG
+#ifndef __KERNEL__
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#else
+#include <linux/types.h>
+#endif
+
+#if defined __IAR_SYSTEMS_ICC__ || defined ATOP
+#   define PACKED_STRUCT_DEF __packed struct
+#   define PEDANTIC_STRUCT_DEF __packed struct
+#   define PACKED_UNION_DEF  __packed union
+#   define WEAK
+#else
+#   define PACKED_STRUCT_DEF struct __attribute__((packed))
+#   define PEDANTIC_STRUCT_DEF struct
+#   define PACKED_UNION_DEF  union   /* Sane compilers do not require packed unions */
+#   define WEAK __attribute__((weak))
+#   ifdef __GNUC__
+#       define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+#       if ((GCC_VERSION >= 40800))
+#           define BYTESWAP_GCC
+#       endif
+#   endif
+#endif
+
+#ifdef PICO_BIGENDIAN
+
+# define PICO_IDETH_IPV4 0x0800
+# define PICO_IDETH_ARP 0x0806
+# define PICO_IDETH_IPV6 0x86DD
+
+# define PICO_ARP_REQUEST 0x0001
+# define PICO_ARP_REPLY   0x0002
+# define PICO_ARP_HTYPE_ETH 0x0001
+
+#define short_be(x) (x)
+#define long_be(x) (x)
+#define long_long_be(x) (x)
+
+static inline uint16_t short_from(void *_p)
+{
+    unsigned char *p = (unsigned char *)_p;
+    uint16_t r, p0, p1;
+    p0 = p[0];
+    p1 = p[1];
+    r = (p0 << 8) + p1;
+    return r;
+}
+
+static inline uint32_t long_from(void *_p)
+{
+    unsigned char *p = (unsigned char *)_p;
+    uint32_t r, p0, p1, p2, p3;
+    p0 = p[0];
+    p1 = p[1];
+    p2 = p[2];
+    p3 = p[3];
+    r = (p0 << 24) + (p1 << 16) + (p2 << 8) + p3;
+    return r;
+}
+
+#else
+
+static inline uint16_t short_from(void *_p)
+{
+    unsigned char *p = (unsigned char *)_p;
+    uint16_t r, _p0, _p1;
+    _p0 = p[0];
+    _p1 = p[1];
+    r = (uint16_t)((_p1 << 8u) + _p0);
+    return r;
+}
+
+static inline uint32_t long_from(void *_p)
+{
+    unsigned char *p = (unsigned char *)_p;
+    uint32_t r, _p0, _p1, _p2, _p3;
+    _p0 = p[0];
+    _p1 = p[1];
+    _p2 = p[2];
+    _p3 = p[3];
+    r = (_p3 << 24) + (_p2 << 16) + (_p1 << 8) + _p0;
+    return r;
+}
+
+
+# define PICO_IDETH_IPV4 0x0008
+# define PICO_IDETH_ARP 0x0608
+# define PICO_IDETH_IPV6 0xDD86
+
+# define PICO_ARP_REQUEST 0x0100
+# define PICO_ARP_REPLY   0x0200
+# define PICO_ARP_HTYPE_ETH 0x0100
+
+#   ifndef BYTESWAP_GCC
+static inline uint16_t short_be(uint16_t le)
+{
+    return (uint16_t)(((le & 0xFFu) << 8) | ((le >> 8u) & 0xFFu));
+}
+
+static inline uint32_t long_be(uint32_t le)
+{
+    uint8_t *b = (uint8_t *)≤
+    uint32_t be = 0;
+    uint32_t b0, b1, b2;
+    b0 = b[0];
+    b1 = b[1];
+    b2 = b[2];
+    be = b[3] + (b2 << 8) + (b1 << 16) + (b0 << 24);
+    return be;
+}
+static inline uint64_t long_long_be(uint64_t le)
+{
+    uint8_t *b = (uint8_t *)≤
+    uint64_t be = 0;
+    uint64_t b0, b1, b2, b3, b4, b5, b6;
+    b0 = b[0];
+    b1 = b[1];
+    b2 = b[2];
+    b3 = b[3];
+    b4 = b[4];
+    b5 = b[5];
+    b6 = b[6];
+    be = b[7] + (b6 << 8) + (b5 << 16) + (b4 << 24) + (b3 << 32) + (b2 << 40) + (b1 << 48) + (b0 << 56);
+    return be;
+}
+#   else
+/*
+   extern uint32_t __builtin_bswap32(uint32_t);
+   extern uint16_t __builtin_bswap16(uint16_t);
+   extern uint64_t __builtin_bswap64(uint64_t);
+ */
+
+static inline uint32_t long_be(uint32_t le)
+{
+    return (uint32_t)__builtin_bswap32(le);
+}
+
+static inline uint16_t short_be(uint16_t le)
+{
+    return (uint16_t)__builtin_bswap16(le);
+}
+
+static inline uint64_t long_long_be(uint64_t le)
+{
+    return (uint64_t)__builtin_bswap64(le);
+}
+
+#   endif /* BYTESWAP_GCC */
+#endif
+
+
+/* Mockables */
+#if defined UNIT_TEST
+#   define MOCKABLE __attribute__((weak))
+#else
+#   define MOCKABLE
+#endif
+
+#include "pico_constants.h"
+#include "pico_mm.h"
+
+#define IGNORE_PARAMETER(x)  ((void)x)
+
+#define PICO_MEM_DEFAULT_SLAB_SIZE 1600
+#define PICO_MEM_PAGE_SIZE 4096
+#define PICO_MEM_PAGE_LIFETIME 100
+#define PICO_MIN_HEAP_SIZE 600
+#define PICO_MIN_SLAB_SIZE 1200
+#define PICO_MAX_SLAB_SIZE 1600
+#define PICO_MEM_MINIMUM_OBJECT_SIZE 4
+
+
+/*** *** *** *** *** *** ***
+ *** PLATFORM SPECIFIC   ***
+ *** *** *** *** *** *** ***/
+#if defined PICO_PORT_CUSTOM
+# include "pico_port.h"
+#elif defined CORTEX_M4_HARDFLOAT
+# include "arch/pico_cortex_m.h"
+#elif defined CORTEX_M4_SOFTFLOAT
+# include "arch/pico_cortex_m.h"
+#elif defined CORTEX_M3
+# include "arch/pico_cortex_m.h"
+#elif defined PIC24
+# include "arch/pico_pic24.h"
+#elif defined MSP430
+# include "arch/pico_msp430.h"
+#elif defined MBED_TEST
+# include "arch/pico_mbed.h"
+#elif defined AVR
+# include "arch/pico_avr.h"
+#elif defined ARM9
+# include "arch/pico_arm9.h"
+#elif defined ESP8266
+# include "arch/pico_esp8266.h"
+#elif defined MT7681
+# include "arch/pico_generic_gcc.h"
+#elif defined FAULTY
+# include "../test/pico_faulty.h"
+#elif defined ARCHNONE
+# include "arch/pico_none.h"
+#elif defined GENERIC
+# include "arch/pico_generic_gcc.h"
+#elif defined __KERNEL__
+# include "arch/pico_linux.h"
+/* #elif defined ... */
+#else
+# include "arch/pico_posix.h"
+#endif
+
+#ifdef PICO_SUPPORT_MM
+#define PICO_ZALLOC(x) pico_mem_zalloc(x)
+#define PICO_FREE(x) pico_mem_free(x)
+#else
+#define PICO_ZALLOC(x) pico_zalloc(x)
+#define PICO_FREE(x) pico_free(x)
+#endif  /* PICO_SUPPORT_MM */
+
+#endif
diff --git a/net/picotcp/include/pico_constants.h b/net/picotcp/include/pico_constants.h
new file mode 100644
index 0000000..f043408
--- /dev/null
+++ b/net/picotcp/include/pico_constants.h
@@ -0,0 +1,54 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+ *********************************************************************/
+#ifndef INCLUDE_PICO_CONST
+#define INCLUDE_PICO_CONST
+/* Included from pico_config.h */
+
+/** Non-endian dependant constants */
+#define PICO_SIZE_IP4    4
+#define PICO_SIZE_IP6   16
+#define PICO_SIZE_ETH    6
+#define PICO_SIZE_TRANS  8
+
+/** Endian-dependant constants **/
+typedef uint64_t pico_time;
+extern volatile uint64_t pico_tick;
+
+
+/*** *** *** *** *** *** ***
+ ***     ARP CONFIG      ***
+ *** *** *** *** *** *** ***/
+
+#include "pico_addressing.h"
+
+/* Maximum amount of accepted ARP requests per burst interval */
+#define PICO_ARP_MAX_RATE 1
+/* Duration of the burst interval in milliseconds */
+#define PICO_ARP_INTERVAL 1000
+
+/* Add well-known host numbers here. (bigendian constants only beyond this point) */
+#define PICO_IP4_ANY (0x00000000U)
+#define PICO_IP4_BCAST (0xffffffffU)
+
+/* defined in modules/pico_ipv6.c */
+#ifdef PICO_SUPPORT_IPV6
+extern const uint8_t PICO_IPV6_ANY[PICO_SIZE_IP6];
+#endif
+
+static inline uint32_t pico_hash(const void *buf, uint32_t size)
+{
+    uint32_t hash = 5381;
+    uint32_t i;
+    const uint8_t *ptr = (const uint8_t *)buf;
+    for(i = 0; i < size; i++)
+        hash = ((hash << 5) + hash) + ptr[i]; /* hash * 33 + char */
+    return hash;
+}
+
+/* Debug */
+/* #define PICO_SUPPORT_DEBUG_MEMORY */
+/* #define PICO_SUPPORT_DEBUG_TOOLS */
+#endif
diff --git a/net/picotcp/include/pico_device.h b/net/picotcp/include/pico_device.h
new file mode 100644
index 0000000..a2d03ec
--- /dev/null
+++ b/net/picotcp/include/pico_device.h
@@ -0,0 +1,54 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+ *********************************************************************/
+#ifndef INCLUDE_PICO_DEVICE
+#define INCLUDE_PICO_DEVICE
+#include "pico_queue.h"
+#include "pico_frame.h"
+#include "pico_addressing.h"
+#include "pico_tree.h"
+extern struct pico_tree Device_tree;
+#include "pico_ipv6_nd.h"
+#define MAX_DEVICE_NAME 16
+
+
+struct pico_ethdev {
+    struct pico_eth mac;
+};
+
+struct pico_device {
+    char name[MAX_DEVICE_NAME];
+    uint32_t hash;
+    uint32_t overhead;
+    uint32_t mtu;
+    struct pico_ethdev *eth; /* Null if non-ethernet */
+    struct pico_queue *q_in;
+    struct pico_queue *q_out;
+    int (*link_state)(struct pico_device *self);
+    int (*send)(struct pico_device *self, void *buf, int len); /* Send function. Return 0 if busy */
+    int (*poll)(struct pico_device *self, int loop_score);
+    void (*destroy)(struct pico_device *self);
+    int (*dsr)(struct pico_device *self, int loop_score);
+    int __serving_interrupt;
+    /* used to signal the upper layer the number of events arrived since the last processing */
+    volatile int eventCnt;
+  #ifdef PICO_SUPPORT_IPV6
+    struct pico_nd_hostvars hostvars;
+  #endif
+};
+
+
+int pico_device_init(struct pico_device *dev, const char *name, uint8_t *mac);
+void pico_device_destroy(struct pico_device *dev);
+int pico_devices_loop(int loop_score, int direction);
+struct pico_device*pico_get_device(const char*name);
+int32_t pico_device_broadcast(struct pico_frame *f);
+int pico_device_link_state(struct pico_device *dev);
+int pico_device_ipv6_random_ll(struct pico_device *dev);
+#ifdef PICO_SUPPORT_IPV6
+struct pico_ipv6_link *pico_ipv6_link_add_local(struct pico_device *dev, const struct pico_ip6 *prefix);
+#endif
+
+#endif
diff --git a/net/picotcp/include/pico_eth.h b/net/picotcp/include/pico_eth.h
new file mode 100644
index 0000000..89846c7
--- /dev/null
+++ b/net/picotcp/include/pico_eth.h
@@ -0,0 +1,21 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+ *********************************************************************/
+#ifndef INCLUDE_PICO_ETH
+#define INCLUDE_PICO_ETH
+#include "pico_addressing.h"
+#include "pico_ipv4.h"
+#include "pico_ipv6.h"
+
+
+PACKED_STRUCT_DEF pico_eth_hdr {
+    uint8_t daddr[6];
+    uint8_t saddr[6];
+    uint16_t proto;
+};
+
+#define PICO_SIZE_ETHHDR 14
+
+#endif
diff --git a/net/picotcp/include/pico_frame.h b/net/picotcp/include/pico_frame.h
new file mode 100644
index 0000000..feb8f78
--- /dev/null
+++ b/net/picotcp/include/pico_frame.h
@@ -0,0 +1,122 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+ *********************************************************************/
+#ifndef INCLUDE_PICO_FRAME
+#define INCLUDE_PICO_FRAME
+#include "pico_config.h"
+
+
+#define PICO_FRAME_FLAG_BCAST               (0x01)
+#define PICO_FRAME_FLAG_EXT_BUFFER          (0x02)
+#define PICO_FRAME_FLAG_EXT_USAGE_COUNTER   (0x04)
+#define PICO_FRAME_FLAG_SACKED              (0x80)
+#define IS_BCAST(f) ((f->flags & PICO_FRAME_FLAG_BCAST) == PICO_FRAME_FLAG_BCAST)
+
+
+struct pico_socket;
+
+
+struct pico_frame {
+
+    /* Connector for queues */
+    struct pico_frame *next;
+
+    /* Start of the whole buffer, total frame length. */
+    unsigned char *buffer;
+    uint32_t buffer_len;
+
+    /* For outgoing packets: this is the meaningful buffer. */
+    unsigned char *start;
+    uint32_t len;
+
+    /* Pointer to usage counter */
+    uint32_t *usage_count;
+
+    /* Pointer to protocol headers */
+    uint8_t *datalink_hdr;
+
+    uint8_t *net_hdr;
+    uint16_t net_len;
+    uint8_t *transport_hdr;
+    uint16_t transport_len;
+    uint8_t *app_hdr;
+    uint16_t app_len;
+
+    /* Pointer to the phisical device this packet belongs to.
+     * Should be valid in both routing directions
+     */
+    struct pico_device *dev;
+
+    pico_time timestamp;
+
+    /* Failures due to bad datalink addressing. */
+    uint16_t failure_count;
+
+    /* Protocol over IP */
+    uint8_t proto;
+
+    /* PICO_FRAME_FLAG_* */
+    uint8_t flags;
+
+    /* Pointer to payload */
+    unsigned char *payload;
+    uint16_t payload_len;
+
+#ifdef PICO_SUPPORT_IPFRAG
+    /* Payload fragmentation info */
+    uint16_t frag;
+#endif
+
+    /* Pointer to socket */
+    struct pico_socket *sock;
+
+    /* Pointer to transport info, used to store remote UDP endpoint (IP + port) */
+    void *info;
+
+    /*Priority. "best-effort" priority, the default value is 0. Priority can be in between -10 and +10*/
+    int8_t priority;
+    uint8_t transport_flags_saved;
+
+    /* Callback to notify listener when the buffer has been discarded */
+    void (*notify_free)(uint8_t *);
+
+    uint8_t send_ttl; /* Special TTL/HOPS value, 0 = auto assign */
+    uint8_t send_tos; /* Type of service */
+};
+
+/** frame alloc/dealloc/copy **/
+void pico_frame_discard(struct pico_frame *f);
+struct pico_frame *pico_frame_copy(struct pico_frame *f);
+struct pico_frame *pico_frame_deepcopy(struct pico_frame *f);
+struct pico_frame *pico_frame_alloc(uint32_t size);
+int pico_frame_grow(struct pico_frame *f, uint32_t size);
+struct pico_frame *pico_frame_alloc_skeleton(uint32_t size, int ext_buffer);
+int pico_frame_skeleton_set_buffer(struct pico_frame *f, void *buf);
+uint16_t pico_checksum(void *inbuf, uint32_t len);
+uint16_t pico_dualbuffer_checksum(void *b1, uint32_t len1, void *b2, uint32_t len2);
+
+static inline int pico_is_digit(char c)
+{
+    if (c < '0' || c > '9')
+        return 0;
+
+    return 1;
+}
+
+static inline int pico_is_hex(char c)
+{
+    if (c >= '0' && c <= '9')
+        return 1;
+
+    if (c >= 'a' && c <= 'f')
+        return 1;
+
+    if (c >= 'A' && c <= 'F')
+        return 1;
+
+    return 0;
+}
+
+#endif
diff --git a/net/picotcp/include/pico_module_eth.h b/net/picotcp/include/pico_module_eth.h
new file mode 100644
index 0000000..c86680d
--- /dev/null
+++ b/net/picotcp/include/pico_module_eth.h
@@ -0,0 +1,33 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+ *********************************************************************/
+#ifndef PICO_MODULE_IPV4_H
+#define PICO_MODULE_IPV4_H
+
+struct pico_arp_entry {
+    struct eth dest;
+#ifdef PICO_CONFIG_IPV4
+    struct ipv4 addr_ipv4;
+#endif
+    RB_ENTRY(pico_arp_entry) node;
+};
+
+/* Configured device */
+struct pico_eth_link {
+    struct pico_device *dev;
+    struct eth address;
+    struct eth netmask;
+    RB_ENTRY(pico_eth_link) node;
+};
+
+#ifndef IS_MODULE_ETH
+# define _mod extern
+#else
+# define _mod
+#endif
+_mod struct pico_module pico_module_eth;
+#undef _mod
+
+#endif
diff --git a/net/picotcp/include/pico_protocol.h b/net/picotcp/include/pico_protocol.h
new file mode 100644
index 0000000..fd7c122
--- /dev/null
+++ b/net/picotcp/include/pico_protocol.h
@@ -0,0 +1,95 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+ *********************************************************************/
+#ifndef INCLUDE_PICO_PROTOCOL
+#define INCLUDE_PICO_PROTOCOL
+#include "pico_config.h"
+#include "pico_queue.h"
+
+#define PICO_LOOP_DIR_IN   1
+#define PICO_LOOP_DIR_OUT  2
+
+enum pico_layer {
+    PICO_LAYER_DATALINK = 2, /* Ethernet only. */
+    PICO_LAYER_NETWORK = 3, /* IPv4, IPv6, ARP. Arp is there because it communicates with L2 */
+    PICO_LAYER_TRANSPORT = 4, /* UDP, TCP, ICMP */
+    PICO_LAYER_SOCKET = 5   /* Socket management */
+};
+
+enum pico_err_e {
+    PICO_ERR_NOERR = 0,
+    PICO_ERR_EPERM = 1,
+    PICO_ERR_ENOENT = 2,
+    /* ... */
+    PICO_ERR_EINTR = 4,
+    PICO_ERR_EIO = 5,
+    PICO_ERR_ENXIO = 6,
+    /* ... */
+    PICO_ERR_EAGAIN = 11,
+    PICO_ERR_ENOMEM = 12,
+    PICO_ERR_EACCESS = 13,
+    PICO_ERR_EFAULT = 14,
+    /* ... */
+    PICO_ERR_EBUSY = 16,
+    PICO_ERR_EEXIST = 17,
+    /* ... */
+    PICO_ERR_EINVAL = 22,
+    /* ... */
+    PICO_ERR_ENONET = 64,
+    /* ... */
+    PICO_ERR_EPROTO = 71,
+    /* ... */
+    PICO_ERR_ENOPROTOOPT = 92,
+    PICO_ERR_EPROTONOSUPPORT = 93,
+    /* ... */
+    PICO_ERR_EOPNOTSUPP = 95,
+    PICO_ERR_EADDRINUSE = 98,
+    PICO_ERR_EADDRNOTAVAIL = 99,
+    PICO_ERR_ENETDOWN = 100,
+    PICO_ERR_ENETUNREACH = 101,
+    /* ... */
+    PICO_ERR_ECONNRESET = 104,
+    /* ... */
+    PICO_ERR_EISCONN = 106,
+    PICO_ERR_ENOTCONN = 107,
+    PICO_ERR_ESHUTDOWN = 108,
+    /* ... */
+    PICO_ERR_ETIMEDOUT = 110,
+    PICO_ERR_ECONNREFUSED = 111,
+    PICO_ERR_EHOSTDOWN = 112,
+    PICO_ERR_EHOSTUNREACH = 113,
+};
+
+typedef enum pico_err_e pico_err_t;
+extern volatile pico_err_t pico_err;
+
+#define IS_IPV6(f) (f && f->net_hdr && ((((uint8_t *)(f->net_hdr))[0] & 0xf0) == 0x60))
+#define IS_IPV4(f) (f && f->net_hdr && ((((uint8_t *)(f->net_hdr))[0] & 0xf0) == 0x40))
+
+#define MAX_PROTOCOL_NAME 16
+
+struct pico_protocol {
+    char name[MAX_PROTOCOL_NAME];
+    uint32_t hash;
+    enum pico_layer layer;
+    uint16_t proto_number;
+    struct pico_queue *q_in;
+    struct pico_queue *q_out;
+    struct pico_frame *(*alloc)(struct pico_protocol *self, uint16_t size); /* Frame allocation. */
+    int (*push)(struct pico_protocol *self, struct pico_frame *p);    /* Push function, for active outgoing pkts from above */
+    int (*process_out)(struct pico_protocol *self, struct pico_frame *p);  /* Send loop. */
+    int (*process_in)(struct pico_protocol *self, struct pico_frame *p);  /* Recv loop. */
+    uint16_t (*get_mtu)(struct pico_protocol *self);
+};
+
+int pico_protocols_loop(int loop_score);
+void pico_protocol_init(struct pico_protocol *p);
+
+int pico_protocol_datalink_loop(int loop_score, int direction);
+int pico_protocol_network_loop(int loop_score, int direction);
+int pico_protocol_transport_loop(int loop_score, int direction);
+int pico_protocol_socket_loop(int loop_score, int direction);
+
+#endif
diff --git a/net/picotcp/include/pico_queue.h b/net/picotcp/include/pico_queue.h
new file mode 100644
index 0000000..6cbb979
--- /dev/null
+++ b/net/picotcp/include/pico_queue.h
@@ -0,0 +1,166 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+ *********************************************************************/
+#ifndef INCLUDE_PICO_QUEUE
+#define INCLUDE_PICO_QUEUE
+#include "pico_config.h"
+#include "pico_frame.h"
+
+#define Q_LIMIT 0
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+void *pico_mutex_init(void);
+void pico_mutex_deinit(void *mutex);
+void pico_mutex_lock(void *mutex);
+int pico_mutex_lock_timeout(void *mutex, int timeout);
+void pico_mutex_unlock(void *mutex);
+void pico_mutex_unlock_ISR(void *mutex);
+
+struct pico_queue {
+    uint32_t frames;
+    uint32_t size;
+    uint32_t max_frames;
+    uint32_t max_size;
+    struct pico_frame *head;
+    struct pico_frame *tail;
+#ifdef PICO_SUPPORT_MUTEX
+    void *mutex;
+#endif
+    uint8_t shared;
+    uint16_t overhead;
+};
+
+#ifdef PICO_SUPPORT_MUTEX
+#define PICOTCP_MUTEX_LOCK(x) { \
+        if (x == NULL) \
+            x = pico_mutex_init(); \
+        pico_mutex_lock(x); \
+}
+#define PICOTCP_MUTEX_UNLOCK(x) pico_mutex_unlock(x)
+#define PICOTCP_MUTEX_DEL(x) pico_mutex_deinit(x)
+
+#else
+#define PICOTCP_MUTEX_LOCK(x) do {} while(0)
+#define PICOTCP_MUTEX_UNLOCK(x) do {} while(0)
+#define PICOTCP_MUTEX_DEL(x) do {} while(0)
+#endif
+
+#ifdef PICO_SUPPORT_DEBUG_TOOLS
+static void debug_q(struct pico_queue *q)
+{
+    struct pico_frame *p = q->head;
+    dbg("%d: ", q->frames);
+    while(p) {
+        dbg("(%p)-->", p);
+        p = p->next;
+    }
+    dbg("X\n");
+}
+
+#else
+
+#define debug_q(x) do {} while(0)
+#endif
+
+static inline int32_t pico_enqueue(struct pico_queue *q, struct pico_frame *p)
+{
+    if ((q->max_frames) && (q->max_frames <= q->frames))
+        return -1;
+
+#if (Q_LIMIT != 0)
+    if ((Q_LIMIT < p->buffer_len + q->size))
+        return -1;
+
+#endif
+
+    if ((q->max_size) && (q->max_size < (p->buffer_len + q->size)))
+        return -1;
+
+    if (q->shared)
+        PICOTCP_MUTEX_LOCK(q->mutex);
+
+    p->next = NULL;
+    if (!q->head) {
+        q->head = p;
+        q->tail = p;
+        q->size = 0;
+        q->frames = 0;
+    } else {
+        q->tail->next = p;
+        q->tail = p;
+    }
+
+    q->size += p->buffer_len + q->overhead;
+    q->frames++;
+    debug_q(q);
+
+    if (q->shared)
+        PICOTCP_MUTEX_UNLOCK(q->mutex);
+
+    return (int32_t)q->size;
+}
+
+static inline struct pico_frame *pico_dequeue(struct pico_queue *q)
+{
+    struct pico_frame *p = q->head;
+    if (!p)
+        return NULL;
+
+    if (q->frames < 1)
+        return NULL;
+
+    if (q->shared)
+        PICOTCP_MUTEX_LOCK(q->mutex);
+
+    q->head = p->next;
+    q->frames--;
+    q->size -= p->buffer_len - q->overhead;
+    if (q->head == NULL)
+        q->tail = NULL;
+
+    debug_q(q);
+
+    p->next = NULL;
+    if (q->shared)
+        PICOTCP_MUTEX_UNLOCK(q->mutex);
+
+    return p;
+}
+
+static inline struct pico_frame *pico_queue_peek(struct pico_queue *q)
+{
+    struct pico_frame *p = q->head;
+    if (q->frames < 1)
+        return NULL;
+
+    debug_q(q);
+    return p;
+}
+
+static inline void pico_queue_deinit(struct pico_queue *q)
+{
+    if (q->shared) {
+        PICOTCP_MUTEX_DEL(q->mutex);
+    }
+}
+
+static inline void pico_queue_empty(struct pico_queue *q)
+{
+    struct pico_frame *p = pico_dequeue(q);
+    while(p) {
+        pico_frame_discard(p);
+        p = pico_dequeue(q);
+    }
+}
+
+static inline void pico_queue_protect(struct pico_queue *q)
+{
+    q->shared = 1;
+}
+
+#endif
diff --git a/net/picotcp/include/pico_socket.h b/net/picotcp/include/pico_socket.h
new file mode 100644
index 0000000..a076654
--- /dev/null
+++ b/net/picotcp/include/pico_socket.h
@@ -0,0 +1,268 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+ *********************************************************************/
+#ifndef INCLUDE_PICO_SOCKET
+#define INCLUDE_PICO_SOCKET
+#include "pico_queue.h"
+#include "pico_addressing.h"
+#include "pico_config.h"
+#include "pico_protocol.h"
+#include "pico_tree.h"
+
+#ifdef __linux__
+    #define PICO_DEFAULT_SOCKETQ (16 * 1024) /* Linux host, so we want full throttle */
+#else
+    #define PICO_DEFAULT_SOCKETQ (6 * 1024) /* seems like an acceptable default for small embedded systems */
+#endif
+
+#define PICO_SHUT_RD   1
+#define PICO_SHUT_WR   2
+#define PICO_SHUT_RDWR 3
+
+#ifdef PICO_SUPPORT_IPV4
+# define IS_SOCK_IPV4(s) ((s->net == &pico_proto_ipv4))
+#else
+# define IS_SOCK_IPV4(s) (0)
+#endif
+
+#ifdef PICO_SUPPORT_IPV6
+# define IS_SOCK_IPV6(s) ((s->net == &pico_proto_ipv6))
+#else
+# define IS_SOCK_IPV6(s) (0)
+#endif
+
+
+struct pico_sockport
+{
+    struct pico_tree socks; /* how you make the connection ? */
+    uint16_t number;
+    uint16_t proto;
+};
+
+
+struct pico_socket {
+    struct pico_protocol *proto;
+    struct pico_protocol *net;
+
+    union pico_address local_addr;
+    union pico_address remote_addr;
+
+    uint16_t local_port;
+    uint16_t remote_port;
+
+    struct pico_queue q_in;
+    struct pico_queue q_out;
+
+    void (*wakeup)(uint16_t ev, struct pico_socket *s);
+
+
+#ifdef PICO_SUPPORT_TCP
+    /* For the TCP backlog queue */
+    struct pico_socket *backlog;
+    struct pico_socket *next;
+    struct pico_socket *parent;
+    uint16_t max_backlog;
+    uint16_t number_of_pending_conn;
+#endif
+#ifdef PICO_SUPPORT_MCAST
+    struct pico_tree *MCASTListen;
+#endif
+    uint16_t ev_pending;
+
+    struct pico_device *dev;
+
+    /* Private field. */
+    int id;
+    uint16_t state;
+    uint16_t opt_flags;
+    pico_time timestamp;
+    void *priv;
+};
+
+struct pico_remote_endpoint {
+    union pico_address remote_addr;
+    uint16_t remote_port;
+};
+
+
+/* request struct for multicast socket opt */
+struct pico_ip_mreq {
+    struct pico_ip4 mcast_group_addr;
+    struct pico_ip4 mcast_link_addr;
+};
+
+struct pico_ip_mreq_source {
+    struct pico_ip4 mcast_group_addr;
+    struct pico_ip4 mcast_source_addr;
+    struct pico_ip4 mcast_link_addr;
+};
+
+#ifdef PICO_SUPPORT_IPV6
+
+/* same as above, but ipv6 */
+struct pico_ipv6_mreq {
+    struct pico_ip6 mcast_group_addr;
+    struct pico_ip6 mcast_link_addr;
+};
+
+struct pico_ipv6_mreq_source {
+    struct pico_ip6 mcast_group_addr;
+    struct pico_ip6 mcast_source_addr;
+    struct pico_ip6 mcast_link_addr;
+};
+
+#endif
+
+#define PICO_SOCKET_STATE_UNDEFINED       0x0000u
+#define PICO_SOCKET_STATE_SHUT_LOCAL      0x0001u
+#define PICO_SOCKET_STATE_SHUT_REMOTE     0x0002u
+#define PICO_SOCKET_STATE_BOUND           0x0004u
+#define PICO_SOCKET_STATE_CONNECTED       0x0008u
+#define PICO_SOCKET_STATE_CLOSING         0x0010u
+#define PICO_SOCKET_STATE_CLOSED          0x0020u
+
+# define PICO_SOCKET_STATE_TCP                0xFF00u
+# define PICO_SOCKET_STATE_TCP_UNDEF          0x00FFu
+# define PICO_SOCKET_STATE_TCP_CLOSED         0x0100u
+# define PICO_SOCKET_STATE_TCP_LISTEN         0x0200u
+# define PICO_SOCKET_STATE_TCP_SYN_SENT       0x0300u
+# define PICO_SOCKET_STATE_TCP_SYN_RECV       0x0400u
+# define PICO_SOCKET_STATE_TCP_ESTABLISHED    0x0500u
+# define PICO_SOCKET_STATE_TCP_CLOSE_WAIT     0x0600u
+# define PICO_SOCKET_STATE_TCP_LAST_ACK       0x0700u
+# define PICO_SOCKET_STATE_TCP_FIN_WAIT1      0x0800u
+# define PICO_SOCKET_STATE_TCP_FIN_WAIT2      0x0900u
+# define PICO_SOCKET_STATE_TCP_CLOSING        0x0a00u
+# define PICO_SOCKET_STATE_TCP_TIME_WAIT      0x0b00u
+# define PICO_SOCKET_STATE_TCP_ARRAYSIZ       0x0cu
+
+
+/* Socket options */
+# define PICO_TCP_NODELAY                     1
+# define PICO_SOCKET_OPT_TCPNODELAY           0x0000u
+
+# define PICO_IP_MULTICAST_EXCLUDE            0
+# define PICO_IP_MULTICAST_INCLUDE            1
+# define PICO_IP_MULTICAST_IF                 32
+# define PICO_IP_MULTICAST_TTL                33
+# define PICO_IP_MULTICAST_LOOP               34
+# define PICO_IP_ADD_MEMBERSHIP               35
+# define PICO_IP_DROP_MEMBERSHIP              36
+# define PICO_IP_UNBLOCK_SOURCE               37
+# define PICO_IP_BLOCK_SOURCE                 38
+# define PICO_IP_ADD_SOURCE_MEMBERSHIP        39
+# define PICO_IP_DROP_SOURCE_MEMBERSHIP       40
+
+# define PICO_SOCKET_OPT_MULTICAST_LOOP       1
+
+# define PICO_SOCKET_OPT_RCVBUF               52
+# define PICO_SOCKET_OPT_SNDBUF               53
+
+/* Constants */
+# define PICO_IP_DEFAULT_MULTICAST_TTL        1
+# define PICO_IP_DEFAULT_MULTICAST_LOOP       1
+
+#define PICO_SOCKET_TIMEOUT                   90000u /* 90 seconds */
+#define PICO_SOCKET_BOUND_TIMEOUT             30000u /* 30 seconds */
+
+#define PICO_SOCKET_SHUTDOWN_WRITE 0x01u
+#define PICO_SOCKET_SHUTDOWN_READ  0x02u
+#define TCPSTATE(s) ((s)->state & PICO_SOCKET_STATE_TCP)
+
+#define PICO_SOCK_EV_RD 1u
+#define PICO_SOCK_EV_WR 2u
+#define PICO_SOCK_EV_CONN 4u
+#define PICO_SOCK_EV_CLOSE 8u
+#define PICO_SOCK_EV_FIN 0x10u
+#define PICO_SOCK_EV_ERR 0x80u
+
+struct pico_msginfo {
+    struct pico_device *dev;
+    uint8_t ttl;
+    uint8_t tos;
+};
+
+struct pico_socket *pico_socket_open(uint16_t net, uint16_t proto, void (*wakeup)(uint16_t ev, struct pico_socket *s));
+
+int pico_socket_read(struct pico_socket *s, void *buf, int len);
+int pico_socket_write(struct pico_socket *s, const void *buf, int len);
+
+int pico_socket_sendto(struct pico_socket *s, const void *buf, int len, void *dst, uint16_t remote_port);
+int pico_socket_sendto_extended(struct pico_socket *s, const void *buf, const int len,
+                                void *dst, uint16_t remote_port, struct pico_msginfo *msginfo);
+
+int pico_socket_recvfrom(struct pico_socket *s, void *buf, int len, void *orig, uint16_t *local_port);
+int pico_socket_recvfrom_extended(struct pico_socket *s, void *buf, int len, void *orig,
+                                  uint16_t *remote_port, struct pico_msginfo *msginfo);
+
+int pico_socket_send(struct pico_socket *s, const void *buf, int len);
+int pico_socket_recv(struct pico_socket *s, void *buf, int len);
+
+int pico_socket_bind(struct pico_socket *s, void *local_addr, uint16_t *port);
+int pico_socket_getname(struct pico_socket *s, void *local_addr, uint16_t *port, uint16_t *proto);
+int pico_socket_getpeername(struct pico_socket *s, void *remote_addr, uint16_t *port, uint16_t *proto);
+
+int pico_socket_connect(struct pico_socket *s, const void *srv_addr, uint16_t remote_port);
+int pico_socket_listen(struct pico_socket *s, const int backlog);
+struct pico_socket *pico_socket_accept(struct pico_socket *s, void *orig, uint16_t *port);
+int8_t pico_socket_del(struct pico_socket *s);
+
+int pico_socket_setoption(struct pico_socket *s, int option, void *value);
+int pico_socket_getoption(struct pico_socket *s, int option, void *value);
+
+int pico_socket_shutdown(struct pico_socket *s, int mode);
+int pico_socket_close(struct pico_socket *s);
+
+struct pico_frame *pico_socket_frame_alloc(struct pico_socket *s, uint16_t len);
+
+#ifdef PICO_SUPPORT_IPV4
+# define is_sock_ipv4(x) (x->net == &pico_proto_ipv4)
+#else
+# define is_sock_ipv4(x) (0)
+#endif
+
+#ifdef PICO_SUPPORT_IPV6
+# define is_sock_ipv6(x) (x->net == &pico_proto_ipv6)
+#else
+# define is_sock_ipv6(x) (0)
+#endif
+
+#ifdef PICO_SUPPORT_UDP
+# define is_sock_udp(x) (x->proto == &pico_proto_udp)
+#else
+# define is_sock_udp(x) (0)
+#endif
+
+#ifdef PICO_SUPPORT_TCP
+# define is_sock_tcp(x) (x->proto == &pico_proto_tcp)
+#else
+# define is_sock_tcp(x) (0)
+#endif
+
+/* Interface towards transport protocol */
+int pico_transport_process_in(struct pico_protocol *self, struct pico_frame *f);
+struct pico_socket *pico_socket_clone(struct pico_socket *facsimile);
+int8_t pico_socket_add(struct pico_socket *s);
+int pico_transport_error(struct pico_frame *f, uint8_t proto, int code);
+
+/* Socket loop */
+int pico_sockets_loop(int loop_score);
+struct pico_socket*pico_sockets_find(uint16_t local, uint16_t remote);
+/* Port check */
+int pico_is_port_free(uint16_t proto, uint16_t port, void *addr, void *net);
+
+struct pico_sockport *pico_get_sockport(uint16_t proto, uint16_t port);
+
+uint32_t pico_socket_get_mss(struct pico_socket *s);
+int pico_socket_set_family(struct pico_socket *s, uint16_t family);
+
+int pico_count_sockets(uint8_t proto);
+
+#define PICO_SOCKET_SETOPT_EN(socket, index)  (socket->opt_flags |=  (1 << index))
+#define PICO_SOCKET_SETOPT_DIS(socket, index) (socket->opt_flags &= (uint16_t) ~(1 << index))
+#define PICO_SOCKET_GETOPT(socket, index) ((socket->opt_flags & (1u << index)) != 0)
+
+
+#endif
diff --git a/net/picotcp/include/pico_socket_multicast.h b/net/picotcp/include/pico_socket_multicast.h
new file mode 100644
index 0000000..44c30c5
--- /dev/null
+++ b/net/picotcp/include/pico_socket_multicast.h
@@ -0,0 +1,10 @@
+#ifndef PICO_SOCKET_MULTICAST_H
+#define PICO_SOCKET_MULTICAST_H
+int pico_socket_mcast_filter(struct pico_socket *s, union pico_address *mcast_group, union pico_address *src);
+void pico_multicast_delete(struct pico_socket *s);
+int pico_setsockopt_mcast(struct pico_socket *s, int option, void *value);
+int pico_getsockopt_mcast(struct pico_socket *s, int option, void *value);
+int pico_udp_get_mc_ttl(struct pico_socket *s, uint8_t *ttl);
+int pico_udp_set_mc_ttl(struct pico_socket *s, void *_ttl);
+
+#endif
diff --git a/net/picotcp/include/pico_stack.h b/net/picotcp/include/pico_stack.h
new file mode 100644
index 0000000..69b0bf1
--- /dev/null
+++ b/net/picotcp/include/pico_stack.h
@@ -0,0 +1,87 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+ *********************************************************************/
+#ifndef INCLUDE_PICO_STACK
+#define INCLUDE_PICO_STACK
+#include "pico_config.h"
+#include "pico_frame.h"
+
+#define PICO_MAX_TIMERS 20
+
+#define PICO_ETH_MRU (1514u)
+#define PICO_IP_MRU (1500u)
+
+/* ===== RECEIVING FUNCTIONS (from dev up to socket) ===== */
+
+/* TRANSPORT LEVEL */
+/* interface towards network */
+int32_t pico_transport_receive(struct pico_frame *f, uint8_t proto);
+
+/* NETWORK LEVEL */
+/* interface towards ethernet */
+int32_t pico_network_receive(struct pico_frame *f);
+
+
+/* LOWEST LEVEL: interface towards devices. */
+/* Device driver will call this function which returns immediately.
+ * Incoming packet will be processed later on in the dev loop.
+ * The zerocopy version will associate the current buffer to the newly created frame.
+ * Warning: the buffer used in the zerocopy version MUST have been allocated using PICO_ZALLOC()
+ */
+int32_t pico_stack_recv(struct pico_device *dev, uint8_t *buffer, uint32_t len);
+int32_t pico_stack_recv_zerocopy(struct pico_device *dev, uint8_t *buffer, uint32_t len);
+int32_t pico_stack_recv_zerocopy_ext_buffer(struct pico_device *dev, uint8_t *buffer, uint32_t len);
+int32_t pico_stack_recv_zerocopy_ext_buffer_notify(struct pico_device *dev, uint8_t *buffer, uint32_t len, void (*notify_free)(uint8_t *buffer));
+
+/* ===== SENDING FUNCTIONS (from socket down to dev) ===== */
+
+int32_t pico_network_send(struct pico_frame *f);
+int32_t pico_sendto_dev(struct pico_frame *f);
+
+#ifdef PICO_SUPPORT_ETH
+int32_t pico_ethernet_send(struct pico_frame *f);
+
+/* The pico_ethernet_receive() function is used by
+ * those devices supporting ETH in order to push packets up
+ * into the stack.
+ */
+/* DATALINK LEVEL */
+int32_t pico_ethernet_receive(struct pico_frame *f);
+#else
+/* When ETH is not supported by the stack... */
+#   define pico_ethernet_send(f)    (-1)
+#   define pico_ethernet_receive(f) (-1)
+#endif
+
+/* ----- Initialization ----- */
+int pico_stack_init(void);
+
+/* ----- Loop Function. ----- */
+void pico_stack_tick(void);
+void pico_stack_loop(void);
+
+/* ---- Notifications for stack errors */
+int pico_notify_socket_unreachable(struct pico_frame *f);
+int pico_notify_proto_unreachable(struct pico_frame *f);
+int pico_notify_dest_unreachable(struct pico_frame *f);
+int pico_notify_ttl_expired(struct pico_frame *f);
+int pico_notify_frag_expired(struct pico_frame *f);
+int pico_notify_pkt_too_big(struct pico_frame *f);
+
+/* Various. */
+struct pico_timer;
+int pico_source_is_local(struct pico_frame *f);
+int pico_frame_dst_is_unicast(struct pico_frame *f);
+void pico_store_network_origin(void *src, struct pico_frame *f);
+struct pico_timer *pico_timer_add(pico_time expire, void (*timer)(pico_time, void *), void *arg);
+void pico_timer_cancel(struct pico_timer *t);
+pico_time pico_timer_get_expire(struct pico_timer *t);
+uint32_t pico_rand(void);
+void pico_rand_feed(uint32_t feed);
+void pico_to_lowercase(char *str);
+int pico_address_compare(union pico_address *a, union pico_address *b, uint16_t proto);
+int32_t pico_seq_compare(uint32_t a, uint32_t b);
+
+#endif
diff --git a/net/picotcp/include/pico_tree.h b/net/picotcp/include/pico_tree.h
new file mode 100644
index 0000000..8667a1f
--- /dev/null
+++ b/net/picotcp/include/pico_tree.h
@@ -0,0 +1,93 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   Author: Andrei Carp <andrei.carp at tass.be>
+ *********************************************************************/
+
+#ifndef PICO_RBTREE_H
+#define PICO_RBTREE_H
+
+#include "pico_config.h"
+
+/* This is used to declare a new tree, leaf root by default */
+#define PICO_TREE_DECLARE(name, compareFunction) \
+    struct pico_tree name = \
+    { \
+        &LEAF, \
+        compareFunction \
+    }
+
+#define USE_PICO_PAGE0_ZALLOC (1)
+#define USE_PICO_ZALLOC (2)
+
+struct pico_tree_node
+{
+    void*keyValue; /* generic key */
+    struct pico_tree_node*parent;
+    struct pico_tree_node*leftChild;
+    struct pico_tree_node*rightChild;
+    uint8_t color;
+};
+
+struct pico_tree
+{
+    struct pico_tree_node *root;  /* root of the tree */
+
+    /* this function directly provides the keys as parameters not the nodes. */
+    int (*compare)(void*keyA, void*keyB);
+};
+
+extern struct pico_tree_node LEAF; /* generic leaf node */
+
+#ifdef PICO_SUPPORT_MM
+void *pico_tree_insert_implementation(struct pico_tree *tree, void *key, uint8_t allocator);
+void *pico_tree_delete_implementation(struct pico_tree *tree, void *key, uint8_t allocator);
+#endif
+
+
+/*
+ * Manipulation functions
+ */
+void *pico_tree_insert(struct pico_tree *tree, void *key);
+void *pico_tree_delete(struct pico_tree *tree, void *key);
+void *pico_tree_findKey(struct pico_tree *tree, void *key);
+void    pico_tree_drop(struct pico_tree *tree);
+int     pico_tree_empty(struct pico_tree *tree);
+struct pico_tree_node *pico_tree_findNode(struct pico_tree *tree, void *key);
+
+void *pico_tree_first(struct pico_tree *tree);
+void *pico_tree_last(struct pico_tree *tree);
+/*
+ * Traverse functions
+ */
+struct pico_tree_node *pico_tree_lastNode(struct pico_tree_node *node);
+struct pico_tree_node *pico_tree_firstNode(struct pico_tree_node *node);
+struct pico_tree_node *pico_tree_next(struct pico_tree_node *node);
+struct pico_tree_node *pico_tree_prev(struct pico_tree_node *node);
+
+/*
+ * For each macros
+ */
+
+#define pico_tree_foreach(idx, tree) \
+    for ((idx) = pico_tree_firstNode((tree)->root); \
+         (idx) != &LEAF; \
+         (idx) = pico_tree_next(idx))
+
+#define pico_tree_foreach_reverse(idx, tree) \
+    for ((idx) = pico_tree_lastNode((tree)->root); \
+         (idx) != &LEAF; \
+         (idx) = pico_tree_prev(idx))
+
+#define pico_tree_foreach_safe(idx, tree, idx2) \
+    for ((idx) = pico_tree_firstNode((tree)->root); \
+         ((idx) != &LEAF) && ((idx2) = pico_tree_next(idx), 1); \
+         (idx) = (idx2))
+
+#define pico_tree_foreach_reverse_safe(idx, tree, idx2) \
+    for ((idx) = pico_tree_lastNode((tree)->root); \
+         ((idx) != &LEAF) && ((idx2) = pico_tree_prev(idx), 1); \
+         (idx) = (idx2))
+
+#endif
diff --git a/net/picotcp/modules/pico_aodv.h b/net/picotcp/modules/pico_aodv.h
new file mode 100644
index 0000000..a3e9b66
--- /dev/null
+++ b/net/picotcp/modules/pico_aodv.h
@@ -0,0 +1,131 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   .
+
+   Author: Daniele Lacamera <daniele.lacamera at altran.com>
+ *********************************************************************/
+#ifndef PICO_AODV_H_
+#define PICO_AODV_H_
+#include <stdint.h>
+
+/* RFC3561 */
+#define PICO_AODV_PORT (654)
+
+/* RFC3561 $10 */
+#define AODV_ACTIVE_ROUTE_TIMEOUT     (8000u) /* Conservative value for link breakage detection */
+#define AODV_DELETE_PERIOD            (5 * AODV_ACTIVE_ROUTE_TIMEOUT) /* Recommended value K = 5 */
+#define AODV_ALLOWED_HELLO_LOSS       (4) /* conservative */
+#define AODV_NET_DIAMETER             ((uint8_t)(35))
+#define AODV_RREQ_RETRIES             (2)
+#define AODV_NODE_TRAVERSAL_TIME      (40)
+#define AODV_HELLO_INTERVAL           (1000)
+#define AODV_LOCAL_ADD_TTL            2
+#define AODV_RREQ_RATELIMIT           (10)
+#define AODV_TIMEOUT_BUFFER           (2)
+#define AODV_TTL_START                ((uint8_t)(1))
+#define AODV_TTL_INCREMENT            2
+#define AODV_TTL_THRESHOLD            ((uint8_t)(7))
+#define AODV_RERR_RATELIMIT           (10)
+#define AODV_MAX_REPAIR_TTL           ((uint8_t)(AODV_NET_DIAMETER / 3))
+#define AODV_MY_ROUTE_TIMEOUT         (2 * AODV_ACTIVE_ROUTE_TIMEOUT)
+#define AODV_NET_TRAVERSAL_TIME       (2 * AODV_NODE_TRAVERSAL_TIME * AODV_NET_DIAMETER)
+#define AODV_BLACKLIST_TIMEOUT        (AODV_RREQ_RETRIES * AODV_NET_TRAVERSAL_TIME)
+#define AODV_NEXT_HOP_WAIT            (AODV_NODE_TRAVERSAL_TIME + 10)
+#define AODV_PATH_DISCOVERY_TIME      (2 * AODV_NET_TRAVERSAL_TIME)
+#define AODV_RING_TRAVERSAL_TIME(ttl)   (2 * AODV_NODE_TRAVERSAL_TIME * (ttl + AODV_TIMEOUT_BUFFER))
+/* End section RFC3561 $10 */
+
+
+#define AODV_TYPE_RREQ 1
+#define AODV_TYPE_RREP 2
+#define AODV_TYPE_RERR 3
+#define AODV_TYPE_RACK 4
+
+PACKED_STRUCT_DEF pico_aodv_rreq
+{
+    uint8_t type;
+    uint16_t req_flags;
+    uint8_t hop_count;
+    uint32_t rreq_id;
+    uint32_t dest;
+    uint32_t dseq;
+    uint32_t orig;
+    uint32_t oseq;
+};
+
+#define AODV_RREQ_FLAG_J 0x8000
+#define AODV_RREQ_FLAG_R 0x4000
+#define AODV_RREQ_FLAG_G 0x2000
+#define AODV_RREQ_FLAG_D 0x1000
+#define AODV_RREQ_FLAG_U 0x0800
+#define AODV_RREQ_FLAG_RESERVED 0x07FF
+
+PACKED_STRUCT_DEF pico_aodv_rrep
+{
+    uint8_t type;
+    uint8_t rep_flags;
+    uint8_t prefix_sz;
+    uint8_t hop_count;
+    uint32_t dest;
+    uint32_t dseq;
+    uint32_t orig;
+    uint32_t lifetime;
+};
+
+#define AODV_RREP_MAX_PREFIX 0x1F
+#define AODV_RREP_FLAG_R 0x80
+#define AODV_RREP_FLAG_A 0x40
+#define AODV_RREP_FLAG_RESERVED 0x3F
+
+#define PICO_AODV_NODE_NEW          0x0000
+#define PICO_AODV_NODE_SYNC         0x0001
+#define PICO_AODV_NODE_REQUESTING   0x0002
+#define PICO_AODV_NODE_ROUTE_UP     0x0004
+#define PICO_AODV_NODE_ROUTE_DOWN   0x0008
+#define PICO_AODV_NODE_IDLING       0x0010
+#define PICO_AODV_NODE_UNREACH      0x0020
+
+#define PICO_AODV_ACTIVE(node) ((node->flags & PICO_AODV_NODE_ROUTE_UP) && (node->flags & PICO_AODV_NODE_ROUTE_DOWN))
+
+
+struct pico_aodv_node
+{
+    union pico_address dest;
+    pico_time last_seen;
+    pico_time fwd_time;
+    uint32_t dseq;
+    uint16_t flags;
+    uint8_t metric;
+    uint8_t ring_ttl;
+    uint8_t rreq_retry;
+};
+
+PACKED_STRUCT_DEF pico_aodv_unreachable
+{
+    uint32_t addr;
+    uint32_t dseq;
+};
+
+PACKED_STRUCT_DEF pico_aodv_rerr
+{
+    uint8_t type;
+    uint16_t rerr_flags;
+    uint8_t dst_count;
+    uint32_t unreach_addr;
+    uint32_t unreach_dseq;
+    struct pico_aodv_unreachable unreach[1]; /* unrechable nodes: must be at least 1. See dst_count field above */
+};
+
+PACKED_STRUCT_DEF pico_aodv_rack
+{
+    uint8_t type;
+    uint8_t reserved;
+};
+
+int pico_aodv_init(void);
+int pico_aodv_add(struct pico_device *dev);
+int pico_aodv_lookup(const union pico_address *addr);
+void pico_aodv_refresh(const union pico_address *addr);
+#endif
diff --git a/net/picotcp/modules/pico_arp.c b/net/picotcp/modules/pico_arp.c
new file mode 100644
index 0000000..f205bfe
--- /dev/null
+++ b/net/picotcp/modules/pico_arp.c
@@ -0,0 +1,531 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   .
+
+   Authors: Daniele Lacamera
+ *********************************************************************/
+
+
+#include "pico_config.h"
+#include "pico_arp.h"
+#include "pico_tree.h"
+#include "pico_ipv4.h"
+#include "pico_device.h"
+#include "pico_stack.h"
+
+extern const uint8_t PICO_ETHADDR_ALL[6];
+#define PICO_ARP_TIMEOUT 600000llu
+#define PICO_ARP_RETRY 300lu
+#define PICO_ARP_MAX_PENDING 5
+
+#ifdef DEBUG_ARP
+    #define arp_dbg dbg
+#else
+    #define arp_dbg(...) do {} while(0)
+#endif
+
+static int max_arp_reqs = PICO_ARP_MAX_RATE;
+static struct pico_frame *frames_queued[PICO_ARP_MAX_PENDING] = { };
+
+static void pico_arp_queued_trigger(void)
+{
+    int i;
+    struct pico_frame *f;
+    for (i = 0; i < PICO_ARP_MAX_PENDING; i++)
+    {
+        f = frames_queued[i];
+        if (f) {
+            pico_ethernet_send(f);
+            frames_queued[i] = NULL;
+        }
+    }
+}
+
+static void update_max_arp_reqs(pico_time now, void *unused)
+{
+    IGNORE_PARAMETER(now);
+    IGNORE_PARAMETER(unused);
+    if (max_arp_reqs < PICO_ARP_MAX_RATE)
+        max_arp_reqs++;
+
+    pico_timer_add(PICO_ARP_INTERVAL / PICO_ARP_MAX_RATE, &update_max_arp_reqs, NULL);
+}
+
+void pico_arp_init(void)
+{
+    pico_timer_add(PICO_ARP_INTERVAL / PICO_ARP_MAX_RATE, &update_max_arp_reqs, NULL);
+}
+
+PACKED_STRUCT_DEF pico_arp_hdr
+{
+    uint16_t htype;
+    uint16_t ptype;
+    uint8_t hsize;
+    uint8_t psize;
+    uint16_t opcode;
+    uint8_t s_mac[PICO_SIZE_ETH];
+    struct pico_ip4 src;
+    uint8_t d_mac[PICO_SIZE_ETH];
+    struct pico_ip4 dst;
+};
+
+
+
+/* Callback handler for ip conflict service (e.g. IPv4 SLAAC)
+ *  Whenever the IP address registered here is seen in the network,
+ *  the callback is awaken to take countermeasures against IP collisions.
+ *
+ */
+
+struct arp_service_ipconflict {
+    struct pico_eth mac;
+    struct pico_ip4 ip;
+    void (*conflict)(void);
+};
+
+static struct arp_service_ipconflict conflict_ipv4;
+
+
+
+#define PICO_SIZE_ARPHDR ((sizeof(struct pico_arp_hdr)))
+
+/* Arp Entries for the tables. */
+struct pico_arp {
+/* CAREFUL MAN! ARP entry MUST begin with a pico_eth structure,
+ * due to in-place casting!!! */
+    struct pico_eth eth;
+    struct pico_ip4 ipv4;
+    int arp_status;
+    pico_time timestamp;
+    struct pico_device *dev;
+    struct pico_timer *timer;
+};
+
+
+
+/*****************/
+/**  ARP TREE **/
+/*****************/
+
+/* Routing destination */
+
+static int arp_compare(void *ka, void *kb)
+{
+    struct pico_arp *a = ka, *b = kb;
+    return pico_ipv4_compare(&a->ipv4, &b->ipv4);
+}
+
+PICO_TREE_DECLARE(arp_tree, arp_compare);
+
+/*********************/
+/**  END ARP TREE **/
+/*********************/
+
+struct pico_eth *pico_arp_lookup(struct pico_ip4 *dst)
+{
+    struct pico_arp search, *found;
+    search.ipv4.addr = dst->addr;
+    found = pico_tree_findKey(&arp_tree, &search);
+    if (found && (found->arp_status != PICO_ARP_STATUS_STALE))
+        return &found->eth;
+
+    return NULL;
+}
+
+struct pico_ip4 *pico_arp_reverse_lookup(struct pico_eth *dst)
+{
+    struct pico_arp*search;
+    struct pico_tree_node *index;
+    pico_tree_foreach(index, &arp_tree){
+        search = index->keyValue;
+        if(memcmp(&(search->eth.addr), &dst->addr, 6) == 0)
+            return &search->ipv4;
+    }
+    return NULL;
+}
+
+static void pico_arp_unreachable(struct pico_ip4 *a)
+{
+    int i;
+    struct pico_frame *f;
+    struct pico_ipv4_hdr *hdr;
+    struct pico_ip4 dst;
+    for (i = 0; i < PICO_ARP_MAX_PENDING; i++)
+    {
+        f = frames_queued[i];
+        if (f) {
+            hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+            dst = pico_ipv4_route_get_gateway(&hdr->dst);
+            if (!dst.addr)
+                dst.addr = hdr->dst.addr;
+
+            if (dst.addr ==  a->addr) {
+                if (!pico_source_is_local(f)) {
+                    pico_notify_dest_unreachable(f);
+                }
+
+                pico_frame_discard(f);
+                frames_queued[i] = NULL;
+            }
+        }
+    }
+}
+
+static void pico_arp_retry(struct pico_frame *f, struct pico_ip4 *where)
+{
+    if (++f->failure_count < 4) {
+        arp_dbg ("================= ARP REQUIRED: %d =============\n\n", f->failure_count);
+        /* check if dst is local (gateway = 0), or if to use gateway */
+        pico_arp_request(f->dev, where, PICO_ARP_QUERY);
+    } else {
+        pico_arp_unreachable(where);
+    }
+}
+
+struct pico_eth *pico_arp_get(struct pico_frame *f)
+{
+    struct pico_eth *a4;
+    struct pico_ip4 gateway;
+    struct pico_ip4 *where;
+    struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+    struct pico_ipv4_link *l;
+    if (!hdr)
+        return NULL;
+
+    l = pico_ipv4_link_get(&hdr->dst);
+    if(l) {
+        /* address belongs to ourself */
+        return &l->dev->eth->mac;
+    }
+
+    gateway = pico_ipv4_route_get_gateway(&hdr->dst);
+    /* check if dst is local (gateway = 0), or if to use gateway */
+    if (gateway.addr != 0)
+        where = &gateway;
+    else
+        where = &hdr->dst;
+
+    a4 = pico_arp_lookup(where);      /* check if dst ip mac in cache */
+
+    if (!a4)
+        pico_arp_retry(f, where);
+
+    return a4;
+}
+
+
+void pico_arp_postpone(struct pico_frame *f)
+{
+    int i;
+    for (i = 0; i < PICO_ARP_MAX_PENDING; i++)
+    {
+        if (!frames_queued[i]) {
+            frames_queued[i] = pico_frame_copy(f);
+            return;
+        }
+    }
+    /* Not possible to enqueue: caller will discard packet */
+}
+
+
+#ifdef DEBUG_ARP
+void dbg_arp(void)
+{
+    struct pico_arp *a;
+    struct pico_tree_node *index;
+
+    pico_tree_foreach(index, &arp_tree) {
+        a = index->keyValue;
+        arp_dbg("ARP to  %08x, mac: %02x:%02x:%02x:%02x:%02x:%02x\n", a->ipv4.addr, a->eth.addr[0], a->eth.addr[1], a->eth.addr[2], a->eth.addr[3], a->eth.addr[4], a->eth.addr[5] );
+    }
+}
+#endif
+
+static void arp_expire(pico_time now, void *_stale)
+{
+    struct pico_arp *stale = (struct pico_arp *) _stale;
+    if (now >= (stale->timestamp + PICO_ARP_TIMEOUT)) {
+        stale->arp_status = PICO_ARP_STATUS_STALE;
+        arp_dbg("ARP: Setting arp_status to STALE\n");
+        pico_arp_request(stale->dev, &stale->ipv4, PICO_ARP_QUERY);
+    } else {
+        /* Timer must be rescheduled, ARP entry has been renewed lately.
+         * No action required to refresh the entry, will check on the next timeout */
+        pico_timer_add(PICO_ARP_TIMEOUT + stale->timestamp - now, arp_expire, stale);
+    }
+}
+
+static void pico_arp_add_entry(struct pico_arp *entry)
+{
+    entry->arp_status = PICO_ARP_STATUS_REACHABLE;
+    entry->timestamp  = PICO_TIME();
+
+    pico_tree_insert(&arp_tree, entry);
+    arp_dbg("ARP ## reachable.\n");
+    pico_arp_queued_trigger();
+    pico_timer_add(PICO_ARP_TIMEOUT, arp_expire, entry);
+}
+
+int pico_arp_create_entry(uint8_t *hwaddr, struct pico_ip4 ipv4, struct pico_device *dev)
+{
+    struct pico_arp*arp = PICO_ZALLOC(sizeof(struct pico_arp));
+    if(!arp) {
+        pico_err = PICO_ERR_ENOMEM;
+        return -1;
+    }
+
+    memcpy(arp->eth.addr, hwaddr, 6);
+    arp->ipv4.addr = ipv4.addr;
+    arp->dev = dev;
+
+    pico_arp_add_entry(arp);
+
+    return 0;
+}
+
+static void pico_arp_check_conflict(struct pico_arp_hdr *hdr)
+{
+
+    if ((conflict_ipv4.conflict) &&
+        ((conflict_ipv4.ip.addr == hdr->src.addr) &&
+         (memcmp(hdr->s_mac, conflict_ipv4.mac.addr, PICO_SIZE_ETH) != 0)))
+        conflict_ipv4.conflict();
+}
+
+static struct pico_arp *pico_arp_lookup_entry(struct pico_frame *f)
+{
+    struct pico_arp search;
+    struct pico_arp *found = NULL;
+    struct pico_arp_hdr *hdr = (struct pico_arp_hdr *) f->net_hdr;
+    /* Populate a new arp entry */
+    search.ipv4.addr = hdr->src.addr;
+
+    /* Search for already existing entry */
+    found = pico_tree_findKey(&arp_tree, &search);
+    if (found) {
+        if (found->arp_status == PICO_ARP_STATUS_STALE) {
+            /* Replace if stale */
+            pico_tree_delete(&arp_tree, found);
+            pico_arp_add_entry(found);
+        } else {
+            /* Update mac address */
+            memcpy(found->eth.addr, hdr->s_mac, PICO_SIZE_ETH);
+            arp_dbg("ARP entry updated!\n");
+
+            /* Refresh timestamp, this will force a reschedule on the next timeout*/
+            found->timestamp = PICO_TIME();
+        }
+    }
+
+    return found;
+}
+
+
+static int pico_arp_check_incoming_hdr_type(struct pico_arp_hdr *h)
+{
+    /* Check the hardware type and protocol */
+    if ((h->htype != PICO_ARP_HTYPE_ETH) || (h->ptype != PICO_IDETH_IPV4))
+        return -1;
+
+    return 0;
+}
+
+static int pico_arp_check_incoming_hdr(struct pico_frame *f, struct pico_ip4 *dst_addr)
+{
+    struct pico_arp_hdr *hdr = (struct pico_arp_hdr *) f->net_hdr;
+    if (!hdr)
+        return -1;
+
+    dst_addr->addr = hdr->dst.addr;
+    if (pico_arp_check_incoming_hdr_type(hdr) < 0)
+        return -1;
+
+    /* The source mac address must not be a multicast or broadcast address */
+    if (hdr->s_mac[0] & 0x01)
+        return -1;
+
+    return 0;
+}
+
+static void pico_arp_reply_on_request(struct pico_frame *f, struct pico_ip4 me)
+{
+    struct pico_arp_hdr *hdr;
+    struct pico_eth_hdr *eh;
+
+    hdr = (struct pico_arp_hdr *) f->net_hdr;
+    eh = (struct pico_eth_hdr *)f->datalink_hdr;
+    if (hdr->opcode != PICO_ARP_REQUEST)
+        return;
+
+    hdr->opcode = PICO_ARP_REPLY;
+    memcpy(hdr->d_mac, hdr->s_mac, PICO_SIZE_ETH);
+    memcpy(hdr->s_mac, f->dev->eth->mac.addr, PICO_SIZE_ETH);
+    hdr->dst.addr = hdr->src.addr;
+    hdr->src.addr = me.addr;
+
+    /* Prepare eth header for arp reply */
+    memcpy(eh->daddr, eh->saddr, PICO_SIZE_ETH);
+    memcpy(eh->saddr, f->dev->eth->mac.addr, PICO_SIZE_ETH);
+    f->start = f->datalink_hdr;
+    f->len = PICO_SIZE_ETHHDR + PICO_SIZE_ARPHDR;
+    f->dev->send(f->dev, f->start, (int)f->len);
+}
+
+static int pico_arp_check_flooding(struct pico_frame *f, struct pico_ip4 me)
+{
+    struct pico_device *link_dev;
+    struct pico_arp_hdr *hdr;
+    hdr = (struct pico_arp_hdr *) f->net_hdr;
+
+    /* Prevent ARP flooding */
+    link_dev = pico_ipv4_link_find(&me);
+    if ((link_dev == f->dev) && (hdr->opcode == PICO_ARP_REQUEST)) {
+        if (max_arp_reqs == 0)
+            return -1;
+        else
+            max_arp_reqs--;
+    }
+
+    /* Check if we are the target IP address */
+    if (link_dev != f->dev)
+        return -1;
+
+    return 0;
+}
+
+static int pico_arp_process_in(struct pico_frame *f, struct pico_arp_hdr *hdr, struct pico_arp *found)
+{
+    struct pico_ip4 me;
+    if (pico_arp_check_incoming_hdr(f, &me) < 0) {
+        pico_frame_discard(f);
+        return -1;
+    }
+
+    if (pico_arp_check_flooding(f, me) < 0) {
+        pico_frame_discard(f);
+        return -1;
+    }
+
+    /* If no existing entry was found, create a new entry, or fail trying. */
+    if ((!found) && (pico_arp_create_entry(hdr->s_mac, hdr->src, f->dev) < 0)) {
+        pico_frame_discard(f);
+        return -1;
+    }
+
+    /* If the packet is a request, send a reply */
+    pico_arp_reply_on_request(f, me);
+
+#ifdef DEBUG_ARP
+    dbg_arp();
+#endif
+    pico_frame_discard(f);
+    return 0;
+}
+
+int pico_arp_receive(struct pico_frame *f)
+{
+    struct pico_arp_hdr *hdr;
+    struct pico_arp *found = NULL;
+
+    hdr = (struct pico_arp_hdr *) f->net_hdr;
+    if (!hdr)
+        return -1;
+
+    pico_arp_check_conflict(hdr);
+    found = pico_arp_lookup_entry(f);
+    return pico_arp_process_in(f, hdr, found);
+
+}
+
+static int32_t pico_arp_request_xmit(struct pico_device *dev, struct pico_frame *f, struct pico_ip4 *src, struct pico_ip4 *dst, uint8_t type)
+{
+    struct pico_arp_hdr *ah = (struct pico_arp_hdr *) (f->start + PICO_SIZE_ETHHDR);
+    int ret;
+
+    /* Fill arp header */
+    ah->htype  = PICO_ARP_HTYPE_ETH;
+    ah->ptype  = PICO_IDETH_IPV4;
+    ah->hsize  = PICO_SIZE_ETH;
+    ah->psize  = PICO_SIZE_IP4;
+    ah->opcode = PICO_ARP_REQUEST;
+    memcpy(ah->s_mac, dev->eth->mac.addr, PICO_SIZE_ETH);
+
+    switch (type) {
+    case PICO_ARP_ANNOUNCE:
+        ah->src.addr = dst->addr;
+        ah->dst.addr = dst->addr;
+        break;
+    case PICO_ARP_PROBE:
+        ah->src.addr = 0;
+        ah->dst.addr = dst->addr;
+        break;
+    case PICO_ARP_QUERY:
+        ah->src.addr = src->addr;
+        ah->dst.addr = dst->addr;
+        break;
+    default:
+        pico_frame_discard(f);
+        return -1;
+    }
+    arp_dbg("Sending arp request.\n");
+    ret = dev->send(dev, f->start, (int) f->len);
+    pico_frame_discard(f);
+    return ret;
+}
+
+int32_t pico_arp_request(struct pico_device *dev, struct pico_ip4 *dst, uint8_t type)
+{
+    struct pico_frame *q = pico_frame_alloc(PICO_SIZE_ETHHDR + PICO_SIZE_ARPHDR);
+    struct pico_eth_hdr *eh;
+    struct pico_ip4 *src = NULL;
+
+    if (!q)
+        return -1;
+
+    if (type == PICO_ARP_QUERY)
+    {
+        src = pico_ipv4_source_find(dst);
+        if (!src) {
+            pico_frame_discard(q);
+            return -1;
+        }
+    }
+
+    arp_dbg("QUERY: %08x\n", dst->addr);
+
+    eh = (struct pico_eth_hdr *)q->start;
+
+    /* Fill eth header */
+    memcpy(eh->saddr, dev->eth->mac.addr, PICO_SIZE_ETH);
+    memcpy(eh->daddr, PICO_ETHADDR_ALL, PICO_SIZE_ETH);
+    eh->proto = PICO_IDETH_ARP;
+
+    return pico_arp_request_xmit(dev, q, src, dst, type);
+}
+
+int pico_arp_get_neighbors(struct pico_device *dev, struct pico_ip4 *neighbors, int maxlen)
+{
+    struct pico_arp*search;
+    struct pico_tree_node *index;
+    int i = 0;
+    pico_tree_foreach(index, &arp_tree){
+        search = index->keyValue;
+        if (search->dev == dev) {
+            neighbors[i++].addr = search->ipv4.addr;
+            if (i >= maxlen)
+                return i;
+        }
+    }
+    return i;
+}
+
+void pico_arp_register_ipconflict(struct pico_ip4 *ip, struct pico_eth *mac, void (*cb)(void))
+{
+    conflict_ipv4.conflict = cb;
+    conflict_ipv4.ip.addr = ip->addr;
+    if (mac != NULL)
+        memcpy(conflict_ipv4.mac.addr, mac, 6);
+}
+
diff --git a/net/picotcp/modules/pico_arp.h b/net/picotcp/modules/pico_arp.h
new file mode 100644
index 0000000..18e1f69
--- /dev/null
+++ b/net/picotcp/modules/pico_arp.h
@@ -0,0 +1,32 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+ *********************************************************************/
+#ifndef INCLUDE_PICO_ARP
+#define INCLUDE_PICO_ARP
+#include "pico_eth.h"
+#include "pico_device.h"
+
+int pico_arp_receive(struct pico_frame *);
+
+
+struct pico_eth *pico_arp_get(struct pico_frame *f);
+int32_t pico_arp_request(struct pico_device *dev, struct pico_ip4 *dst, uint8_t type);
+
+#define PICO_ARP_STATUS_REACHABLE 0x00
+#define PICO_ARP_STATUS_PERMANENT 0x01
+#define PICO_ARP_STATUS_STALE     0x02
+
+#define PICO_ARP_QUERY    0x00
+#define PICO_ARP_PROBE    0x01
+#define PICO_ARP_ANNOUNCE 0x02
+
+struct pico_eth *pico_arp_lookup(struct pico_ip4 *dst);
+struct pico_ip4 *pico_arp_reverse_lookup(struct pico_eth *dst);
+int pico_arp_create_entry(uint8_t*hwaddr, struct pico_ip4 ipv4, struct pico_device*dev);
+int pico_arp_get_neighbors(struct pico_device *dev, struct pico_ip4 *neighbors, int maxlen);
+void pico_arp_register_ipconflict(struct pico_ip4 *ip, struct pico_eth *mac, void (*cb)(void));
+void pico_arp_postpone(struct pico_frame *f);
+void pico_arp_init(void);
+#endif
diff --git a/net/picotcp/modules/pico_dev_loop.c b/net/picotcp/modules/pico_dev_loop.c
new file mode 100644
index 0000000..4416d0b
--- /dev/null
+++ b/net/picotcp/modules/pico_dev_loop.c
@@ -0,0 +1,66 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   Authors: Daniele Lacamera
+ *********************************************************************/
+
+
+#include "pico_device.h"
+#include "pico_dev_loop.h"
+#include "pico_stack.h"
+
+
+#define LOOP_MTU 1500
+static uint8_t l_buf[LOOP_MTU];
+static int l_bufsize = 0;
+
+
+static int pico_loop_send(struct pico_device *dev, void *buf, int len)
+{
+    IGNORE_PARAMETER(dev);
+    if (len > LOOP_MTU)
+        return 0;
+
+    if (l_bufsize == 0) {
+        memcpy(l_buf, buf, (size_t)len);
+        l_bufsize += len;
+        return len;
+    }
+
+    return 0;
+}
+
+static int pico_loop_poll(struct pico_device *dev, int loop_score)
+{
+    if (loop_score <= 0)
+        return 0;
+
+    if (l_bufsize > 0) {
+        pico_stack_recv(dev, l_buf, (uint32_t)l_bufsize);
+        l_bufsize = 0;
+        loop_score--;
+    }
+
+    return loop_score;
+}
+
+
+struct pico_device *pico_loop_create(void)
+{
+    struct pico_device *loop = PICO_ZALLOC(sizeof(struct pico_device));
+    if (!loop)
+        return NULL;
+
+    if( 0 != pico_device_init(loop, "loop", NULL)) {
+        dbg ("Loop init failed.\n");
+        pico_device_destroy(loop);
+        return NULL;
+    }
+
+    loop->send = pico_loop_send;
+    loop->poll = pico_loop_poll;
+    dbg("Device %s created.\n", loop->name);
+    return loop;
+}
+
diff --git a/net/picotcp/modules/pico_dev_loop.h b/net/picotcp/modules/pico_dev_loop.h
new file mode 100644
index 0000000..6cb8de1
--- /dev/null
+++ b/net/picotcp/modules/pico_dev_loop.h
@@ -0,0 +1,15 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+ *********************************************************************/
+#ifndef INCLUDE_PICO_LOOP
+#define INCLUDE_PICO_LOOP
+#include "pico_config.h"
+#include "pico_device.h"
+
+void pico_loop_destroy(struct pico_device *loop);
+struct pico_device *pico_loop_create(void);
+
+#endif
+
diff --git a/net/picotcp/modules/pico_dev_mock.h b/net/picotcp/modules/pico_dev_mock.h
new file mode 100644
index 0000000..dfc2cfc
--- /dev/null
+++ b/net/picotcp/modules/pico_dev_mock.h
@@ -0,0 +1,47 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+ *********************************************************************/
+#ifndef INCLUDE_PICO_MOCK
+#define INCLUDE_PICO_MOCK
+#include "pico_config.h"
+#include "pico_device.h"
+
+
+struct mock_frame {
+    uint8_t*buffer;
+    int len;
+    int read;
+
+    struct mock_frame*next;
+};
+
+struct mock_device {
+    struct pico_device*dev;
+    struct mock_frame*in_head;
+    struct mock_frame*in_tail;
+    struct mock_frame*out_head;
+    struct mock_frame*out_tail;
+
+    uint8_t*mac;
+
+};
+
+struct mock_device;
+/* A mockup-device for the purpose of testing. It provides a couple of extra "network"-functions, which represent the network-side of the device. A network_send will result in mock_poll reading something, a network_read will see if the stack has sent anything through our mock-device. */
+void pico_mock_destroy(struct pico_device *dev);
+struct mock_device *pico_mock_create(uint8_t*mac);
+
+int pico_mock_network_read(struct mock_device*mock, void *buf, int len);
+int pico_mock_network_write(struct mock_device*mock, const void *buf, int len);
+
+/* TODO */
+/* we could use a few checking functions, e.g. one to see if it's a valid IP packet, if it's TCP, if the IP-address matches,... */
+/* That would be useful to avoid having to manually create buffers of what you expect, probably with masks for things that are random,... */
+uint32_t mock_get_sender_ip4(struct mock_device*mock, void*buf, int len);
+
+int mock_ip_protocol(struct mock_device*mock, void*buf, int len);
+int mock_icmp_type(struct mock_device*mock, void*buf, int len);
+int mock_icmp_code(struct mock_device*mock, void*buf, int len);
+#endif
diff --git a/net/picotcp/modules/pico_dev_null.c b/net/picotcp/modules/pico_dev_null.c
new file mode 100644
index 0000000..5fed494
--- /dev/null
+++ b/net/picotcp/modules/pico_dev_null.c
@@ -0,0 +1,60 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   Authors: Daniele Lacamera
+ *********************************************************************/
+
+
+#include "pico_device.h"
+#include "pico_dev_null.h"
+#include "pico_stack.h"
+
+struct pico_device_null {
+    struct pico_device dev;
+    int statistics_frames_out;
+};
+
+#define NULL_MTU 0
+
+static int pico_null_send(struct pico_device *dev, void *buf, int len)
+{
+    struct pico_device_null *null = (struct pico_device_null *) dev;
+    IGNORE_PARAMETER(buf);
+
+    /* Increase the statistic count */
+    null->statistics_frames_out++;
+
+    /* Discard the frame content silently. */
+    return len;
+}
+
+static int pico_null_poll(struct pico_device *dev, int loop_score)
+{
+    /* We never have packet to receive, no score is used. */
+    IGNORE_PARAMETER(dev);
+    return loop_score;
+}
+
+/* Public interface: create/destroy. */
+
+
+struct pico_device *pico_null_create(char *name)
+{
+    struct pico_device_null *null = PICO_ZALLOC(sizeof(struct pico_device_null));
+
+    if (!null)
+        return NULL;
+
+    if( 0 != pico_device_init((struct pico_device *)null, name, NULL)) {
+        return NULL;
+    }
+
+    null->dev.overhead = 0;
+    null->statistics_frames_out = 0;
+    null->dev.send = pico_null_send;
+    null->dev.poll = pico_null_poll;
+    dbg("Device %s created.\n", null->dev.name);
+    return (struct pico_device *)null;
+}
+
diff --git a/net/picotcp/modules/pico_dev_null.h b/net/picotcp/modules/pico_dev_null.h
new file mode 100644
index 0000000..a0eb98e
--- /dev/null
+++ b/net/picotcp/modules/pico_dev_null.h
@@ -0,0 +1,15 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+ *********************************************************************/
+#ifndef INCLUDE_PICO_NULL
+#define INCLUDE_PICO_NULL
+#include "pico_config.h"
+#include "pico_device.h"
+
+void pico_null_destroy(struct pico_device *null);
+struct pico_device *pico_null_create(char *name);
+
+#endif
+
diff --git a/net/picotcp/modules/pico_dhcp_client.c b/net/picotcp/modules/pico_dhcp_client.c
new file mode 100644
index 0000000..7f8df7f
--- /dev/null
+++ b/net/picotcp/modules/pico_dhcp_client.c
@@ -0,0 +1,960 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   Authors: Kristof Roelants, Frederik Van Slycken
+ *********************************************************************/
+
+
+#include "pico_dhcp_client.h"
+#include "pico_stack.h"
+#include "pico_config.h"
+#include "pico_device.h"
+#include "pico_ipv4.h"
+#include "pico_socket.h"
+#include "pico_eth.h"
+
+#if (defined PICO_SUPPORT_DHCPC && defined PICO_SUPPORT_UDP)
+#define dhcpc_dbg(...) do {} while(0)
+/* #define dhcpc_dbg dbg */
+
+/* timer values */
+#define DHCP_CLIENT_REINIT             6000 /* msec */
+#define DHCP_CLIENT_RETRANS            4 /* sec */
+#define DHCP_CLIENT_RETRIES            3
+
+#define DHCP_CLIENT_TIMER_STOPPED      0
+#define DHCP_CLIENT_TIMER_STARTED      1
+
+/* maximum size of a DHCP message */
+#define DHCP_CLIENT_MAXMSGZISE         (PICO_IP_MRU - PICO_SIZE_IP4HDR)
+
+enum dhcp_client_state {
+    DHCP_CLIENT_STATE_INIT_REBOOT = 0,
+    DHCP_CLIENT_STATE_REBOOTING,
+    DHCP_CLIENT_STATE_INIT,
+    DHCP_CLIENT_STATE_SELECTING,
+    DHCP_CLIENT_STATE_REQUESTING,
+    DHCP_CLIENT_STATE_BOUND,
+    DHCP_CLIENT_STATE_RENEWING,
+    DHCP_CLIENT_STATE_REBINDING
+};
+
+
+#define PICO_DHCPC_TIMER_INIT    0
+#define PICO_DHCPC_TIMER_REQUEST 1
+#define PICO_DHCPC_TIMER_RENEW   2
+#define PICO_DHCPC_TIMER_REBIND  3
+#define PICO_DHCPC_TIMER_T1      4
+#define PICO_DHCPC_TIMER_T2      5
+#define PICO_DHCPC_TIMER_LEASE   6
+#define PICO_DHCPC_TIMER_ARRAY_SIZE 7
+
+struct dhcp_client_timer
+{
+    uint8_t state;
+    unsigned int type;
+    uint32_t xid;
+};
+
+struct pico_dhcp_client_cookie
+{
+    uint8_t event;
+    uint8_t retry;
+    uint32_t xid;
+    uint32_t *uid;
+    enum dhcp_client_state state;
+    void (*cb)(void*dhcpc, int code);
+    pico_time init_timestamp;
+    struct pico_socket *s;
+    struct pico_ip4 address;
+    struct pico_ip4 netmask;
+    struct pico_ip4 gateway;
+    struct pico_ip4 nameserver[2];
+    struct pico_ip4 server_id;
+    struct pico_device *dev;
+    struct dhcp_client_timer *timer[PICO_DHCPC_TIMER_ARRAY_SIZE];
+    uint32_t t1_time;
+    uint32_t t2_time;
+    uint32_t lease_time;
+    uint32_t renew_time;
+    uint32_t rebind_time;
+};
+
+static int pico_dhcp_client_init(struct pico_dhcp_client_cookie *dhcpc);
+static int reset(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
+static int8_t pico_dhcp_client_msg(struct pico_dhcp_client_cookie *dhcpc, uint8_t msg_type);
+static void pico_dhcp_client_wakeup(uint16_t ev, struct pico_socket *s);
+static void pico_dhcp_state_machine(uint8_t event, struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
+
+static const struct pico_ip4 bcast = {
+    .addr = 0xFFFFFFFF
+};
+
+static const struct pico_ip4 bcast_netmask = {
+    .addr = 0xFFFFFFFF
+};
+
+static struct pico_ip4 inaddr_any = {
+    0
+};
+
+
+static int dhcp_cookies_cmp(void *ka, void *kb)
+{
+    struct pico_dhcp_client_cookie *a = ka, *b = kb;
+    if (a->xid == b->xid)
+        return 0;
+
+    return (a->xid < b->xid) ? (-1) : (1);
+}
+PICO_TREE_DECLARE(DHCPCookies, dhcp_cookies_cmp);
+
+static struct pico_dhcp_client_cookie *pico_dhcp_client_add_cookie(uint32_t xid, struct pico_device *dev, void (*cb)(void *dhcpc, int code), uint32_t *uid)
+{
+    struct pico_dhcp_client_cookie *dhcpc = NULL, *found = NULL, test = {
+        0
+    };
+
+    test.xid = xid;
+    found = pico_tree_findKey(&DHCPCookies, &test);
+    if (found) {
+        pico_err = PICO_ERR_EAGAIN;
+        return NULL;
+    }
+
+    dhcpc = PICO_ZALLOC(sizeof(struct pico_dhcp_client_cookie));
+    if (!dhcpc) {
+        pico_err = PICO_ERR_ENOMEM;
+        return NULL;
+    }
+
+    dhcpc->state = DHCP_CLIENT_STATE_INIT;
+    dhcpc->xid = xid;
+    dhcpc->uid = uid;
+    *(dhcpc->uid) = 0;
+    dhcpc->cb = cb;
+    dhcpc->dev = dev;
+
+    pico_tree_insert(&DHCPCookies, dhcpc);
+    return dhcpc;
+}
+
+static void pico_dhcp_client_stop_timers(struct pico_dhcp_client_cookie *dhcpc);
+static int pico_dhcp_client_del_cookie(uint32_t xid)
+{
+    struct pico_dhcp_client_cookie test = {
+        0
+    }, *found = NULL;
+
+    test.xid = xid;
+    found = pico_tree_findKey(&DHCPCookies, &test);
+    if (!found)
+        return -1;
+
+    pico_dhcp_client_stop_timers(found);
+    pico_socket_close(found->s);
+    found->s = NULL;
+    pico_ipv4_link_del(found->dev, found->address);
+    pico_tree_delete(&DHCPCookies, found);
+    PICO_FREE(found);
+    return 0;
+}
+
+static struct pico_dhcp_client_cookie *pico_dhcp_client_find_cookie(uint32_t xid)
+{
+    struct pico_dhcp_client_cookie test = {
+        0
+    }, *found = NULL;
+
+    test.xid = xid;
+    found = pico_tree_findKey(&DHCPCookies, &test);
+    if (found)
+        return found;
+    else
+        return NULL;
+}
+
+static void pico_dhcp_client_timer_handler(pico_time now, void *arg);
+static void pico_dhcp_client_reinit(pico_time now, void *arg);
+static struct dhcp_client_timer *pico_dhcp_timer_add(uint8_t type, uint32_t time, struct pico_dhcp_client_cookie *ck)
+{
+    struct dhcp_client_timer *t;
+
+    t = PICO_ZALLOC(sizeof(struct dhcp_client_timer));
+    if (!t)
+        return NULL;
+
+    t->state = DHCP_CLIENT_TIMER_STARTED;
+    t->xid = ck->xid;
+    t->type = type;
+    pico_timer_add(time, pico_dhcp_client_timer_handler, t);
+    if (ck->timer[type]) {
+        ck->timer[type]->state = DHCP_CLIENT_TIMER_STOPPED;
+    }
+
+    ck->timer[type] = t;
+    return t;
+}
+
+static int dhcp_get_timer_event(struct pico_dhcp_client_cookie *dhcpc, unsigned int type)
+{
+    const int events[PICO_DHCPC_TIMER_ARRAY_SIZE] =
+    {
+        PICO_DHCP_EVENT_RETRANSMIT,
+        PICO_DHCP_EVENT_RETRANSMIT,
+        PICO_DHCP_EVENT_RETRANSMIT,
+        PICO_DHCP_EVENT_RETRANSMIT,
+        PICO_DHCP_EVENT_T1,
+        PICO_DHCP_EVENT_T2,
+        PICO_DHCP_EVENT_LEASE
+    };
+
+    if (type == PICO_DHCPC_TIMER_REQUEST) {
+        if (++dhcpc->retry > DHCP_CLIENT_RETRIES) {
+            reset(dhcpc, NULL);
+            return PICO_DHCP_EVENT_NONE;
+        }
+    } else if (type < PICO_DHCPC_TIMER_T1) {
+        dhcpc->retry++;
+    }
+
+    return events[type];
+}
+
+static void pico_dhcp_client_timer_handler(pico_time now, void *arg)
+{
+    struct dhcp_client_timer *t = (struct dhcp_client_timer *)arg;
+    struct pico_dhcp_client_cookie *dhcpc;
+
+    if (!t)
+        return;
+
+    (void) now;
+    if (t->state != DHCP_CLIENT_TIMER_STOPPED) {
+        dhcpc = pico_dhcp_client_find_cookie(t->xid);
+        if (dhcpc && dhcpc->timer) {
+            t->state = DHCP_CLIENT_TIMER_STOPPED;
+            if ((t->type == PICO_DHCPC_TIMER_INIT) && (dhcpc->state < DHCP_CLIENT_STATE_SELECTING)) {
+                pico_dhcp_client_reinit(now, dhcpc);
+            } else if (t->type != PICO_DHCPC_TIMER_INIT) {
+                dhcpc->event = (uint8_t)dhcp_get_timer_event(dhcpc, t->type);
+                if (dhcpc->event != PICO_DHCP_EVENT_NONE)
+                    pico_dhcp_state_machine(dhcpc->event, dhcpc, NULL);
+            }
+        }
+    }
+}
+
+static void pico_dhcp_client_timer_stop(struct pico_dhcp_client_cookie *dhcpc, int type)
+{
+    if (dhcpc->timer[type]) {
+        dhcpc->timer[type]->state = DHCP_CLIENT_TIMER_STOPPED;
+    }
+
+}
+
+
+static void pico_dhcp_client_reinit(pico_time now, void *arg)
+{
+    struct pico_dhcp_client_cookie *dhcpc = (struct pico_dhcp_client_cookie *)arg;
+    (void) now;
+
+    if (dhcpc->s) {
+        pico_socket_close(dhcpc->s);
+        dhcpc->s = NULL;
+    }
+
+    if (++dhcpc->retry > DHCP_CLIENT_RETRIES) {
+        pico_err = PICO_ERR_EAGAIN;
+        if (dhcpc->cb)
+            dhcpc->cb(dhcpc, PICO_DHCP_ERROR);
+
+        pico_dhcp_client_del_cookie(dhcpc->xid);
+        return;
+    }
+
+    pico_dhcp_client_init(dhcpc);
+    return;
+}
+
+
+static void pico_dhcp_client_stop_timers(struct pico_dhcp_client_cookie *dhcpc)
+{
+    int i;
+    dhcpc->retry = 0;
+    for (i = 0; i < PICO_DHCPC_TIMER_ARRAY_SIZE; i++)
+        pico_dhcp_client_timer_stop(dhcpc, i);
+}
+
+static void pico_dhcp_client_start_init_timer(struct pico_dhcp_client_cookie *dhcpc)
+{
+    uint32_t time = 0;
+    /* timer value is doubled with every retry (exponential backoff) */
+    time = (uint32_t) (DHCP_CLIENT_RETRANS << dhcpc->retry);
+    pico_dhcp_timer_add(PICO_DHCPC_TIMER_INIT, time * 1000, dhcpc);
+}
+
+static void pico_dhcp_client_start_requesting_timer(struct pico_dhcp_client_cookie *dhcpc)
+{
+    uint32_t time = 0;
+
+    /* timer value is doubled with every retry (exponential backoff) */
+    time = (uint32_t)(DHCP_CLIENT_RETRANS << dhcpc->retry);
+    pico_dhcp_timer_add(PICO_DHCPC_TIMER_REQUEST, time * 1000, dhcpc);
+}
+
+static void pico_dhcp_client_start_renewing_timer(struct pico_dhcp_client_cookie *dhcpc)
+{
+    uint32_t halftime = 0;
+
+    /* wait one-half of the remaining time until T2, down to a minimum of 60 seconds */
+    /* (dhcpc->retry + 1): initial -> divide by 2, 1st retry -> divide by 4, 2nd retry -> divide by 8, etc */
+    pico_dhcp_client_stop_timers(dhcpc);
+    halftime = dhcpc->renew_time >> (dhcpc->retry + 1);
+    if (halftime < 60)
+        halftime = 60;
+
+    pico_dhcp_timer_add(PICO_DHCPC_TIMER_RENEW, halftime * 1000, dhcpc);
+
+    return;
+}
+
+static void pico_dhcp_client_start_rebinding_timer(struct pico_dhcp_client_cookie *dhcpc)
+{
+    uint32_t halftime = 0;
+
+    pico_dhcp_client_stop_timers(dhcpc);
+    halftime = dhcpc->rebind_time >> (dhcpc->retry + 1);
+    if (halftime < 60)
+        halftime = 60;
+
+    pico_dhcp_timer_add(PICO_DHCPC_TIMER_REBIND, halftime * 1000, dhcpc);
+
+    return;
+}
+
+static void pico_dhcp_client_start_reacquisition_timers(struct pico_dhcp_client_cookie *dhcpc)
+{
+
+    pico_dhcp_client_stop_timers(dhcpc);
+    pico_dhcp_timer_add(PICO_DHCPC_TIMER_T1, dhcpc->t1_time * 1000, dhcpc);
+    pico_dhcp_timer_add(PICO_DHCPC_TIMER_T2, dhcpc->t2_time * 1000, dhcpc);
+    pico_dhcp_timer_add(PICO_DHCPC_TIMER_LEASE, dhcpc->lease_time * 1000, dhcpc);
+}
+
+static int pico_dhcp_client_init(struct pico_dhcp_client_cookie *dhcpc)
+{
+    uint16_t port = PICO_DHCP_CLIENT_PORT;
+    if (!dhcpc)
+        return -1;
+
+    /* adding a link with address 0.0.0.0 and netmask 0.0.0.0,
+     * automatically adds a route for a global broadcast */
+    pico_ipv4_link_add(dhcpc->dev, inaddr_any, bcast_netmask);
+    if (!dhcpc->s)
+        dhcpc->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcp_client_wakeup);
+
+    if (!dhcpc->s) {
+        pico_dhcp_timer_add(PICO_DHCPC_TIMER_INIT, DHCP_CLIENT_REINIT, dhcpc);
+        return 0;
+    }
+
+    dhcpc->s->dev = dhcpc->dev;
+    if (pico_socket_bind(dhcpc->s, &inaddr_any, &port) < 0) {
+        pico_socket_close(dhcpc->s);
+        dhcpc->s = NULL;
+        pico_dhcp_timer_add(PICO_DHCPC_TIMER_INIT, DHCP_CLIENT_REINIT, dhcpc);
+        return 0;
+    }
+
+    if (pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_DISCOVER) < 0) {
+        pico_socket_close(dhcpc->s);
+        dhcpc->s = NULL;
+        pico_dhcp_timer_add(PICO_DHCPC_TIMER_INIT, DHCP_CLIENT_REINIT, dhcpc);
+        return 0;
+    }
+
+    dhcpc->retry = 0;
+    dhcpc->init_timestamp = PICO_TIME_MS();
+    pico_dhcp_client_start_init_timer(dhcpc);
+    return 0;
+}
+
+int pico_dhcp_initiate_negotiation(struct pico_device *dev, void (*cb)(void *dhcpc, int code), uint32_t *uid)
+{
+    uint8_t retry = 32;
+    uint32_t xid = 0;
+    struct pico_dhcp_client_cookie *dhcpc = NULL;
+
+    if (!dev || !cb || !uid) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    if (!dev->eth) {
+        pico_err = PICO_ERR_EOPNOTSUPP;
+        return -1;
+    }
+
+    /* attempt to generate a correct xid, else fail */
+    do {
+        xid = pico_rand();
+    } while (!xid && --retry);
+
+    if (!xid) {
+        pico_err = PICO_ERR_EAGAIN;
+        return -1;
+    }
+
+    dhcpc = pico_dhcp_client_add_cookie(xid, dev, cb, uid);
+    if (!dhcpc)
+        return -1;
+
+    dhcpc_dbg("DHCP client: cookie with xid %u\n", dhcpc->xid);
+    *uid = xid;
+    return pico_dhcp_client_init(dhcpc);
+}
+
+static void pico_dhcp_client_recv_params(struct pico_dhcp_client_cookie *dhcpc, struct pico_dhcp_opt *opt)
+{
+    do {
+        switch (opt->code)
+        {
+        case PICO_DHCP_OPT_PAD:
+            break;
+
+        case PICO_DHCP_OPT_END:
+            break;
+
+        case PICO_DHCP_OPT_MSGTYPE:
+            dhcpc->event = opt->ext.msg_type.type;
+            dhcpc_dbg("DHCP client: message type %u\n", dhcpc->event);
+            break;
+
+        case PICO_DHCP_OPT_LEASETIME:
+            dhcpc->lease_time = long_be(opt->ext.lease_time.time);
+            dhcpc_dbg("DHCP client: lease time %u\n", dhcpc->lease_time);
+            break;
+
+        case PICO_DHCP_OPT_RENEWALTIME:
+            dhcpc->t1_time = long_be(opt->ext.renewal_time.time);
+            dhcpc_dbg("DHCP client: renewal time %u\n", dhcpc->t1_time);
+            break;
+
+        case PICO_DHCP_OPT_REBINDINGTIME:
+            dhcpc->t2_time = long_be(opt->ext.rebinding_time.time);
+            dhcpc_dbg("DHCP client: rebinding time %u\n", dhcpc->t2_time);
+            break;
+
+        case PICO_DHCP_OPT_ROUTER:
+            dhcpc->gateway = opt->ext.router.ip;
+            dhcpc_dbg("DHCP client: router %08X\n", dhcpc->gateway.addr);
+            break;
+
+        case PICO_DHCP_OPT_DNS:
+            dhcpc->nameserver[0] = opt->ext.dns1.ip;
+            dhcpc_dbg("DHCP client: dns1 %08X\n", dhcpc->nameserver[0].addr);
+            if (opt->len >= 8) {
+                dhcpc->nameserver[1] = opt->ext.dns2.ip;
+                dhcpc_dbg("DHCP client: dns1 %08X\n", dhcpc->nameserver[1].addr);
+            }
+
+            break;
+
+        case PICO_DHCP_OPT_NETMASK:
+            dhcpc->netmask = opt->ext.netmask.ip;
+            dhcpc_dbg("DHCP client: netmask %08X\n", dhcpc->netmask.addr);
+            break;
+
+        case PICO_DHCP_OPT_SERVERID:
+            dhcpc->server_id = opt->ext.server_id.ip;
+            dhcpc_dbg("DHCP client: server ID %08X\n", dhcpc->server_id.addr);
+            break;
+
+        case PICO_DHCP_OPT_OPTOVERLOAD:
+            dhcpc_dbg("DHCP client: WARNING option overload present (not processed)");
+            break;
+
+        default:
+            dhcpc_dbg("DHCP client: WARNING unsupported option %u\n", opt->code);
+            break;
+        }
+    } while (pico_dhcp_next_option(&opt));
+
+    /* default values for T1 and T2 when not provided */
+    if (!dhcpc->t1_time)
+        dhcpc->t1_time = dhcpc->lease_time >> 1;
+
+    if (!dhcpc->t2_time)
+        dhcpc->t2_time = (dhcpc->lease_time * 875) / 1000;
+
+    return;
+}
+
+static int recv_offer(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
+{
+    struct pico_dhcp_hdr *hdr = (struct pico_dhcp_hdr *)buf;
+    struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)hdr->options;
+
+    pico_dhcp_client_recv_params(dhcpc, opt);
+    if ((dhcpc->event != PICO_DHCP_MSG_OFFER) || !dhcpc->server_id.addr || !dhcpc->netmask.addr || !dhcpc->lease_time)
+        return -1;
+
+    dhcpc->address.addr = hdr->yiaddr;
+
+    /* we skip state SELECTING, process first offer received */
+    dhcpc->state = DHCP_CLIENT_STATE_REQUESTING;
+    dhcpc->retry = 0;
+    pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST);
+    pico_dhcp_client_start_requesting_timer(dhcpc);
+    return 0;
+}
+
+static int recv_ack(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
+{
+    struct pico_dhcp_hdr *hdr = (struct pico_dhcp_hdr *)buf;
+    struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)hdr->options;
+    struct pico_ip4 address = {
+        0
+    };
+    struct pico_ip4 any_address = {
+        0
+    };
+
+    struct pico_ipv4_link *l;
+
+    pico_dhcp_client_recv_params(dhcpc, opt);
+    if ((dhcpc->event != PICO_DHCP_MSG_ACK) || !dhcpc->server_id.addr || !dhcpc->netmask.addr || !dhcpc->lease_time)
+        return -1;
+
+    /* Issue #20 the server can transmit on ACK a different IP than the one in OFFER */
+    /* RFC2131 ch 4.3.2 ... The client SHOULD use the parameters in the DHCPACK message for configuration */
+    if (dhcpc->state == DHCP_CLIENT_STATE_REQUESTING)
+        dhcpc->address.addr = hdr->yiaddr;
+
+
+    /* close the socket used for address (re)acquisition */
+    pico_socket_close(dhcpc->s);
+    dhcpc->s = NULL;
+
+    /* Delete all the links before adding the address */
+    pico_ipv4_link_del(dhcpc->dev, address);
+    l = pico_ipv4_link_by_dev(dhcpc->dev);
+    while(l) {
+        pico_ipv4_link_del(dhcpc->dev, l->address);
+        l = pico_ipv4_link_by_dev_next(dhcpc->dev, l);
+    }
+    pico_ipv4_link_add(dhcpc->dev, dhcpc->address, dhcpc->netmask);
+
+    dbg("DHCP client: renewal time (T1) %u\n", (unsigned int)dhcpc->t1_time);
+    dbg("DHCP client: rebinding time (T2) %u\n", (unsigned int)dhcpc->t2_time);
+    dbg("DHCP client: lease time %u\n", (unsigned int)dhcpc->lease_time);
+
+    /* If router option is received, use it as default gateway */
+    if (dhcpc->gateway.addr != 0U) {
+        pico_ipv4_route_add(any_address, any_address, dhcpc->gateway, 1, NULL);
+    }
+
+    dhcpc->retry = 0;
+    dhcpc->renew_time = dhcpc->t2_time - dhcpc->t1_time;
+    dhcpc->rebind_time = dhcpc->lease_time - dhcpc->t2_time;
+    pico_dhcp_client_start_reacquisition_timers(dhcpc);
+
+    *(dhcpc->uid) = dhcpc->xid;
+    if (dhcpc->cb)
+        dhcpc->cb(dhcpc, PICO_DHCP_SUCCESS);
+
+    dhcpc->state = DHCP_CLIENT_STATE_BOUND;
+    return 0;
+}
+
+static int renew(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
+{
+    uint16_t port = PICO_DHCP_CLIENT_PORT;
+    (void) buf;
+    dhcpc->state = DHCP_CLIENT_STATE_RENEWING;
+    dhcpc->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcp_client_wakeup);
+    if (!dhcpc->s) {
+        dhcpc_dbg("DHCP client ERROR: failure opening socket on renew, aborting DHCP! (%s)\n", strerror(pico_err));
+        if (dhcpc->cb)
+            dhcpc->cb(dhcpc, PICO_DHCP_ERROR);
+
+        return -1;
+    }
+
+    if (pico_socket_bind(dhcpc->s, &dhcpc->address, &port) != 0) {
+        dhcpc_dbg("DHCP client ERROR: failure binding socket on renew, aborting DHCP! (%s)\n", strerror(pico_err));
+        pico_socket_close(dhcpc->s);
+        dhcpc->s = NULL;
+        if (dhcpc->cb)
+            dhcpc->cb(dhcpc, PICO_DHCP_ERROR);
+
+        return -1;
+    }
+
+    dhcpc->retry = 0;
+    pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST);
+    pico_dhcp_client_start_renewing_timer(dhcpc);
+
+    return 0;
+}
+
+static int rebind(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
+{
+    (void) buf;
+
+    dhcpc->state = DHCP_CLIENT_STATE_REBINDING;
+    dhcpc->retry = 0;
+    pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST);
+    pico_dhcp_client_start_rebinding_timer(dhcpc);
+
+    return 0;
+}
+
+static int reset(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
+{
+    struct pico_ip4 address = {
+        0
+    };
+    (void) buf;
+
+    if (dhcpc->state == DHCP_CLIENT_STATE_REQUESTING)
+        address.addr = PICO_IP4_ANY;
+    else
+        address.addr = dhcpc->address.addr;
+
+    /* close the socket used for address (re)acquisition */
+    pico_socket_close(dhcpc->s);
+    dhcpc->s = NULL;
+    /* delete the link with the currently in use address */
+    pico_ipv4_link_del(dhcpc->dev, address);
+
+    if (dhcpc->cb)
+        dhcpc->cb(dhcpc, PICO_DHCP_RESET);
+
+    if (dhcpc->state < DHCP_CLIENT_STATE_BOUND)
+    {
+        /* pico_dhcp_client_timer_stop(dhcpc, PICO_DHCPC_TIMER_INIT); */
+    }
+
+
+    dhcpc->state = DHCP_CLIENT_STATE_INIT;
+    pico_dhcp_client_stop_timers(dhcpc);
+    pico_dhcp_client_init(dhcpc);
+    return 0;
+}
+
+static int retransmit(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
+{
+    (void) buf;
+    switch (dhcpc->state)
+    {
+    case DHCP_CLIENT_STATE_INIT:
+        pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_DISCOVER);
+        pico_dhcp_client_start_init_timer(dhcpc);
+        break;
+
+    case DHCP_CLIENT_STATE_REQUESTING:
+        pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST);
+        pico_dhcp_client_start_requesting_timer(dhcpc);
+        break;
+
+    case DHCP_CLIENT_STATE_RENEWING:
+        pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST);
+        pico_dhcp_client_start_renewing_timer(dhcpc);
+        break;
+
+    case DHCP_CLIENT_STATE_REBINDING:
+        pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_DISCOVER);
+        pico_dhcp_client_start_rebinding_timer(dhcpc);
+        break;
+
+    default:
+        dhcpc_dbg("DHCP client WARNING: retransmit in incorrect state (%u)!\n", dhcpc->state);
+        return -1;
+    }
+    return 0;
+}
+
+struct dhcp_action_entry {
+    int (*offer)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
+    int (*ack)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
+    int (*nak)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
+    int (*timer1)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
+    int (*timer2)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
+    int (*timer_lease)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
+    int (*timer_retransmit)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf);
+};
+
+static struct dhcp_action_entry dhcp_fsm[] =
+{ /* event                |offer      |ack      |nak    |T1    |T2     |lease  |retransmit */
+/* state init-reboot */
+    { NULL,       NULL,     NULL,   NULL,  NULL,   NULL,  NULL       },
+/* state rebooting   */ { NULL,       NULL,     NULL,   NULL,  NULL,   NULL,  NULL       },
+/* state init        */ { recv_offer, NULL,     NULL,   NULL,  NULL,   NULL,  retransmit },
+/* state selecting   */ { NULL,       NULL,     NULL,   NULL,  NULL,   NULL,  NULL       },
+/* state requesting  */ { NULL,       recv_ack, reset,  NULL,  NULL,   NULL,  retransmit },
+/* state bound       */ { NULL,       NULL,     NULL,   renew, NULL,   NULL,  NULL       },
+/* state renewing    */ { NULL,       recv_ack, reset,  NULL,  rebind, NULL,  retransmit },
+/* state rebinding   */ { NULL,       recv_ack, reset,  NULL,  NULL,   reset, retransmit },
+};
+
+/* TIMERS REMARK:
+ * In state bound we have T1, T2 and the lease timer running. If T1 goes off, we attempt to renew.
+ * If the renew succeeds a new T1, T2 and lease timer is started. The former T2 and lease timer is
+ * still running though. This poses no concerns as the T2 and lease event in state bound have a NULL
+ * pointer in the fsm. If the former T2 or lease timer goes off, nothing happens. Same situation
+ * applies for T2 and a succesfull rebind. */
+
+static void pico_dhcp_state_machine(uint8_t event, struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
+{
+    switch (event)
+    {
+    case PICO_DHCP_MSG_OFFER:
+        dhcpc_dbg("DHCP client: received OFFER\n");
+        if (dhcp_fsm[dhcpc->state].offer)
+            dhcp_fsm[dhcpc->state].offer(dhcpc, buf);
+
+        break;
+
+    case PICO_DHCP_MSG_ACK:
+        dhcpc_dbg("DHCP client: received ACK\n");
+        if (dhcp_fsm[dhcpc->state].ack)
+            dhcp_fsm[dhcpc->state].ack(dhcpc, buf);
+
+        break;
+
+    case PICO_DHCP_MSG_NAK:
+        dhcpc_dbg("DHCP client: received NAK\n");
+        if (dhcp_fsm[dhcpc->state].nak)
+            dhcp_fsm[dhcpc->state].nak(dhcpc, buf);
+
+        break;
+
+    case PICO_DHCP_EVENT_T1:
+        dhcpc_dbg("DHCP client: received T1 timeout\n");
+        if (dhcp_fsm[dhcpc->state].timer1)
+            dhcp_fsm[dhcpc->state].timer1(dhcpc, NULL);
+
+        break;
+
+    case PICO_DHCP_EVENT_T2:
+        dhcpc_dbg("DHCP client: received T2 timeout\n");
+        if (dhcp_fsm[dhcpc->state].timer2)
+            dhcp_fsm[dhcpc->state].timer2(dhcpc, NULL);
+
+        break;
+
+    case PICO_DHCP_EVENT_LEASE:
+        dhcpc_dbg("DHCP client: received LEASE timeout\n");
+        if (dhcp_fsm[dhcpc->state].timer_lease)
+            dhcp_fsm[dhcpc->state].timer_lease(dhcpc, NULL);
+
+        break;
+
+    case PICO_DHCP_EVENT_RETRANSMIT:
+        dhcpc_dbg("DHCP client: received RETRANSMIT timeout\n");
+        if (dhcp_fsm[dhcpc->state].timer_retransmit)
+            dhcp_fsm[dhcpc->state].timer_retransmit(dhcpc, NULL);
+
+        break;
+
+    default:
+        dhcpc_dbg("DHCP client WARNING: unrecognized event (%u)!\n", dhcpc->event);
+        return;
+    }
+    return;
+}
+
+static int16_t pico_dhcp_client_opt_parse(void *ptr, uint16_t len)
+{
+    uint32_t optlen = len - (uint32_t)sizeof(struct pico_dhcp_hdr);
+    struct pico_dhcp_hdr *hdr = (struct pico_dhcp_hdr *)ptr;
+    struct pico_dhcp_opt *opt = NULL;
+
+    if (hdr->dhcp_magic != PICO_DHCPD_MAGIC_COOKIE)
+        return -1;
+
+    if (!pico_dhcp_are_options_valid(hdr->options, (int32_t)optlen))
+        return -1;
+
+    opt = (struct pico_dhcp_opt *)hdr->options;
+    do {
+        if (opt->code == PICO_DHCP_OPT_MSGTYPE)
+            return opt->ext.msg_type.type;
+    } while (pico_dhcp_next_option(&opt));
+
+    return -1;
+}
+
+static int8_t pico_dhcp_client_msg(struct pico_dhcp_client_cookie *dhcpc, uint8_t msg_type)
+{
+    int32_t r = 0;
+    uint16_t optlen = 0, offset = 0;
+    struct pico_ip4 destination = {
+        .addr = 0xFFFFFFFF
+    };
+    struct pico_dhcp_hdr *hdr = NULL;
+
+
+    /* RFC 2131 3.1.3: Request is always BROADCAST */
+
+    /* Set again default route for the bcast request */
+    pico_ipv4_route_set_bcast_link(pico_ipv4_link_by_dev(dhcpc->dev));
+
+    switch (msg_type)
+    {
+    case PICO_DHCP_MSG_DISCOVER:
+        dhcpc_dbg("DHCP client: sent DHCPDISCOVER\n");
+        optlen = PICO_DHCP_OPTLEN_MSGTYPE + PICO_DHCP_OPTLEN_MAXMSGSIZE + PICO_DHCP_OPTLEN_PARAMLIST + PICO_DHCP_OPTLEN_END;
+        hdr = PICO_ZALLOC((size_t)(sizeof(struct pico_dhcp_hdr) + optlen));
+        if (!hdr) {
+            pico_err = PICO_ERR_ENOMEM;
+            return -1;
+        }
+
+        /* specific options */
+        offset = (uint16_t)(offset + pico_dhcp_opt_maxmsgsize(&hdr->options[offset], DHCP_CLIENT_MAXMSGZISE));
+        break;
+
+    case PICO_DHCP_MSG_REQUEST:
+        optlen = PICO_DHCP_OPTLEN_MSGTYPE + PICO_DHCP_OPTLEN_MAXMSGSIZE + PICO_DHCP_OPTLEN_PARAMLIST + PICO_DHCP_OPTLEN_REQIP + PICO_DHCP_OPTLEN_SERVERID
+                 + PICO_DHCP_OPTLEN_END;
+        hdr = PICO_ZALLOC(sizeof(struct pico_dhcp_hdr) + optlen);
+        if (!hdr) {
+            pico_err = PICO_ERR_ENOMEM;
+            return -1;
+        }
+
+        /* specific options */
+        offset = (uint16_t)(offset + pico_dhcp_opt_maxmsgsize(&hdr->options[offset], DHCP_CLIENT_MAXMSGZISE));
+        if (dhcpc->state == DHCP_CLIENT_STATE_REQUESTING) {
+            offset = (uint16_t)(offset + pico_dhcp_opt_reqip(&hdr->options[offset], &dhcpc->address));
+            offset = (uint16_t)(offset + pico_dhcp_opt_serverid(&hdr->options[offset], &dhcpc->server_id));
+        }
+
+        break;
+
+    default:
+        return -1;
+    }
+
+    /* common options */
+    offset = (uint16_t)(offset + pico_dhcp_opt_msgtype(&hdr->options[offset], msg_type));
+    offset = (uint16_t)(offset + pico_dhcp_opt_paramlist(&hdr->options[offset]));
+    offset = (uint16_t)(offset + pico_dhcp_opt_end(&hdr->options[offset]));
+
+    switch (dhcpc->state)
+    {
+    case DHCP_CLIENT_STATE_BOUND:
+        destination.addr = dhcpc->server_id.addr;
+        hdr->ciaddr = dhcpc->address.addr;
+        break;
+
+    case DHCP_CLIENT_STATE_RENEWING:
+        destination.addr = dhcpc->server_id.addr;
+        hdr->ciaddr = dhcpc->address.addr;
+        break;
+
+    case DHCP_CLIENT_STATE_REBINDING:
+        hdr->ciaddr = dhcpc->address.addr;
+        break;
+
+    default:
+        /* do nothing */
+        break;
+    }
+
+    /* header information */
+    hdr->op = PICO_DHCP_OP_REQUEST;
+    hdr->htype = PICO_DHCP_HTYPE_ETH;
+    hdr->hlen = PICO_SIZE_ETH;
+    hdr->xid = dhcpc->xid;
+    /* hdr->flags = short_be(PICO_DHCP_FLAG_BROADCAST); / * Nope: see bug #96! * / */
+    hdr->dhcp_magic = PICO_DHCPD_MAGIC_COOKIE;
+    /* copy client hardware address */
+    memcpy(hdr->hwaddr, &dhcpc->dev->eth->mac, PICO_SIZE_ETH);
+
+    if (destination.addr == PICO_IP4_BCAST)
+        pico_ipv4_route_set_bcast_link(pico_ipv4_link_get(&dhcpc->address));
+
+    r = pico_socket_sendto(dhcpc->s, hdr, (int)(sizeof(struct pico_dhcp_hdr) + optlen), &destination, PICO_DHCPD_PORT);
+    PICO_FREE(hdr);
+    if (r < 0)
+        return -1;
+
+    return 0;
+}
+
+static void pico_dhcp_client_wakeup(uint16_t ev, struct pico_socket *s)
+{
+
+    uint8_t *buf;
+    int r = 0;
+    struct pico_dhcp_hdr *hdr = NULL;
+    struct pico_dhcp_client_cookie *dhcpc = NULL;
+
+    if ((ev & PICO_SOCK_EV_RD) == 0)
+        return;
+
+    buf = PICO_ZALLOC(DHCP_CLIENT_MAXMSGZISE);
+    if (!buf) {
+        return;
+    }
+
+    r = pico_socket_recvfrom(s, buf, DHCP_CLIENT_MAXMSGZISE, NULL, NULL);
+    if (r < 0)
+        goto out_discard_buf;
+
+    /* If the 'xid' of an arriving message does not match the 'xid'
+     * of the most recent transmitted message, the message must be
+     * silently discarded. */
+    hdr = (struct pico_dhcp_hdr *)buf;
+    dhcpc = pico_dhcp_client_find_cookie(hdr->xid);
+    if (!dhcpc)
+        goto out_discard_buf;
+
+    dhcpc->event = (uint8_t)pico_dhcp_client_opt_parse(buf, (uint16_t)r);
+    pico_dhcp_state_machine(dhcpc->event, dhcpc, buf);
+
+out_discard_buf:
+    PICO_FREE(buf);
+}
+
+void *pico_dhcp_get_identifier(uint32_t xid)
+{
+    return (void *)pico_dhcp_client_find_cookie(xid);
+}
+
+struct pico_ip4 pico_dhcp_get_address(void*dhcpc)
+{
+    return ((struct pico_dhcp_client_cookie*)dhcpc)->address;
+}
+
+struct pico_ip4 pico_dhcp_get_gateway(void*dhcpc)
+{
+    return ((struct pico_dhcp_client_cookie*)dhcpc)->gateway;
+}
+
+struct pico_ip4 pico_dhcp_get_netmask(void *dhcpc)
+{
+    return ((struct pico_dhcp_client_cookie*)dhcpc)->netmask;
+}
+
+struct pico_ip4 pico_dhcp_get_nameserver(void*dhcpc, int index)
+{
+    struct pico_ip4 fault = {
+        .addr = 0xFFFFFFFFU
+    };
+    if ((index != 0) && (index != 1))
+        return fault;
+
+    return ((struct pico_dhcp_client_cookie*)dhcpc)->nameserver[index];
+}
+
+int pico_dhcp_client_abort(uint32_t xid)
+{
+    return pico_dhcp_client_del_cookie(xid);
+}
+#endif
diff --git a/net/picotcp/modules/pico_dhcp_client.h b/net/picotcp/modules/pico_dhcp_client.h
new file mode 100644
index 0000000..2b5bbf9
--- /dev/null
+++ b/net/picotcp/modules/pico_dhcp_client.h
@@ -0,0 +1,30 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   .
+
+ *********************************************************************/
+#ifndef INCLUDE_PICO_DHCP_CLIENT
+#define INCLUDE_PICO_DHCP_CLIENT
+#include "pico_defines.h"
+#ifdef PICO_SUPPORT_UDP
+#include "pico_dhcp_common.h"
+#include "pico_addressing.h"
+#include "pico_protocol.h"
+
+int pico_dhcp_initiate_negotiation(struct pico_device *device, void (*callback)(void*cli, int code), uint32_t *xid);
+void *pico_dhcp_get_identifier(uint32_t xid);
+struct pico_ip4 pico_dhcp_get_address(void *cli);
+struct pico_ip4 pico_dhcp_get_gateway(void *cli);
+struct pico_ip4 pico_dhcp_get_netmask(void *cli);
+struct pico_ip4 pico_dhcp_get_nameserver(void*cli, int index);
+int pico_dhcp_client_abort(uint32_t xid);
+
+/* possible codes for the callback */
+#define PICO_DHCP_SUCCESS 0
+#define PICO_DHCP_ERROR   1
+#define PICO_DHCP_RESET   2
+
+#endif
+#endif
diff --git a/net/picotcp/modules/pico_dhcp_common.c b/net/picotcp/modules/pico_dhcp_common.c
new file mode 100644
index 0000000..99701ba
--- /dev/null
+++ b/net/picotcp/modules/pico_dhcp_common.c
@@ -0,0 +1,189 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   .
+
+   Authors: Frederik Van Slycken
+ *********************************************************************/
+
+#include "pico_config.h"
+#include "pico_stack.h"
+#include "pico_dhcp_common.h"
+
+#if defined (PICO_SUPPORT_DHCPC) || defined (PICO_SUPPORT_DHCPD)
+/* pico_dhcp_are_options_valid needs to be called first to prevent illegal memory access */
+/* The argument pointer is moved forward to the next option */
+struct pico_dhcp_opt *pico_dhcp_next_option(struct pico_dhcp_opt **ptr)
+{
+    uint8_t **p = (uint8_t **)ptr;
+    struct pico_dhcp_opt *opt = *ptr;
+
+    if (opt->code == PICO_DHCP_OPT_END)
+        return NULL;
+
+    if (opt->code == PICO_DHCP_OPT_PAD) {
+        *p += 1;
+        return *ptr;
+    }
+
+    *p += (opt->len + 2); /* (len + 2) to account for code and len octet */
+    return *ptr;
+}
+
+uint8_t pico_dhcp_are_options_valid(void *ptr, int32_t len)
+{
+    uint8_t optlen = 0, *p = ptr;
+
+    while (len > 0) {
+        switch (*p)
+        {
+        case PICO_DHCP_OPT_END:
+            return 1;
+
+        case PICO_DHCP_OPT_PAD:
+            p++;
+            len--;
+            break;
+
+        default:
+            p++; /* move pointer from code octet to len octet */
+            len--;
+            if ((len <= 0) || (len - (*p + 1) < 0)) /* (*p + 1) to account for len octet */
+                return 0;
+
+            optlen = *p;
+            p += optlen + 1;
+            len -= optlen;
+            break;
+        }
+    }
+    return 0;
+}
+
+uint8_t pico_dhcp_opt_netmask(void *ptr, struct pico_ip4 *ip)
+{
+    struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
+
+    /* option: netmask */
+    opt->code = PICO_DHCP_OPT_NETMASK;
+    opt->len = PICO_DHCP_OPTLEN_NETMASK - PICO_DHCP_OPTLEN_HDR;
+    opt->ext.netmask.ip = *ip;
+    return PICO_DHCP_OPTLEN_NETMASK;
+}
+
+uint8_t pico_dhcp_opt_router(void *ptr, struct pico_ip4 *ip)
+{
+    struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
+
+    /* option: router */
+    opt->code = PICO_DHCP_OPT_ROUTER;
+    opt->len = PICO_DHCP_OPTLEN_ROUTER - PICO_DHCP_OPTLEN_HDR;
+    opt->ext.router.ip = *ip;
+    return PICO_DHCP_OPTLEN_ROUTER;
+}
+
+uint8_t pico_dhcp_opt_dns(void *ptr, struct pico_ip4 *ip)
+{
+    struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
+
+    /* option: dns */
+    opt->code = PICO_DHCP_OPT_DNS;
+    opt->len = PICO_DHCP_OPTLEN_DNS - PICO_DHCP_OPTLEN_HDR;
+    opt->ext.dns1.ip = *ip;
+    return PICO_DHCP_OPTLEN_DNS;
+}
+
+uint8_t pico_dhcp_opt_broadcast(void *ptr, struct pico_ip4 *ip)
+{
+    struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
+
+    /* option: broadcast */
+    opt->code = PICO_DHCP_OPT_BROADCAST;
+    opt->len = PICO_DHCP_OPTLEN_BROADCAST - PICO_DHCP_OPTLEN_HDR;
+    opt->ext.broadcast.ip = *ip;
+    return PICO_DHCP_OPTLEN_BROADCAST;
+}
+
+uint8_t pico_dhcp_opt_reqip(void *ptr, struct pico_ip4 *ip)
+{
+    struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
+
+    /* option: request IP address */
+    opt->code = PICO_DHCP_OPT_REQIP;
+    opt->len = PICO_DHCP_OPTLEN_REQIP - PICO_DHCP_OPTLEN_HDR;
+    opt->ext.req_ip.ip = *ip;
+    return PICO_DHCP_OPTLEN_REQIP;
+}
+
+uint8_t pico_dhcp_opt_leasetime(void *ptr, uint32_t time)
+{
+    struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
+
+    /* option: lease time */
+    opt->code = PICO_DHCP_OPT_LEASETIME;
+    opt->len = PICO_DHCP_OPTLEN_LEASETIME - PICO_DHCP_OPTLEN_HDR;
+    opt->ext.lease_time.time = time;
+    return PICO_DHCP_OPTLEN_LEASETIME;
+}
+
+uint8_t pico_dhcp_opt_msgtype(void *ptr, uint8_t type)
+{
+    struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
+
+    /* option: message type */
+    opt->code = PICO_DHCP_OPT_MSGTYPE;
+    opt->len = PICO_DHCP_OPTLEN_MSGTYPE - PICO_DHCP_OPTLEN_HDR;
+    opt->ext.msg_type.type = type;
+    return PICO_DHCP_OPTLEN_MSGTYPE;
+}
+
+uint8_t pico_dhcp_opt_serverid(void *ptr, struct pico_ip4 *ip)
+{
+    struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
+
+    /* option: server identifier */
+    opt->code = PICO_DHCP_OPT_SERVERID;
+    opt->len = PICO_DHCP_OPTLEN_SERVERID - PICO_DHCP_OPTLEN_HDR;
+    opt->ext.server_id.ip = *ip;
+    return PICO_DHCP_OPTLEN_SERVERID;
+}
+
+uint8_t pico_dhcp_opt_paramlist(void *ptr)
+{
+    struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
+
+    /* option: parameter list */
+    opt->code = PICO_DHCP_OPT_PARAMLIST;
+    opt->len = PICO_DHCP_OPTLEN_PARAMLIST - PICO_DHCP_OPTLEN_HDR;
+    opt->ext.param_list.code[0] = PICO_DHCP_OPT_NETMASK;
+    opt->ext.param_list.code[1] = PICO_DHCP_OPT_TIME;
+    opt->ext.param_list.code[2] = PICO_DHCP_OPT_ROUTER;
+    opt->ext.param_list.code[3] = PICO_DHCP_OPT_HOSTNAME;
+    opt->ext.param_list.code[4] = PICO_DHCP_OPT_RENEWALTIME;
+    opt->ext.param_list.code[5] = PICO_DHCP_OPT_REBINDINGTIME;
+    opt->ext.param_list.code[6] = PICO_DHCP_OPT_DNS;
+    return PICO_DHCP_OPTLEN_PARAMLIST;
+}
+
+uint8_t pico_dhcp_opt_maxmsgsize(void *ptr, uint16_t size)
+{
+    struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
+
+    /* option: maximum message size */
+    opt->code = PICO_DHCP_OPT_MAXMSGSIZE;
+    opt->len = PICO_DHCP_OPTLEN_MAXMSGSIZE - PICO_DHCP_OPTLEN_HDR;
+    opt->ext.max_msg_size.size = short_be(size);
+    return PICO_DHCP_OPTLEN_MAXMSGSIZE;
+}
+
+uint8_t pico_dhcp_opt_end(void *ptr)
+{
+    uint8_t *opt = (uint8_t *)ptr;
+
+    /* option: end of options */
+    *opt = PICO_DHCP_OPT_END;
+    return PICO_DHCP_OPTLEN_END;
+}
+
+#endif
diff --git a/net/picotcp/modules/pico_dhcp_common.h b/net/picotcp/modules/pico_dhcp_common.h
new file mode 100644
index 0000000..cdf1bd1
--- /dev/null
+++ b/net/picotcp/modules/pico_dhcp_common.h
@@ -0,0 +1,186 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   .
+
+ *********************************************************************/
+#ifndef INCLUDE_PICO_DHCP_COMMON
+#define INCLUDE_PICO_DHCP_COMMON
+#include "pico_config.h"
+#include "pico_addressing.h"
+
+#define PICO_DHCPD_PORT (short_be(67))
+#define PICO_DHCP_CLIENT_PORT (short_be(68))
+#define PICO_DHCPD_MAGIC_COOKIE (long_be(0x63825363))
+#define PICO_DHCP_HTYPE_ETH 1
+
+/* flags */
+#define PICO_DHCP_FLAG_BROADCAST        0x8000
+
+/* options */
+#define PICO_DHCP_OPT_PAD               0x00
+#define PICO_DHCP_OPT_NETMASK           0x01
+#define PICO_DHCP_OPT_TIME              0x02
+#define PICO_DHCP_OPT_ROUTER            0x03
+#define PICO_DHCP_OPT_DNS               0x06
+#define PICO_DHCP_OPT_HOSTNAME          0x0c
+#define PICO_DHCP_OPT_DOMAINNAME        0x0f
+#define PICO_DHCP_OPT_MTU               0x1a
+#define PICO_DHCP_OPT_BROADCAST         0x1c
+#define PICO_DHCP_OPT_NETBIOSNS         0x2c
+#define PICO_DHCP_OPT_NETBIOSSCOPE      0x2f
+#define PICO_DHCP_OPT_REQIP             0x32
+#define PICO_DHCP_OPT_LEASETIME         0x33
+#define PICO_DHCP_OPT_OPTOVERLOAD       0x34
+#define PICO_DHCP_OPT_MSGTYPE           0x35
+#define PICO_DHCP_OPT_SERVERID          0x36
+#define PICO_DHCP_OPT_PARAMLIST         0x37
+#define PICO_DHCP_OPT_MESSAGE           0x38
+#define PICO_DHCP_OPT_MAXMSGSIZE        0x39
+#define PICO_DHCP_OPT_RENEWALTIME       0x3a
+#define PICO_DHCP_OPT_REBINDINGTIME     0x3b
+#define PICO_DHCP_OPT_VENDORID          0x3c
+#define PICO_DHCP_OPT_CLIENTID          0x3d
+#define PICO_DHCP_OPT_DOMAINSEARCH      0x77
+#define PICO_DHCP_OPT_STATICROUTE       0x79
+#define PICO_DHCP_OPT_END               0xFF
+
+/* options len */
+#define PICO_DHCP_OPTLEN_HDR            2 /* account for code and len field */
+#define PICO_DHCP_OPTLEN_NETMASK        6
+#define PICO_DHCP_OPTLEN_ROUTER         6
+#define PICO_DHCP_OPTLEN_DNS            6
+#define PICO_DHCP_OPTLEN_BROADCAST      6
+#define PICO_DHCP_OPTLEN_REQIP          6
+#define PICO_DHCP_OPTLEN_LEASETIME      6
+#define PICO_DHCP_OPTLEN_OPTOVERLOAD    3
+#define PICO_DHCP_OPTLEN_MSGTYPE        3
+#define PICO_DHCP_OPTLEN_SERVERID       6
+#define PICO_DHCP_OPTLEN_PARAMLIST      9 /* PicoTCP specific */
+#define PICO_DHCP_OPTLEN_MAXMSGSIZE     4
+#define PICO_DHCP_OPTLEN_RENEWALTIME    6
+#define PICO_DHCP_OPTLEN_REBINDINGTIME  6
+#define PICO_DHCP_OPTLEN_END            1
+
+/* op codes */
+#define PICO_DHCP_OP_REQUEST            1
+#define PICO_DHCP_OP_REPLY              2
+
+/* rfc message types */
+#define PICO_DHCP_MSG_DISCOVER          1
+#define PICO_DHCP_MSG_OFFER             2
+#define PICO_DHCP_MSG_REQUEST           3
+#define PICO_DHCP_MSG_DECLINE           4
+#define PICO_DHCP_MSG_ACK               5
+#define PICO_DHCP_MSG_NAK               6
+#define PICO_DHCP_MSG_RELEASE           7
+#define PICO_DHCP_MSG_INFORM            8
+
+/* custom message types */
+#define PICO_DHCP_EVENT_T1              9
+#define PICO_DHCP_EVENT_T2              10
+#define PICO_DHCP_EVENT_LEASE           11
+#define PICO_DHCP_EVENT_RETRANSMIT      12
+#define PICO_DHCP_EVENT_NONE            0xff
+
+PACKED_STRUCT_DEF pico_dhcp_hdr
+{
+    uint8_t op;
+    uint8_t htype;
+    uint8_t hlen;
+    uint8_t hops; /* zero */
+    uint32_t xid; /* store this in the request */
+    uint16_t secs; /* ignore */
+    uint16_t flags;
+    uint32_t ciaddr; /* client address - if asking for renewal */
+    uint32_t yiaddr; /* your address (client) */
+    uint32_t siaddr; /* dhcp offered address */
+    uint32_t giaddr; /* relay agent, bootp. */
+    uint8_t hwaddr[6];
+    uint8_t hwaddr_padding[10];
+    char hostname[64];
+    char bootp_filename[128];
+    uint32_t dhcp_magic;
+    uint8_t options[0];
+};
+
+PACKED_STRUCT_DEF pico_dhcp_opt
+{
+    uint8_t code;
+    uint8_t len;
+    PACKED_UNION_DEF dhcp_opt_ext_u {
+        PEDANTIC_STRUCT_DEF netmask_s {
+            struct pico_ip4 ip;
+        } netmask;
+        PEDANTIC_STRUCT_DEF router_s {
+            struct pico_ip4 ip;
+        } router;
+        PEDANTIC_STRUCT_DEF dns_s {
+            struct pico_ip4 ip;
+        } dns1;
+        struct dns_s dns2;
+        PEDANTIC_STRUCT_DEF broadcast_s {
+            struct pico_ip4 ip;
+        } broadcast;
+        PEDANTIC_STRUCT_DEF req_ip_s {
+            struct pico_ip4 ip;
+        } req_ip;
+        PEDANTIC_STRUCT_DEF lease_time_s {
+            uint32_t time;
+        } lease_time;
+        PEDANTIC_STRUCT_DEF opt_overload_s {
+            uint8_t value;
+        } opt_overload;
+        PEDANTIC_STRUCT_DEF tftp_server_s {
+            char name[1];
+        } tftp_server;
+        PEDANTIC_STRUCT_DEF bootfile_s {
+            char name[1];
+        } bootfile;
+        PEDANTIC_STRUCT_DEF msg_type_s {
+            uint8_t type;
+        } msg_type;
+        PEDANTIC_STRUCT_DEF server_id_s {
+            struct pico_ip4 ip;
+        } server_id;
+        PEDANTIC_STRUCT_DEF param_list_s {
+            uint8_t code[1];
+        } param_list;
+        PEDANTIC_STRUCT_DEF message_s {
+            char error[1];
+        } message;
+        PEDANTIC_STRUCT_DEF max_msg_size_s {
+            uint16_t size;
+        } max_msg_size;
+        PEDANTIC_STRUCT_DEF renewal_time_s {
+            uint32_t time;
+        } renewal_time;
+        PEDANTIC_STRUCT_DEF rebinding_time_s {
+            uint32_t time;
+        } rebinding_time;
+        PEDANTIC_STRUCT_DEF vendor_id_s {
+            uint8_t id[1];
+        } vendor_id;
+        PEDANTIC_STRUCT_DEF client_id_s {
+            uint8_t id[1];
+        } client_id;
+    } ext;
+};
+
+uint8_t dhcp_get_next_option(uint8_t *begin, uint8_t *data, int *len, uint8_t **nextopt);
+struct pico_dhcp_opt *pico_dhcp_next_option(struct pico_dhcp_opt **ptr);
+uint8_t pico_dhcp_are_options_valid(void *ptr, int32_t len);
+
+uint8_t pico_dhcp_opt_netmask(void *ptr, struct pico_ip4 *ip);
+uint8_t pico_dhcp_opt_router(void *ptr, struct pico_ip4 *ip);
+uint8_t pico_dhcp_opt_dns(void *ptr, struct pico_ip4 *ip);
+uint8_t pico_dhcp_opt_broadcast(void *ptr, struct pico_ip4 *ip);
+uint8_t pico_dhcp_opt_reqip(void *ptr, struct pico_ip4 *ip);
+uint8_t pico_dhcp_opt_leasetime(void *ptr, uint32_t time);
+uint8_t pico_dhcp_opt_msgtype(void *ptr, uint8_t type);
+uint8_t pico_dhcp_opt_serverid(void *ptr, struct pico_ip4 *ip);
+uint8_t pico_dhcp_opt_paramlist(void *ptr);
+uint8_t pico_dhcp_opt_maxmsgsize(void *ptr, uint16_t size);
+uint8_t pico_dhcp_opt_end(void *ptr);
+#endif
diff --git a/net/picotcp/modules/pico_dns_client.h b/net/picotcp/modules/pico_dns_client.h
new file mode 100644
index 0000000..910cc92
--- /dev/null
+++ b/net/picotcp/modules/pico_dns_client.h
@@ -0,0 +1,46 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   .
+
+   Authors: Kristof Roelants
+ *********************************************************************/
+
+#ifndef INCLUDE_PICO_DNS_CLIENT
+#define INCLUDE_PICO_DNS_CLIENT
+
+#define PICO_DNS_NS_DEL 0
+#define PICO_DNS_NS_ADD 1
+#include "pico_config.h"
+
+/* Compression values */
+#define PICO_DNS_LABEL 0
+#define PICO_DNS_POINTER 3
+
+/* Label len */
+#define PICO_DNS_LABEL_INITIAL 1u
+#define PICO_DNS_LABEL_ROOT 1
+
+/* TTL values */
+#define PICO_DNS_MAX_TTL 604800 /* one week */
+
+/* Len of an IPv4 address string */
+#define PICO_DNS_IPV4_ADDR_LEN 16
+#define PICO_DNS_IPV6_ADDR_LEN 54
+
+/* Default nameservers + port */
+#define PICO_DNS_NS_DEFAULT "208.67.222.222"
+#define PICO_DNS_NS_PORT 53
+
+int pico_dns_client_init(void);
+/* flag is PICO_DNS_NS_DEL or PICO_DNS_NS_ADD */
+int pico_dns_client_nameserver(struct pico_ip4 *ns, uint8_t flag);
+int pico_dns_client_getaddr(const char *url, void (*callback)(char *ip, void *arg), void *arg);
+int pico_dns_client_getname(const char *ip, void (*callback)(char *url, void *arg), void *arg);
+#ifdef PICO_SUPPORT_IPV6
+int pico_dns_client_getaddr6(const char *url, void (*callback)(char *, void *), void *arg);
+int pico_dns_client_getname6(const char *url, void (*callback)(char *, void *), void *arg);
+#endif
+
+#endif /* _INCLUDE_PICO_DNS_CLIENT */
diff --git a/net/picotcp/modules/pico_dns_common.h b/net/picotcp/modules/pico_dns_common.h
new file mode 100644
index 0000000..9172696
--- /dev/null
+++ b/net/picotcp/modules/pico_dns_common.h
@@ -0,0 +1,521 @@
+
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   See LICENSE and COPYING for usage.
+   .
+   Authors: Toon Stegen, Jelle De Vleeschouwer
+ *********************************************************************/
+
+#ifndef INCLUDE_PICO_DNS_COMMON
+#define INCLUDE_PICO_DNS_COMMON
+
+#include "pico_config.h"
+#include "pico_tree.h"
+
+/* TYPE values */
+#define PICO_DNS_TYPE_A 1
+#define PICO_DNS_TYPE_CNAME 5
+#define PICO_DNS_TYPE_PTR 12
+#define PICO_DNS_TYPE_TXT 16
+#define PICO_DNS_TYPE_AAAA 28
+#define PICO_DNS_TYPE_SRV 33
+#define PICO_DNS_TYPE_NSEC 47
+#define PICO_DNS_TYPE_ANY 255
+
+/* CLASS values */
+#define PICO_DNS_CLASS_IN 1
+
+/* FLAG values */
+#define PICO_DNS_QR_QUERY 0
+#define PICO_DNS_QR_RESPONSE 1
+#define PICO_DNS_OPCODE_QUERY 0
+#define PICO_DNS_OPCODE_IQUERY 1
+#define PICO_DNS_OPCODE_STATUS 2
+#define PICO_DNS_AA_NO_AUTHORITY 0
+#define PICO_DNS_AA_IS_AUTHORITY 1
+#define PICO_DNS_TC_NO_TRUNCATION 0
+#define PICO_DNS_TC_IS_TRUNCATED 1
+#define PICO_DNS_RD_NO_DESIRE 0
+#define PICO_DNS_RD_IS_DESIRED 1
+#define PICO_DNS_RA_NO_SUPPORT 0
+#define PICO_DNS_RA_IS_SUPPORTED 1
+#define PICO_DNS_RCODE_NO_ERROR 0
+#define PICO_DNS_RCODE_EFORMAT 1
+#define PICO_DNS_RCODE_ESERVER 2
+#define PICO_DNS_RCODE_ENAME 3
+#define PICO_DNS_RCODE_ENOIMP 4
+#define PICO_DNS_RCODE_EREFUSED 5
+
+#define PICO_ARPA_IPV4_SUFFIX ".in-addr.arpa"
+
+#ifdef PICO_SUPPORT_IPV6
+#define STRLEN_PTR_IP6 63
+#define PICO_ARPA_IPV6_SUFFIX ".IP6.ARPA"
+#endif
+
+#define PICO_DNS_NAMEBUF_SIZE (256)
+
+enum pico_dns_arpa
+{
+    PICO_DNS_ARPA4,
+    PICO_DNS_ARPA6,
+    PICO_DNS_NO_ARPA,
+};
+
+/* flags split in 2x uint8 due to endianness */
+PACKED_STRUCT_DEF pico_dns_header
+{
+    uint16_t id;        /* Packet id */
+    uint8_t rd : 1;     /* Recursion Desired */
+    uint8_t tc : 1;     /* TrunCation */
+    uint8_t aa : 1;     /* Authoritative Answer */
+    uint8_t opcode : 4; /* Opcode */
+    uint8_t qr : 1;     /* Query/Response */
+    uint8_t rcode : 4;  /* Response code */
+    uint8_t z : 3;      /* Zero */
+    uint8_t ra : 1;     /* Recursion Available */
+    uint16_t qdcount;   /* Question count */
+    uint16_t ancount;   /* Answer count */
+    uint16_t nscount;   /* Authority count */
+    uint16_t arcount;   /* Additional count */
+};
+typedef struct pico_dns_header pico_dns_packet;
+
+/* Question fixed-sized fields */
+PACKED_STRUCT_DEF pico_dns_question_suffix
+{
+    uint16_t qtype;
+    uint16_t qclass;
+};
+
+/* Resource record fixed-sized fields */
+PACKED_STRUCT_DEF pico_dns_record_suffix
+{
+    uint16_t rtype;
+    uint16_t rclass;
+    uint32_t rttl;
+    uint16_t rdlength;
+};
+
+/* DNS QUESTION */
+struct pico_dns_question
+{
+    char *qname;
+    struct pico_dns_question_suffix *qsuffix;
+    uint16_t qname_length;
+    uint8_t proto;
+};
+
+/* DNS RECORD */
+struct pico_dns_record
+{
+    char *rname;
+    struct pico_dns_record_suffix *rsuffix;
+    uint8_t *rdata;
+    uint16_t rname_length;
+};
+
+/* MARK: v NAME & IP FUNCTIONS */
+
+/* ****************************************************************************
+ *  Checks if the DNS name doesn't exceed 256 bytes including zero-byte.
+ *
+ *  @param namelen Length of the DNS name-string including zero-byte
+ *  @return 0 when the length is correct
+ * ****************************************************************************/
+int
+pico_dns_check_namelen( uint16_t namelen );
+
+/* ****************************************************************************
+ *  Returns the length of a name in a DNS-packet as if DNS name compression
+ *  would be applied to the packet. If there's no compression present this
+ *	returns the strlen. If there's compression present this returns the length
+ *	until the compression-pointer + 1.
+ *
+ *  @param name Compressed name you want the calculate the strlen from
+ *  @return Returns strlen of a compressed name, takes the first byte of compr-
+ *			ession pointer into account but not the second byte, which acts
+ *			like a trailing zero-byte.
+ * ****************************************************************************/
+uint16_t
+pico_dns_namelen_comp( char *name );
+
+/* ****************************************************************************
+ *  Returns the uncompressed name in DNS name format when DNS name compression
+ *  is applied to the packet-buffer.
+ *
+ *  @param name   Compressed name, should be in the bounds of the actual packet
+ *  @param packet Packet that contains the compressed name
+ *  @return Returns the decompressed name, NULL on failure.
+ * ****************************************************************************/
+char *
+pico_dns_decompress_name( char *name, pico_dns_packet *packet );
+
+/* ****************************************************************************
+ *  Converts a DNS name in DNS name format to a name in URL format. Provides
+ *  space for the name in URL format as well. PICO_FREE() should be called on
+ *  the returned string buffer that contains the name in URL format.
+ *
+ *  @param qname DNS name in DNS name format to convert
+ *  @return Returns a pointer to a string-buffer with the URL name on success.
+ * ****************************************************************************/
+char *
+pico_dns_qname_to_url( const char *qname );
+
+/* ****************************************************************************
+ *  Converts a DNS name in URL format to name in DNS name format. Provides
+ *  space for the DNS name as well. PICO_FREE() should be called on the returned
+ *  string buffer that contains the DNS name.
+ *
+ *  @param url DNS name in URL format to convert
+ *  @return Returns a pointer to a string-buffer with the DNS name on success.
+ * ****************************************************************************/
+char *
+pico_dns_url_to_qname( const char *url );
+
+/* ****************************************************************************
+ *  @param url String-buffer
+ *  @return Length of string-buffer in an uint16_t
+ * ****************************************************************************/
+uint16_t
+pico_dns_strlen( const char *url );
+
+/* ****************************************************************************
+ *  Replaces .'s in a DNS name in URL format by the label lengths. So it
+ *  actually converts a name in URL format to a name in DNS name format.
+ *  f.e. "*www.google.be" => "3www6google2be0"
+ *
+ *  @param url    Location to buffer with name in URL format. The URL needs to
+ *                be +1 byte offset in the actual buffer. Size is should be
+ *                strlen(url) + 2.
+ *  @param maxlen Maximum length of buffer so it doesn't cause a buffer overflow
+ *  @return 0 on success, something else on failure.
+ * ****************************************************************************/
+int pico_dns_name_to_dns_notation( char *url, unsigned int maxlen );
+
+/* ****************************************************************************
+ *  Replaces the label lengths in a DNS-name by .'s. So it actually converts a
+ *  name in DNS format to a name in URL format.
+ *  f.e. 3www6google2be0 => .www.google.be
+ *
+ *  @param ptr    Location to buffer with name in DNS name format
+ *  @param maxlen Maximum length of buffer so it doesn't cause a buffer overflow
+ *  @return 0 on success, something else on failure.
+ * ****************************************************************************/
+int pico_dns_notation_to_name( char *ptr, unsigned int maxlen );
+
+/* ****************************************************************************
+ *  Determines the length of the first label of a DNS name in URL-format
+ *
+ *  @param url DNS name in URL-format
+ *  @return Length of the first label of DNS name in URL-format
+ * ****************************************************************************/
+uint16_t
+pico_dns_first_label_length( const char *url );
+
+/* ****************************************************************************
+ *  Mirrors a dotted IPv4-address string.
+ *	f.e. 192.168.0.1 => 1.0.168.192
+ *
+ *  @param ptr
+ *  @return 0 on success, something else on failure.
+ * ****************************************************************************/
+int
+pico_dns_mirror_addr( char *ptr );
+
+/* ****************************************************************************
+ *  Convert an IPv6-address in string-format to a IPv6-address in nibble-format.
+ *	Doesn't add a IPv6 ARPA-suffix though.
+ *
+ *  @param ip  IPv6-address stored as a string
+ *  @param dst Destination to store IPv6-address in nibble-format
+ * ****************************************************************************/
+void
+pico_dns_ipv6_set_ptr( const char *ip, char *dst );
+
+/* MARK: QUESTION FUNCTIONS */
+
+/* ****************************************************************************
+ *  Deletes a single DNS Question.
+ *
+ *  @param question Void-pointer to DNS Question. Can be used with pico_tree_-
+ *					destroy.
+ *  @return Returns 0 on success, something else on failure.
+ * ****************************************************************************/
+int
+pico_dns_question_delete( void **question);
+
+/* ****************************************************************************
+ *  Fills in the DNS question suffix-fields with the correct values.
+ *
+ *  todo: Update pico_dns_client to make the same mechanism possible as with
+ *        filling DNS Resource Record-suffixes. This function shouldn't be an
+ *		  API-function.
+ *
+ *  @param suf    Pointer to the suffix member of the DNS question.
+ *  @param qtype  DNS type of the DNS question to be.
+ *  @param qclass DNS class of the DNS question to be.
+ *  @return Returns 0 on success, something else on failure.
+ * ****************************************************************************/
+int
+pico_dns_question_fill_suffix( struct pico_dns_question_suffix *suf,
+                               uint16_t qtype,
+                               uint16_t qclass );
+
+/* ****************************************************************************
+ *  Creates a standalone DNS Question with a given name and type.
+ *
+ *  @param url     DNS question name in URL format. Will be converted to DNS
+ *				   name notation format.
+ *  @param len     Will be filled with the total length of the DNS question.
+ *  @param proto   Protocol for which you want to create a question. Can be
+ *				   either PICO_PROTO_IPV4 or PICO_PROTO_IPV6.
+ *  @param qtype   DNS type of the question to be.
+ *  @param qclass  DNS class of the question to be.
+ *  @param reverse When this is true, a reverse resolution name will be gene-
+ *				   from the URL
+ *  @return Returns pointer to the created DNS Question on success, NULL on
+ *			failure.
+ * ****************************************************************************/
+struct pico_dns_question *
+pico_dns_question_create( const char *url,
+                          uint16_t *len,
+                          uint8_t proto,
+                          uint16_t qtype,
+                          uint16_t qclass,
+                          uint8_t reverse );
+
+/* ****************************************************************************
+ *  Decompresses the name of a single DNS question.
+ *
+ *  @param question Question you want to decompress the name of
+ *  @param packet   Packet in which the DNS question is contained.
+ *  @return Pointer to original name of the DNS question before decompressing.
+ * ****************************************************************************/
+char *
+pico_dns_question_decompress( struct pico_dns_question *question,
+                              pico_dns_packet *packet );
+
+/* MARK: RESOURCE RECORD FUNCTIONS */
+
+/* ****************************************************************************
+ *  Deletes a single DNS resource record.
+ *
+ *  @param record Void-pointer to DNS record. Can be used with pico_tree_destroy
+ *  @return Returns 0 on success, something else on failure.
+ * ****************************************************************************/
+int
+pico_dns_record_delete( void **record );
+
+/* ****************************************************************************
+ *  Just makes a hardcopy from a single DNS Resource Record
+ *
+ *  @param record DNS record you want to copy
+ *  @return Pointer to copy of DNS record.
+ * ****************************************************************************/
+struct pico_dns_record *
+pico_dns_record_copy( struct pico_dns_record *record );
+
+/* ****************************************************************************
+ *  Create a standalone DNS Resource Record with given name, type and data.
+ *
+ *  @param url     DNS rrecord name in URL format. Will be converted to DNS
+ *                 name notation format.
+ *  @param _rdata  Memory buffer with data to insert in the resource record. If
+ *				   data of record should contain a DNS name, the name in the
+ *				   databuffer needs to be in URL-format.
+ *  @param datalen The exact length in bytes of the _rdata-buffer. If data of
+ *				   record should contain a DNS name, datalen needs to be
+ *				   pico_dns_strlen(_rdata).
+ *  @param len     Will be filled with the total length of the DNS rrecord.
+ *  @param rtype   DNS type of the resource record to be.
+ *  @param rclass  DNS class of the resource record to be.
+ *  @param rttl    DNS ttl of the resource record to be.
+ *  @return Returns pointer to the created DNS Resource Record
+ * ****************************************************************************/
+struct pico_dns_record *
+pico_dns_record_create( const char *url,
+                        void *_rdata,
+                        uint16_t datalen,
+                        uint16_t *len,
+                        uint16_t rtype,
+                        uint16_t rclass,
+                        uint32_t rttl );
+
+/* ****************************************************************************
+ *  Decompresses the name of single DNS record.
+ *
+ *  @param record DNS record to decompress the name of.
+ *  @param packet Packet in which is DNS record is present
+ *  @return Pointer to original name of the DNS record before decompressing.
+ * ****************************************************************************/
+char *
+pico_dns_record_decompress( struct pico_dns_record *record,
+                            pico_dns_packet *packet );
+
+/* MARK: COMPARING */
+
+/* ****************************************************************************
+ *  Compares two databuffers against each other.
+ *
+ *  @param a          1st Memory buffer to compare
+ *  @param b          2nd Memory buffer to compare
+ *  @param rdlength_a Length of 1st memory buffer
+ *  @param rdlength_b Length of 2nd memory buffer
+ *  @return 0 when the buffers are equal, returns difference when they're not.
+ * ****************************************************************************/
+int
+pico_dns_rdata_cmp( uint8_t *a, uint8_t *b,
+                    uint16_t rdlength_a, uint16_t rdlength_b );
+
+/* ****************************************************************************
+ *  Compares 2 DNS questions
+ *
+ *  @param qa DNS question A as a void-pointer (for pico_tree)
+ *  @param qb DNS question A as a void-pointer (for pico_tree)
+ *  @return 0 when questions are equal, returns difference when they're not.
+ * ****************************************************************************/
+int
+pico_dns_question_cmp( void *qa,
+                       void *qb );
+
+/* ****************************************************************************
+ *  Compares 2 DNS records by type and name only
+ *
+ *  @param ra DNS record A as a void-pointer (for pico_tree)
+ *  @param rb DNS record B as a void-pointer (for pico_tree)
+ *  @return 0 when name and type of records are equal, returns difference when
+ *			they're not.
+ * ****************************************************************************/
+int
+pico_dns_record_cmp_name_type( void *ra,
+                               void *rb );
+
+/* ****************************************************************************
+ *  Compares 2 DNS records by type, name AND rdata for a truly unique result
+ *
+ *  @param ra DNS record A as a void-pointer (for pico_tree)
+ *  @param rb DNS record B as a void-pointer (for pico_tree)
+ *  @return 0 when records are equal, returns difference when they're not
+ * ****************************************************************************/
+int
+pico_dns_record_cmp( void *ra,
+                     void *rb );
+
+/* MARK: PICO_TREE */
+
+/* ****************************************************************************
+ *  Erases a pico_tree entirely.
+ *
+ *  @param tree        Pointer to a pico_tree-instance
+ *  @param node_delete Helper-function for type-specific deleting.
+ *  @return Returns 0 on success, something else on failure.
+ * ****************************************************************************/
+int
+pico_tree_destroy( struct pico_tree *tree, int (*node_delete)(void **));
+
+/* ****************************************************************************
+ *  Determines the amount of nodes in a pico_tree
+ *
+ *  @param tree Pointer to pico_tree-instance
+ *  @return Amount of items in the tree.
+ * ****************************************************************************/
+uint16_t
+pico_tree_count( struct pico_tree *tree );
+
+/* ****************************************************************************
+ *  Definition of DNS question tree
+ * ****************************************************************************/
+typedef struct pico_tree pico_dns_qtree;
+#define PICO_DNS_QTREE_DECLARE(name) \
+    pico_dns_qtree (name) = {&LEAF, pico_dns_question_cmp}
+#define PICO_DNS_QTREE_DESTROY(qtree) \
+    pico_tree_destroy(qtree, pico_dns_question_delete)
+
+/* ****************************************************************************
+ *  Deletes all the questions with given DNS name from a pico_tree
+ *
+ *  @param qtree Pointer to pico_tree-instance which contains DNS questions
+ *  @param name  Name of the questions you want to delete
+ *  @return Returns 0 on success, something else on failure.
+ * ****************************************************************************/
+int
+pico_dns_qtree_del_name( struct pico_tree *qtree,
+                         const char *name );
+
+/* ****************************************************************************
+ *  Checks whether a question with given name is in the tree or not.
+ *
+ *  @param qtree Pointer to pico_tree-instance which contains DNS questions
+ *  @param name  Name you want to check for
+ *  @return 1 when the name is present in the qtree, 0 when it's not.
+ * ****************************************************************************/
+int
+pico_dns_qtree_find_name( struct pico_tree *qtree,
+                          const char *name );
+
+/* ****************************************************************************
+ *  Definition of DNS record tree
+ * ****************************************************************************/
+typedef struct pico_tree pico_dns_rtree;
+#define PICO_DNS_RTREE_DECLARE(name) \
+    pico_dns_rtree (name) = {&LEAF, pico_dns_record_cmp}
+#define PICO_DNS_RTREE_DESTROY(rtree) \
+    pico_tree_destroy((rtree), pico_dns_record_delete)
+
+/* MARK: DNS PACKET FUNCTIONS */
+
+/* ****************************************************************************
+ *  Fills the header section of a DNS packet with the correct flags and section
+ *  -counts.
+ *
+ *  @param hdr     Header to fill in.
+ *  @param qdcount Amount of questions added to the packet
+ *  @param ancount Amount of answer records added to the packet
+ *  @param nscount Amount of authority records added to the packet
+ *  @param arcount Amount of additional records added to the packet
+ * ****************************************************************************/
+void
+pico_dns_fill_packet_header( struct pico_dns_header *hdr,
+                             uint16_t qdcount,
+                             uint16_t ancount,
+                             uint16_t authcount,
+                             uint16_t addcount );
+
+/* ****************************************************************************
+ *  Creates a DNS Query packet with given question and resource records to put
+ *  the Resource Record Sections. If a NULL-pointer is provided for a certain
+ *  tree, no records will be added to that particular section of the packet.
+ *
+ *  @param qtree  DNS Questions to put in the Question Section
+ *  @param antree DNS Records to put in the Answer Section
+ *  @param nstree DNS Records to put in the Authority Section
+ *  @param artree DNS Records to put in the Additional Section
+ *  @param len    Will get filled with the entire size of the packet
+ *  @return Pointer to created DNS packet
+ * ****************************************************************************/
+pico_dns_packet *
+pico_dns_query_create( struct pico_tree *qtree,
+                       struct pico_tree *antree,
+                       struct pico_tree *nstree,
+                       struct pico_tree *artree,
+                       uint16_t *len );
+
+/* ****************************************************************************
+ *  Creates a DNS Answer packet with given resource records to put in the
+ *  Resource Record Sections. If a NULL-pointer is provided for a certain tree,
+ *  no records will be added to that particular section of the packet.
+ *
+ *  @param antree DNS Records to put in the Answer Section
+ *  @param nstree DNS Records to put in the Authority Section
+ *  @param artree DNS Records to put in the Additional Section
+ *  @param len    Will get filled with the entire size of the packet
+ *  @return Pointer to created DNS packet.
+ * ****************************************************************************/
+pico_dns_packet *
+pico_dns_answer_create( struct pico_tree *antree,
+                        struct pico_tree *nstree,
+                        struct pico_tree *artree,
+                        uint16_t *len );
+
+#endif /* _INCLUDE_PICO_DNS_COMMON */
diff --git a/net/picotcp/modules/pico_dns_sd.h b/net/picotcp/modules/pico_dns_sd.h
new file mode 100644
index 0000000..77f8ef2
--- /dev/null
+++ b/net/picotcp/modules/pico_dns_sd.h
@@ -0,0 +1,90 @@
+/* ****************************************************************************
+ *  PicoTCP. Copyright (c) 2014 TASS Belgium NV. Some rights reserved.
+ *  See LICENSE and COPYING for usage.
+ *  .
+ *  Author: Jelle De Vleeschouwer
+ * ****************************************************************************/
+#ifndef INCLUDE_PICO_DNS_SD
+#define INCLUDE_PICO_DNS_SD
+
+#include "pico_mdns.h"
+
+typedef struct
+{
+    char *key;
+    char *value;
+} key_value_pair_t;
+
+typedef struct
+{
+    key_value_pair_t **pairs;
+    uint16_t count;
+} kv_vector;
+
+#define PICO_DNS_SD_KV_VECTOR_DECLARE(name) \
+    kv_vector (name) = {0}
+
+/* ****************************************************************************
+ *  Just calls pico_mdns_init in it's turn to initialise the mDNS-module.
+ *  See pico_mdns.h for description.
+ * ****************************************************************************/
+int
+pico_dns_sd_init( const char *_hostname,
+                  struct pico_ip4 address,
+                  void (*callback)(pico_mdns_rtree *,
+                                   char *,
+                                   void *),
+                  void *arg );
+
+/* ****************************************************************************
+ *  Register a DNS-SD service via Multicast DNS on the local network.
+ *
+ *  @param name     Instance Name of the service, f.e. "Printer 2nd Floor".
+ *  @param type     ServiceType of the service, f.e. "_http._tcp".
+ *  @param port     Port number on which the service runs.
+ *  @param txt_data TXT data to create TXT record with, need kv_vector-type,
+ *                  Declare such a type with PICO_DNS_SD_KV_VECTOR_DECLARE(*) &
+ *                  add key-value pairs with pico_dns_sd_kv_vector_add().
+ *  @param ttl      TTL
+ *  @param callback Callback-function to call when the service is registered.
+ *  @return
+ * ****************************************************************************/
+int
+pico_dns_sd_register_service( const char *name,
+                              const char *type,
+                              uint16_t port,
+                              kv_vector *txt_data,
+                              uint16_t ttl,
+                              void (*callback)(pico_mdns_rtree *,
+                                               char *,
+                                               void *),
+                              void *arg);
+
+/* ****************************************************************************
+ *  Does nothing for now.
+ *
+ *  @param type     Type to browse for.
+ *  @param callback Callback to call when something particular happens.
+ *  @return When the module successfully started browsing the servicetype.
+ * ****************************************************************************/
+int
+pico_dns_sd_browse_service( const char *type,
+                            void (*callback)(pico_mdns_rtree *,
+                                             char *,
+                                             void *),
+                            void *arg );
+
+/* ****************************************************************************
+ *  Add a key-value pair the a key-value pair vector.
+ *
+ *  @param vector Vector to add the pair to.
+ *  @param key    Key of the pair, cannot be NULL.
+ *  @param value  Value of the pair, can be NULL, empty ("") or filled ("qkejq")
+ *  @return Returns 0 when the pair is added successfully, something else on
+ *			failure.
+ * ****************************************************************************/
+int
+pico_dns_sd_kv_vector_add( kv_vector *vector, char *key, char *value );
+
+
+#endif /* _INCLUDE_PICO_DNS_SD */
\ No newline at end of file
diff --git a/net/picotcp/modules/pico_fragments.c b/net/picotcp/modules/pico_fragments.c
new file mode 100644
index 0000000..e6fe44b
--- /dev/null
+++ b/net/picotcp/modules/pico_fragments.c
@@ -0,0 +1,345 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   Authors: Laurens Miers, Daniele Lacamera
+ *********************************************************************/
+
+
+#include "pico_config.h"
+#ifdef PICO_SUPPORT_IPV6
+#include "pico_ipv6.h"
+#include "pico_icmp6.h"
+#endif
+#ifdef PICO_SUPPORT_IPV4
+#include "pico_ipv4.h"
+#include "pico_icmp4.h"
+#endif
+#include "pico_stack.h"
+#include "pico_eth.h"
+#include "pico_udp.h"
+#include "pico_tcp.h"
+#include "pico_socket.h"
+#include "pico_device.h"
+#include "pico_tree.h"
+#include "pico_constants.h"
+#include "pico_fragments.h"
+#define frag_dbg(...) do {} while(0)
+
+#define IP6_FRAG_OFF(x)         ((x & 0xFFF8u))
+#define IP6_FRAG_MORE(x)        ((x & 0x0001))
+#define IP6_FRAG_ID(x)          ((uint32_t)((x->ext.frag.id[0] << 24) + (x->ext.frag.id[1] << 16) + \
+                                            (x->ext.frag.id[2] << 8) + x->ext.frag.id[3]))
+
+#define IP4_FRAG_OFF(frag)      (((uint32_t)frag & PICO_IPV4_FRAG_MASK) << 3ul)
+#define IP4_FRAG_MORE(frag)     ((frag & PICO_IPV4_MOREFRAG) ? 1 : 0)
+#define IP4_FRAG_ID(hdr)        (hdr->id)
+
+#define FRAG_OFF(net, frag)     ((net == PICO_PROTO_IPV4) ? (IP4_FRAG_OFF(frag)) : (IP6_FRAG_OFF(frag)))
+#define FRAG_MORE(net, frag)    ((net == PICO_PROTO_IPV4) ? (IP4_FRAG_MORE(frag)) : (IP6_FRAG_MORE(frag)))
+
+#define PICO_IPV6_FRAG_TIMEOUT   60000
+#define PICO_IPV4_FRAG_TIMEOUT   15000
+
+static int pico_ipv6_frag_compare(void *ka, void *kb);
+static int pico_ipv4_frag_compare(void *ka, void *kb);
+static void pico_ipv6_fragments_complete(unsigned int len, uint8_t proto);
+static void pico_ipv4_fragments_complete(unsigned int len, uint8_t proto);
+static void pico_fragments_complete(unsigned int bookmark, uint8_t proto, uint8_t net);
+static int pico_fragments_check_complete(uint8_t proto, uint8_t net);
+static void pico_frag_expire(pico_time now, void *arg);
+static void pico_ipv6_frag_timer_on(void);
+static void pico_ipv4_frag_timer_on(void);
+static int pico_ipv6_frag_match(struct pico_frame *a, struct pico_frame *b);
+static int pico_ipv4_frag_match(struct pico_frame *a, struct pico_frame *b);
+
+static uint32_t ipv6_cur_frag_id = 0u;
+static uint32_t ipv4_cur_frag_id = 0u;
+
+static int pico_ipv6_frag_compare(void *ka, void *kb)
+{
+    struct pico_frame *a = ka, *b = kb;
+    if (IP6_FRAG_OFF(a->frag) > IP6_FRAG_OFF(b->frag))
+        return 1;
+
+    if (IP6_FRAG_OFF(a->frag) < IP6_FRAG_OFF(b->frag))
+        return -1;
+
+    return 0;
+}
+PICO_TREE_DECLARE(ipv6_fragments, pico_ipv6_frag_compare);
+
+struct pico_timer *ipv6_fragments_timer = NULL;
+
+static int pico_ipv4_frag_compare(void *ka, void *kb)
+{
+    struct pico_frame *a = ka, *b = kb;
+    if (IP4_FRAG_OFF(a->frag) > IP4_FRAG_OFF(b->frag))
+        return 1;
+
+    if (IP4_FRAG_OFF(a->frag) < IP4_FRAG_OFF(b->frag))
+        return -1;
+
+    return 0;
+}
+PICO_TREE_DECLARE(ipv4_fragments, pico_ipv4_frag_compare);
+
+struct pico_timer *ipv4_fragments_timer = NULL;
+
+static void pico_ipv6_fragments_complete(unsigned int len, uint8_t proto)
+{
+    struct pico_tree_node *index, *tmp;
+    struct pico_frame *f;
+    unsigned int bookmark = 0;
+    struct pico_frame *full = NULL;
+    struct pico_frame *first = pico_tree_first(&ipv6_fragments);
+
+    full = pico_frame_alloc((uint16_t)(PICO_SIZE_IP6HDR + len));
+    if (full) {
+        full->net_hdr = full->buffer;
+        full->net_len = PICO_SIZE_IP6HDR;
+        memcpy(full->net_hdr, first->net_hdr, full->net_len);
+        full->transport_hdr = full->net_hdr + full->net_len;
+        full->transport_len = (uint16_t)len;
+        full->dev = first->dev;
+        pico_tree_foreach_safe(index, &ipv6_fragments, tmp) {
+            f = index->keyValue;
+            memcpy(full->transport_hdr + bookmark, f->transport_hdr, f->transport_len);
+            bookmark += f->transport_len;
+            pico_tree_delete(&ipv6_fragments, f);
+            pico_frame_discard(f);
+        }
+        pico_transport_receive(full, proto);
+        if (ipv6_fragments_timer) {
+            pico_timer_cancel(ipv6_fragments_timer);
+            ipv6_fragments_timer = NULL;
+        }
+    }
+}
+
+static void pico_ipv4_fragments_complete(unsigned int len, uint8_t proto)
+{
+    struct pico_tree_node *index, *tmp;
+    struct pico_frame *f;
+    unsigned int bookmark = 0;
+    struct pico_frame *full = NULL;
+    struct pico_frame *first = pico_tree_first(&ipv4_fragments);
+
+    full = pico_frame_alloc((uint16_t)(PICO_SIZE_IP4HDR + len));
+    if (full) {
+        full->net_hdr = full->buffer;
+        full->net_len = PICO_SIZE_IP4HDR;
+        memcpy(full->net_hdr, first->net_hdr, full->net_len);
+        full->transport_hdr = full->net_hdr + full->net_len;
+        full->transport_len = (uint16_t)len;
+        full->dev = first->dev;
+        pico_tree_foreach_safe(index, &ipv4_fragments, tmp) {
+            f = index->keyValue;
+            memcpy(full->transport_hdr + bookmark, f->transport_hdr, f->transport_len);
+            bookmark += f->transport_len;
+            pico_tree_delete(&ipv4_fragments, f);
+            pico_frame_discard(f);
+        }
+        pico_transport_receive(full, proto);
+        if (ipv4_fragments_timer) {
+            pico_timer_cancel(ipv4_fragments_timer);
+            ipv4_fragments_timer = NULL;
+        }
+    }
+}
+
+static void pico_fragments_complete(unsigned int bookmark, uint8_t proto, uint8_t net)
+{
+    if (net == PICO_PROTO_IPV4)
+    {
+        pico_ipv4_fragments_complete(bookmark, proto);
+    }
+    else
+    {
+        pico_ipv6_fragments_complete(bookmark, proto);
+    }
+}
+
+static int pico_fragments_check_complete(uint8_t proto, uint8_t net)
+{
+    struct pico_tree_node *index, *temp;
+    struct pico_frame *cur;
+    unsigned int bookmark = 0;
+    struct pico_tree *tree = NULL;
+
+    if (net == PICO_PROTO_IPV4)
+    {
+        tree = &ipv4_fragments;
+    }
+    else
+    {
+        tree = &ipv6_fragments;
+    }
+
+
+    pico_tree_foreach_safe(index, tree, temp) {
+        cur = index->keyValue;
+        if (FRAG_OFF(net, cur->frag) != bookmark)
+            return 0;
+
+        bookmark += cur->transport_len;
+        if (!FRAG_MORE(net, cur->frag)) {
+            pico_fragments_complete(bookmark, proto, net);
+            return 1;
+        }
+    }
+    return 0;
+}
+
+static void pico_frag_expire(pico_time now, void *arg)
+{
+    struct pico_tree_node *index, *tmp;
+    struct pico_frame *f = NULL;
+    struct pico_tree *tree = (struct pico_tree *) arg;
+    struct pico_frame *first = NULL;
+    uint8_t net = 0;
+    (void)now;
+
+    if (!tree)
+    {
+        frag_dbg("Expired packet but no tree supplied!\n");
+        return;
+    }
+
+    first = pico_tree_first(tree);
+
+    if (!first) {
+        frag_dbg("not first - not sending notify\n");
+        return;
+    }
+
+    if (IS_IPV4(first))
+    {
+        net = PICO_PROTO_IPV4;
+        frag_dbg("Packet expired! ID:%hu\n", ipv4_cur_frag_id);
+    }
+    else
+    {
+        net = PICO_PROTO_IPV6;
+        frag_dbg("Packet expired! ID:%hu\n", ipv6_cur_frag_id);
+    }
+
+    /* Empty the tree */
+    pico_tree_foreach_safe(index, tree, tmp) {
+        f = index->keyValue;
+        pico_tree_delete(tree, f);
+        if (f != first)
+            pico_frame_discard(f); /* Later, after ICMP notification...*/
+
+    }
+
+    if (((FRAG_OFF(net, first->frag) == 0) && (pico_frame_dst_is_unicast(first))))
+    {
+        frag_dbg("sending notify\n");
+        pico_notify_frag_expired(first);
+    }
+
+    if (f)
+        pico_tree_delete(tree, f);
+
+    pico_frame_discard(first);
+}
+
+static void pico_ipv6_frag_timer_on(void)
+{
+    ipv6_fragments_timer = pico_timer_add(PICO_IPV6_FRAG_TIMEOUT, pico_frag_expire, &ipv6_fragments);
+}
+
+static void pico_ipv4_frag_timer_on(void)
+{
+    ipv4_fragments_timer = pico_timer_add( PICO_IPV4_FRAG_TIMEOUT, pico_frag_expire, &ipv4_fragments);
+}
+
+
+static int pico_ipv6_frag_match(struct pico_frame *a, struct pico_frame *b)
+{
+    struct pico_ipv6_hdr *ha, *hb;
+    if (!a || !b)
+        return 0;
+
+    ha = (struct pico_ipv6_hdr *)a->net_hdr;
+    hb = (struct pico_ipv6_hdr *)b->net_hdr;
+    if (!ha || !hb)
+        return 0;
+
+    if (memcmp(ha->src.addr, hb->src.addr, PICO_SIZE_IP6) != 0)
+        return 0;
+
+    if (memcmp(ha->dst.addr, hb->dst.addr, PICO_SIZE_IP6) != 0)
+        return 0;
+
+    return 1;
+}
+
+static int pico_ipv4_frag_match(struct pico_frame *a, struct pico_frame *b)
+{
+    struct pico_ipv4_hdr *ha, *hb;
+    if (!a || !b)
+        return 0;
+
+    ha = (struct pico_ipv4_hdr *)a->net_hdr;
+    hb = (struct pico_ipv4_hdr *)b->net_hdr;
+    if (!ha || !hb)
+        return 0;
+
+    if (memcmp(&(ha->src.addr), &(hb->src.addr), PICO_SIZE_IP4) != 0)
+        return 0;
+
+    if (memcmp(&(ha->dst.addr), &(hb->dst.addr), PICO_SIZE_IP4) != 0)
+        return 0;
+
+    return 1;
+}
+
+
+void pico_ipv6_process_frag(struct pico_ipv6_exthdr *frag, struct pico_frame *f, uint8_t proto)
+{
+    struct pico_frame *first = pico_tree_first(&ipv6_fragments);
+
+    if (!first) {
+        if (ipv6_cur_frag_id && (IP6_FRAG_ID(frag) == ipv6_cur_frag_id)) {
+            /* Discard late arrivals, without firing the timer.
+             */
+            frag_dbg("discarded late arrival, exp:%hu found:%hu\n", ipv6_cur_frag_id, IP6_FRAG_ID(frag));
+            return;
+        }
+
+        pico_ipv6_frag_timer_on();
+        ipv6_cur_frag_id = IP6_FRAG_ID(frag);
+        frag_dbg("Started new reassembly, ID:%hu\n", ipv6_cur_frag_id);
+    }
+
+    if (!first || (pico_ipv6_frag_match(f, first) && (IP6_FRAG_ID(frag) == ipv6_cur_frag_id))) {
+        pico_tree_insert(&ipv6_fragments, pico_frame_copy(f));
+    }
+
+    pico_fragments_check_complete(proto, PICO_PROTO_IPV6);
+}
+
+void pico_ipv4_process_frag(struct pico_ipv4_hdr *hdr, struct pico_frame *f, uint8_t proto)
+{
+    struct pico_frame *first = pico_tree_first(&ipv4_fragments);
+
+    f->frag = short_be(hdr->frag);
+    if (!first) {
+        if (ipv4_cur_frag_id && (IP4_FRAG_ID(hdr) == ipv4_cur_frag_id)) {
+            /* Discard late arrivals, without firing the timer,
+             */
+            return;
+        }
+
+        pico_ipv4_frag_timer_on();
+        ipv4_cur_frag_id = IP4_FRAG_ID(hdr);
+    }
+
+    if (!first || (pico_ipv4_frag_match(f, first) && (IP4_FRAG_ID(hdr) == ipv4_cur_frag_id))) {
+        pico_tree_insert(&ipv4_fragments, pico_frame_copy(f));
+    }
+
+    pico_fragments_check_complete(proto, PICO_PROTO_IPV4);
+}
diff --git a/net/picotcp/modules/pico_fragments.h b/net/picotcp/modules/pico_fragments.h
new file mode 100644
index 0000000..e51ec44
--- /dev/null
+++ b/net/picotcp/modules/pico_fragments.h
@@ -0,0 +1,11 @@
+#ifndef PICO_FRAGMENTS_H
+#define PICO_FRAGMENTS_H
+#include "pico_ipv4.h"
+#include "pico_ipv6.h"
+#include "pico_addressing.h"
+#include "pico_frame.h"
+
+void pico_ipv6_process_frag(struct pico_ipv6_exthdr *frag, struct pico_frame *f, uint8_t proto);
+void pico_ipv4_process_frag(struct pico_ipv4_hdr *hdr, struct pico_frame *f, uint8_t proto);
+
+#endif
diff --git a/net/picotcp/modules/pico_icmp4.c b/net/picotcp/modules/pico_icmp4.c
new file mode 100644
index 0000000..da3d89c
--- /dev/null
+++ b/net/picotcp/modules/pico_icmp4.c
@@ -0,0 +1,395 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   .
+
+   Authors: Daniele Lacamera
+ *********************************************************************/
+
+
+#include "pico_icmp4.h"
+#include "pico_config.h"
+#include "pico_ipv4.h"
+#include "pico_eth.h"
+#include "pico_device.h"
+#include "pico_stack.h"
+#include "pico_tree.h"
+
+/* Queues */
+static struct pico_queue icmp_in = {
+    0
+};
+static struct pico_queue icmp_out = {
+    0
+};
+
+
+/* Functions */
+
+static int pico_icmp4_checksum(struct pico_frame *f)
+{
+    struct pico_icmp4_hdr *hdr = (struct pico_icmp4_hdr *) f->transport_hdr;
+    if (!hdr) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    hdr->crc = 0;
+    hdr->crc = short_be(pico_checksum(hdr, f->transport_len));
+    return 0;
+}
+
+#ifdef PICO_SUPPORT_PING
+static void ping_recv_reply(struct pico_frame *f);
+#endif
+
+static int pico_icmp4_process_in(struct pico_protocol *self, struct pico_frame *f)
+{
+    struct pico_icmp4_hdr *hdr = (struct pico_icmp4_hdr *) f->transport_hdr;
+    static int firstpkt = 1;
+    static uint16_t last_id = 0;
+    static uint16_t last_seq = 0;
+    IGNORE_PARAMETER(self);
+
+    if (hdr->type == PICO_ICMP_ECHO) {
+        hdr->type = PICO_ICMP_ECHOREPLY;
+        /* outgoing frames require a f->len without the ethernet header len */
+        if (f->dev && f->dev->eth)
+            f->len -= PICO_SIZE_ETHHDR;
+
+        if (!firstpkt && (hdr->hun.ih_idseq.idseq_id ==  last_id) && (last_seq == hdr->hun.ih_idseq.idseq_seq)) {
+            /* The network duplicated the echo. Do not reply. */
+            pico_frame_discard(f);
+            return 0;
+        }
+
+        firstpkt = 0;
+        last_id = hdr->hun.ih_idseq.idseq_id;
+        last_seq = hdr->hun.ih_idseq.idseq_seq;
+        pico_icmp4_checksum(f);
+        pico_ipv4_rebound(f);
+    } else if (hdr->type == PICO_ICMP_UNREACH) {
+        f->net_hdr = f->transport_hdr + PICO_ICMPHDR_UN_SIZE;
+        pico_ipv4_unreachable(f, hdr->code);
+    } else if (hdr->type == PICO_ICMP_ECHOREPLY) {
+#ifdef PICO_SUPPORT_PING
+        ping_recv_reply(f);
+#endif
+        pico_frame_discard(f);
+    } else {
+        pico_frame_discard(f);
+    }
+
+    return 0;
+}
+
+static int pico_icmp4_process_out(struct pico_protocol *self, struct pico_frame *f)
+{
+    IGNORE_PARAMETER(self);
+    IGNORE_PARAMETER(f);
+    dbg("Called %s\n", __FUNCTION__);
+    return 0;
+}
+
+/* Interface: protocol definition */
+struct pico_protocol pico_proto_icmp4 = {
+    .name = "icmp4",
+    .proto_number = PICO_PROTO_ICMP4,
+    .layer = PICO_LAYER_TRANSPORT,
+    .process_in = pico_icmp4_process_in,
+    .process_out = pico_icmp4_process_out,
+    .q_in = &icmp_in,
+    .q_out = &icmp_out,
+};
+
+static int pico_icmp4_notify(struct pico_frame *f, uint8_t type, uint8_t code)
+{
+    struct pico_frame *reply;
+    struct pico_icmp4_hdr *hdr;
+    struct pico_ipv4_hdr *info;
+    uint16_t f_tot_len;
+
+    f_tot_len = short_be(((struct pico_ipv4_hdr *)f->net_hdr)->len);
+
+    if (f_tot_len < (sizeof(struct pico_ipv4_hdr)))
+        return -1;
+
+    /* Truncate tot len to be at most 8 bytes + iphdr */
+    if (f_tot_len > (sizeof(struct pico_ipv4_hdr) + 8u)) {
+        f_tot_len = (sizeof(struct pico_ipv4_hdr) + 8u);
+    }
+
+    if (f == NULL) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    reply = pico_proto_ipv4.alloc(&pico_proto_ipv4, (uint16_t) (f_tot_len + PICO_ICMPHDR_UN_SIZE));
+    info = (struct pico_ipv4_hdr*)(f->net_hdr);
+    hdr = (struct pico_icmp4_hdr *) reply->transport_hdr;
+    hdr->type = type;
+    hdr->code = code;
+    hdr->hun.ih_pmtu.ipm_nmtu = short_be(1500);
+    hdr->hun.ih_pmtu.ipm_void = 0;
+    reply->transport_len = (uint16_t)(f_tot_len +  PICO_ICMPHDR_UN_SIZE);
+    reply->payload = reply->transport_hdr + PICO_ICMPHDR_UN_SIZE;
+    memcpy(reply->payload, f->net_hdr, f_tot_len);
+    pico_icmp4_checksum(reply);
+    pico_ipv4_frame_push(reply, &info->src, PICO_PROTO_ICMP4);
+    return 0;
+}
+
+int pico_icmp4_port_unreachable(struct pico_frame *f)
+{
+    /*Parameter check executed in pico_icmp4_notify*/
+    return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_PORT);
+}
+
+int pico_icmp4_proto_unreachable(struct pico_frame *f)
+{
+    /*Parameter check executed in pico_icmp4_notify*/
+    return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_PROTOCOL);
+}
+
+int pico_icmp4_dest_unreachable(struct pico_frame *f)
+{
+    /*Parameter check executed in pico_icmp4_notify*/
+    return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_HOST);
+}
+
+int pico_icmp4_ttl_expired(struct pico_frame *f)
+{
+    /*Parameter check executed in pico_icmp4_notify*/
+    return pico_icmp4_notify(f, PICO_ICMP_TIME_EXCEEDED, PICO_ICMP_TIMXCEED_INTRANS);
+}
+
+MOCKABLE int pico_icmp4_frag_expired(struct pico_frame *f)
+{
+    /*Parameter check executed in pico_icmp4_notify*/
+    return pico_icmp4_notify(f, PICO_ICMP_TIME_EXCEEDED, PICO_ICMP_TIMXCEED_REASS);
+}
+
+int pico_icmp4_mtu_exceeded(struct pico_frame *f)
+{
+    /*Parameter check executed in pico_icmp4_notify*/
+    return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_NEEDFRAG);
+}
+
+int pico_icmp4_packet_filtered(struct pico_frame *f)
+{
+    /*Parameter check executed in pico_icmp4_notify*/
+    /*Packet Filtered: type 3, code 13 (Communication Administratively Prohibited)*/
+    return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_FILTER_PROHIB);
+}
+
+int pico_icmp4_param_problem(struct pico_frame *f, uint8_t code)
+{
+    return pico_icmp4_notify(f, PICO_ICMP_PARAMPROB, code);
+}
+
+/***********************/
+/* Ping implementation */
+/***********************/
+/***********************/
+/***********************/
+/***********************/
+
+
+#ifdef PICO_SUPPORT_PING
+
+
+struct pico_icmp4_ping_cookie
+{
+    struct pico_ip4 dst;
+    uint16_t err;
+    uint16_t id;
+    uint16_t seq;
+    uint16_t size;
+    int count;
+    pico_time timestamp;
+    int interval;
+    int timeout;
+    void (*cb)(struct pico_icmp4_stats*);
+};
+
+static int cookie_compare(void *ka, void *kb)
+{
+    struct pico_icmp4_ping_cookie *a = ka, *b = kb;
+    if (a->id < b->id)
+        return -1;
+
+    if (a->id > b->id)
+        return 1;
+
+    return (a->seq - b->seq);
+}
+
+PICO_TREE_DECLARE(Pings, cookie_compare);
+
+static int8_t pico_icmp4_send_echo(struct pico_icmp4_ping_cookie *cookie)
+{
+    struct pico_frame *echo = pico_proto_ipv4.alloc(&pico_proto_ipv4, (uint16_t)(PICO_ICMPHDR_UN_SIZE + cookie->size));
+    struct pico_icmp4_hdr *hdr;
+    if (!echo) {
+        return -1;
+    }
+
+    hdr = (struct pico_icmp4_hdr *) echo->transport_hdr;
+
+    hdr->type = PICO_ICMP_ECHO;
+    hdr->code = 0;
+    hdr->hun.ih_idseq.idseq_id = short_be(cookie->id);
+    hdr->hun.ih_idseq.idseq_seq = short_be(cookie->seq);
+    echo->transport_len = (uint16_t)(PICO_ICMPHDR_UN_SIZE + cookie->size);
+    echo->payload = echo->transport_hdr + PICO_ICMPHDR_UN_SIZE;
+    echo->payload_len = cookie->size;
+    /* XXX: Fill payload */
+    pico_icmp4_checksum(echo);
+    pico_ipv4_frame_push(echo, &cookie->dst, PICO_PROTO_ICMP4);
+    return 0;
+}
+
+
+static void ping_timeout(pico_time now, void *arg)
+{
+    struct pico_icmp4_ping_cookie *cookie = (struct pico_icmp4_ping_cookie *)arg;
+    IGNORE_PARAMETER(now);
+
+    if(pico_tree_findKey(&Pings, cookie)) {
+        if (cookie->err == PICO_PING_ERR_PENDING) {
+            struct pico_icmp4_stats stats;
+            stats.dst = cookie->dst;
+            stats.seq = cookie->seq;
+            stats.time = 0;
+            stats.size = cookie->size;
+            stats.err = PICO_PING_ERR_TIMEOUT;
+            dbg(" ---- Ping timeout!!!\n");
+            cookie->cb(&stats);
+        }
+
+        pico_tree_delete(&Pings, cookie);
+        PICO_FREE(cookie);
+    }
+}
+
+static void next_ping(pico_time now, void *arg);
+static inline void send_ping(struct pico_icmp4_ping_cookie *cookie)
+{
+    pico_icmp4_send_echo(cookie);
+    cookie->timestamp = pico_tick;
+    pico_timer_add((uint32_t)cookie->timeout, ping_timeout, cookie);
+    if (cookie->seq < (uint16_t)cookie->count)
+        pico_timer_add((uint32_t)cookie->interval, next_ping, cookie);
+}
+
+static void next_ping(pico_time now, void *arg)
+{
+    struct pico_icmp4_ping_cookie *newcookie, *cookie = (struct pico_icmp4_ping_cookie *)arg;
+    IGNORE_PARAMETER(now);
+
+    if(pico_tree_findKey(&Pings, cookie)) {
+        if (cookie->err == PICO_PING_ERR_ABORTED)
+            return;
+
+        if (cookie->seq < (uint16_t)cookie->count) {
+            newcookie = PICO_ZALLOC(sizeof(struct pico_icmp4_ping_cookie));
+            if (!newcookie)
+                return;
+
+            memcpy(newcookie, cookie, sizeof(struct pico_icmp4_ping_cookie));
+            newcookie->seq++;
+
+            pico_tree_insert(&Pings, newcookie);
+            send_ping(newcookie);
+        }
+    }
+}
+
+
+static void ping_recv_reply(struct pico_frame *f)
+{
+    struct pico_icmp4_ping_cookie test, *cookie;
+    struct pico_icmp4_hdr *hdr = (struct pico_icmp4_hdr *) f->transport_hdr;
+    test.id  = short_be(hdr->hun.ih_idseq.idseq_id );
+    test.seq = short_be(hdr->hun.ih_idseq.idseq_seq);
+
+    cookie = pico_tree_findKey(&Pings, &test);
+    if (cookie) {
+        struct pico_icmp4_stats stats;
+        if (cookie->err == PICO_PING_ERR_ABORTED)
+            return;
+
+        cookie->err = PICO_PING_ERR_REPLIED;
+        stats.dst = ((struct pico_ipv4_hdr *)f->net_hdr)->src;
+        stats.seq = cookie->seq;
+        stats.size = cookie->size;
+        stats.time = pico_tick - cookie->timestamp;
+        stats.err = cookie->err;
+        stats.ttl = ((struct pico_ipv4_hdr *)f->net_hdr)->ttl;
+        if(cookie->cb != NULL)
+            cookie->cb(&stats);
+    } else {
+        dbg("Reply for seq=%d, not found.\n", test.seq);
+    }
+}
+
+int pico_icmp4_ping(char *dst, int count, int interval, int timeout, int size, void (*cb)(struct pico_icmp4_stats *))
+{
+    static uint16_t next_id = 0x91c0;
+    struct pico_icmp4_ping_cookie *cookie;
+
+    if((dst == NULL) || (interval == 0) || (timeout == 0) || (count == 0)) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    cookie = PICO_ZALLOC(sizeof(struct pico_icmp4_ping_cookie));
+    if (!cookie) {
+        pico_err = PICO_ERR_ENOMEM;
+        return -1;
+    }
+
+    if (pico_string_to_ipv4(dst, (uint32_t *)&cookie->dst.addr) < 0) {
+        pico_err = PICO_ERR_EINVAL;
+        PICO_FREE(cookie);
+        return -1;
+    }
+
+    cookie->seq = 1;
+    cookie->id = next_id++;
+    cookie->err = PICO_PING_ERR_PENDING;
+    cookie->size = (uint16_t)size;
+    cookie->interval = interval;
+    cookie->timeout = timeout;
+    cookie->cb = cb;
+    cookie->count = count;
+
+    pico_tree_insert(&Pings, cookie);
+    send_ping(cookie);
+
+    return cookie->id;
+
+}
+
+int pico_icmp4_ping_abort(int id)
+{
+    struct pico_tree_node *node;
+    int found = 0;
+    pico_tree_foreach(node, &Pings)
+    {
+        struct pico_icmp4_ping_cookie *ck =
+            (struct pico_icmp4_ping_cookie *) node->keyValue;
+        if (ck->id == (uint16_t)id) {
+            ck->err = PICO_PING_ERR_ABORTED;
+            found++;
+        }
+    }
+    if (found > 0)
+        return 0; /* OK if at least one pending ping has been canceled */
+
+    pico_err = PICO_ERR_ENOENT;
+    return -1;
+}
+
+#endif
diff --git a/net/picotcp/modules/pico_icmp4.h b/net/picotcp/modules/pico_icmp4.h
new file mode 100644
index 0000000..45f0a62
--- /dev/null
+++ b/net/picotcp/modules/pico_icmp4.h
@@ -0,0 +1,162 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   .
+
+ *********************************************************************/
+#ifndef INCLUDE_PICO_ICMP4
+#define INCLUDE_PICO_ICMP4
+#include "pico_defines.h"
+#include "pico_addressing.h"
+#include "pico_protocol.h"
+
+
+extern struct pico_protocol pico_proto_icmp4;
+
+PACKED_STRUCT_DEF pico_icmp4_hdr {
+    uint8_t type;
+    uint8_t code;
+    uint16_t crc;
+
+    /* hun */
+    PACKED_UNION_DEF hun_u {
+        uint8_t ih_pptr;
+        struct pico_ip4 ih_gwaddr;
+        PEDANTIC_STRUCT_DEF ih_idseq_s {
+            uint16_t idseq_id;
+            uint16_t idseq_seq;
+        } ih_idseq;
+        uint32_t ih_void;
+        PEDANTIC_STRUCT_DEF ih_pmtu_s {
+            uint16_t ipm_void;
+            uint16_t ipm_nmtu;
+        } ih_pmtu;
+        PEDANTIC_STRUCT_DEF ih_rta_s {
+            uint8_t rta_numgw;
+            uint8_t rta_wpa;
+            uint16_t rta_lifetime;
+        } ih_rta;
+    } hun;
+
+    /* dun */
+    PACKED_UNION_DEF dun_u {
+        PEDANTIC_STRUCT_DEF id_ts_s {
+            uint32_t ts_otime;
+            uint32_t ts_rtime;
+            uint32_t ts_ttime;
+        } id_ts;
+        PEDANTIC_STRUCT_DEF id_ip_s {
+            uint32_t ip_options;
+            uint32_t ip_data_hi;
+            uint32_t ip_data_lo;
+        } id_ip;
+        PEDANTIC_STRUCT_DEF id_ra_s {
+            uint32_t ira_addr;
+            uint32_t ira_pref;
+        } id_ra;
+        uint32_t id_mask;
+        uint8_t id_data[1];
+    } dun;
+};
+
+#define PICO_ICMPHDR_DRY_SIZE  4
+#define PICO_ICMPHDR_UN_SIZE  8u
+
+#define PICO_ICMP_ECHOREPLY    0
+#define PICO_ICMP_DEST_UNREACH 3
+#define PICO_ICMP_SOURCE_QUENCH  4
+#define PICO_ICMP_REDIRECT   5
+#define PICO_ICMP_ECHO   8
+#define PICO_ICMP_TIME_EXCEEDED  11
+#define PICO_ICMP_PARAMETERPROB  12
+#define PICO_ICMP_TIMESTAMP    13
+#define PICO_ICMP_TIMESTAMPREPLY 14
+#define PICO_ICMP_INFO_REQUEST 15
+#define PICO_ICMP_INFO_REPLY   16
+#define PICO_ICMP_ADDRESS    17
+#define PICO_ICMP_ADDRESSREPLY 18
+
+
+#define  PICO_ICMP_UNREACH    3
+#define  PICO_ICMP_SOURCEQUENCH  4
+#define  PICO_ICMP_ROUTERADVERT  9
+#define  PICO_ICMP_ROUTERSOLICIT  10
+#define  PICO_ICMP_TIMXCEED    11
+#define  PICO_ICMP_PARAMPROB    12
+#define  PICO_ICMP_TSTAMP    13
+#define  PICO_ICMP_TSTAMPREPLY  14
+#define  PICO_ICMP_IREQ    15
+#define  PICO_ICMP_IREQREPLY    16
+#define  PICO_ICMP_MASKREQ    17
+#define  PICO_ICMP_MASKREPLY    18
+
+#define  PICO_ICMP_MAXTYPE    18
+
+
+#define  PICO_ICMP_UNREACH_NET          0
+#define  PICO_ICMP_UNREACH_HOST          1
+#define  PICO_ICMP_UNREACH_PROTOCOL          2
+#define  PICO_ICMP_UNREACH_PORT          3
+#define  PICO_ICMP_UNREACH_NEEDFRAG          4
+#define  PICO_ICMP_UNREACH_SRCFAIL          5
+#define  PICO_ICMP_UNREACH_NET_UNKNOWN        6
+#define  PICO_ICMP_UNREACH_HOST_UNKNOWN       7
+#define  PICO_ICMP_UNREACH_ISOLATED          8
+#define  PICO_ICMP_UNREACH_NET_PROHIB          9
+#define  PICO_ICMP_UNREACH_HOST_PROHIB        10
+#define  PICO_ICMP_UNREACH_TOSNET          11
+#define  PICO_ICMP_UNREACH_TOSHOST          12
+#define  PICO_ICMP_UNREACH_FILTER_PROHIB      13
+#define  PICO_ICMP_UNREACH_HOST_PRECEDENCE    14
+#define  PICO_ICMP_UNREACH_PRECEDENCE_CUTOFF  15
+
+
+#define  PICO_ICMP_REDIRECT_NET  0
+#define  PICO_ICMP_REDIRECT_HOST  1
+#define  PICO_ICMP_REDIRECT_TOSNET  2
+#define  PICO_ICMP_REDIRECT_TOSHOST  3
+
+
+#define  PICO_ICMP_TIMXCEED_INTRANS  0
+#define  PICO_ICMP_TIMXCEED_REASS  1
+
+
+#define  PICO_ICMP_PARAMPROB_OPTABSENT 1
+
+#define PICO_SIZE_ICMP4HDR ((sizeof(struct pico_icmp4_hdr)))
+
+struct pico_icmp4_stats
+{
+    struct pico_ip4 dst;
+    unsigned long size;
+    unsigned long seq;
+    pico_time time;
+    unsigned long ttl;
+    int err;
+};
+
+int pico_icmp4_port_unreachable(struct pico_frame *f);
+int pico_icmp4_proto_unreachable(struct pico_frame *f);
+int pico_icmp4_dest_unreachable(struct pico_frame *f);
+int pico_icmp4_mtu_exceeded(struct pico_frame *f);
+int pico_icmp4_ttl_expired(struct pico_frame *f);
+int pico_icmp4_frag_expired(struct pico_frame *f);
+int pico_icmp4_ping(char *dst, int count, int interval, int timeout, int size, void (*cb)(struct pico_icmp4_stats *));
+int pico_icmp4_ping_abort(int id);
+
+#ifdef PICO_SUPPORT_ICMP4
+int pico_icmp4_packet_filtered(struct pico_frame *f);
+int pico_icmp4_param_problem(struct pico_frame *f, uint8_t code);
+#else
+# define pico_icmp4_packet_filtered(f) (-1)
+# define pico_icmp4_param_problem(f, c) (-1)
+#endif /* PICO_SUPPORT_ICMP4 */
+
+#define PICO_PING_ERR_REPLIED 0
+#define PICO_PING_ERR_TIMEOUT 1
+#define PICO_PING_ERR_UNREACH 2
+#define PICO_PING_ERR_ABORTED 3
+#define PICO_PING_ERR_PENDING 0xFFFF
+
+#endif
diff --git a/net/picotcp/modules/pico_icmp6.h b/net/picotcp/modules/pico_icmp6.h
new file mode 100644
index 0000000..90e8223
--- /dev/null
+++ b/net/picotcp/modules/pico_icmp6.h
@@ -0,0 +1,259 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   .
+
+ *********************************************************************/
+#ifndef _INCLUDE_PICO_ICMP6
+#define _INCLUDE_PICO_ICMP6
+#include "pico_addressing.h"
+#include "pico_protocol.h"
+
+/* ICMP header sizes */
+#define PICO_ICMP6HDR_DRY_SIZE          4
+#define PICO_ICMP6HDR_ECHO_REQUEST_SIZE 8
+#define PICO_ICMP6HDR_DEST_UNREACH_SIZE 8
+#define PICO_ICMP6HDR_TIME_XCEEDED_SIZE 8
+#define PICO_ICMP6HDR_PARAM_PROBLEM_SIZE 8
+#define PICO_ICMP6HDR_NEIGH_SOL_SIZE    24
+#define PICO_ICMP6HDR_NEIGH_ADV_SIZE    24
+#define PICO_ICMP6HDR_ROUTER_SOL_SIZE   8
+#define PICO_ICMP6HDR_ROUTER_ADV_SIZE   16
+#define PICO_ICMP6HDR_REDIRECT_SIZE     40
+
+/* ICMP types */
+#define PICO_ICMP6_DEST_UNREACH        1
+#define PICO_ICMP6_PKT_TOO_BIG         2
+#define PICO_ICMP6_TIME_EXCEEDED       3
+#define PICO_ICMP6_PARAM_PROBLEM       4
+#define PICO_ICMP6_ECHO_REQUEST        128
+#define PICO_ICMP6_ECHO_REPLY          129
+#define PICO_ICMP6_ROUTER_SOL          133
+#define PICO_ICMP6_ROUTER_ADV          134
+#define PICO_ICMP6_NEIGH_SOL           135
+#define PICO_ICMP6_NEIGH_ADV           136
+#define PICO_ICMP6_REDIRECT            137
+
+/* destination unreachable codes */
+#define PICO_ICMP6_UNREACH_NOROUTE     0
+#define PICO_ICMP6_UNREACH_ADMIN       1
+#define PICO_ICMP6_UNREACH_SRCSCOPE    2
+#define PICO_ICMP6_UNREACH_ADDR        3
+#define PICO_ICMP6_UNREACH_PORT        4
+#define PICO_ICMP6_UNREACH_SRCFILTER   5
+#define PICO_ICMP6_UNREACH_REJROUTE    6
+
+/* time exceeded codes */
+#define PICO_ICMP6_TIMXCEED_INTRANS    0
+#define PICO_ICMP6_TIMXCEED_REASS      1
+
+/* parameter problem codes */
+#define PICO_ICMP6_PARAMPROB_HDRFIELD  0
+#define PICO_ICMP6_PARAMPROB_NXTHDR    1
+#define PICO_ICMP6_PARAMPROB_IPV6OPT   2
+
+/* ping error codes */
+#define PICO_PING6_ERR_REPLIED         0
+#define PICO_PING6_ERR_TIMEOUT         1
+#define PICO_PING6_ERR_UNREACH         2
+#define PICO_PING6_ERR_ABORTED         3
+#define PICO_PING6_ERR_PENDING         0xFFFF
+
+/* ND configuration */
+#define PICO_ND_MAX_FRAMES_QUEUED      4 /* max frames queued while awaiting address resolution */
+
+/* ND RFC constants */
+#define PICO_ND_MAX_SOLICIT            3
+#define PICO_ND_MAX_NEIGHBOR_ADVERT    3
+#define PICO_ND_DELAY_INCOMPLETE       1000 /* msec */
+#define PICO_ND_DELAY_FIRST_PROBE_TIME 5000 /* msec */
+
+/* neighbor discovery options */
+#define PICO_ND_OPT_LLADDR_SRC         1
+#define PICO_ND_OPT_LLADDR_TGT         2
+#define PICO_ND_OPT_PREFIX             3
+#define PICO_ND_OPT_REDIRECT           4
+#define PICO_ND_OPT_MTU                5
+#define PICO_ND_OPT_RDNSS             25 /* RFC 5006 */
+
+/* ND advertisement flags */
+#define PICO_ND_ROUTER             0x80000000
+#define PICO_ND_SOLICITED          0x40000000
+#define PICO_ND_OVERRIDE           0x20000000
+#define IS_ROUTER(x) (long_be(x->msg.info.neigh_adv.rsor) & (PICO_ND_ROUTER))           /* router flag set? */
+#define IS_SOLICITED(x) (long_be(x->msg.info.neigh_adv.rsor) & (PICO_ND_SOLICITED))     /* solicited flag set? */
+#define IS_OVERRIDE(x) (long_be(x->msg.info.neigh_adv.rsor) & (PICO_ND_OVERRIDE))   /* override flag set? */
+
+#define PICO_ND_PREFIX_LIFETIME_INF    0xFFFFFFFFu
+/* #define PICO_ND_DESTINATION_LRU_TIME   600000u / * msecs (10min) * / */
+
+/* custom defines */
+#define PICO_ICMP6_ND_UNICAST          0
+#define PICO_ICMP6_ND_ANYCAST          1
+#define PICO_ICMP6_ND_SOLICITED        2
+#define PICO_ICMP6_ND_DAD              3
+
+#define PICO_ICMP6_MAX_RTR_SOL_DELAY   1000
+
+#define PICO_SIZE_ICMP6HDR ((sizeof(struct pico_icmp6_hdr)))
+#define PICO_ICMP6_OPT_LLADDR_SIZE (8)
+
+extern struct pico_protocol pico_proto_icmp6;
+
+PACKED_STRUCT_DEF pico_icmp6_hdr {
+    uint8_t type;
+    uint8_t code;
+    uint16_t crc;
+
+    PACKED_UNION_DEF icmp6_msg_u {
+        /* error messages */
+        PACKED_UNION_DEF icmp6_err_u {
+            PEDANTIC_STRUCT_DEF dest_unreach_s {
+                uint32_t unused;
+                uint8_t data[0];
+            } dest_unreach;
+            PEDANTIC_STRUCT_DEF pkt_too_big_s {
+                uint32_t mtu;
+                uint8_t data[0];
+            } pkt_too_big;
+            PEDANTIC_STRUCT_DEF time_exceeded_s {
+                uint32_t unused;
+                uint8_t data[0];
+            } time_exceeded;
+            PEDANTIC_STRUCT_DEF param_problem_s {
+                uint32_t ptr;
+                uint8_t data[0];
+            } param_problem;
+        } err;
+
+        /* informational messages */
+        PACKED_UNION_DEF icmp6_info_u {
+            PEDANTIC_STRUCT_DEF echo_request_s {
+                uint16_t id;
+                uint16_t seq;
+                uint8_t data[0];
+            } echo_request;
+            PEDANTIC_STRUCT_DEF echo_reply_s {
+                uint16_t id;
+                uint16_t seq;
+                uint8_t data[0];
+            } echo_reply;
+            PEDANTIC_STRUCT_DEF router_sol_s {
+                uint32_t unused;
+                uint8_t options[0];
+            } router_sol;
+            PEDANTIC_STRUCT_DEF router_adv_s {
+                uint8_t hop;
+                uint8_t mor;
+                uint16_t life_time;
+                uint32_t reachable_time;
+                uint32_t retrans_time;
+                uint8_t options[0];
+            } router_adv;
+            PEDANTIC_STRUCT_DEF neigh_sol_s {
+                uint32_t unused;
+                struct pico_ip6 target;
+                uint8_t options[0];
+            } neigh_sol;
+            PEDANTIC_STRUCT_DEF neigh_adv_s {
+                uint32_t rsor;
+                struct pico_ip6 target;
+                uint8_t options[0];
+            } neigh_adv;
+            PEDANTIC_STRUCT_DEF redirect_s {
+                uint32_t reserved;
+                struct pico_ip6 target;
+                struct pico_ip6 dest;
+                uint8_t options[0];
+            } redirect;
+        } info;
+    } msg;
+};
+
+PACKED_STRUCT_DEF pico_icmp6_opt_lladdr
+{
+    uint8_t type;
+    uint8_t len;
+    PACKED_UNION_DEF icmp6_opt_hw_addr_u {
+        struct pico_eth mac;
+    } addr;
+};
+
+PACKED_STRUCT_DEF pico_icmp6_opt_prefix
+{
+    uint8_t type;
+    uint8_t len;
+    uint8_t prefix_len;
+    uint8_t res : 6;
+    uint8_t aac : 1;
+    uint8_t onlink : 1;
+    uint32_t val_lifetime;
+    uint32_t pref_lifetime;
+    uint32_t reserved;
+    struct pico_ip6 prefix;
+};
+
+PACKED_STRUCT_DEF pico_icmp6_opt_mtu
+{
+    uint8_t type;
+    uint8_t len;
+    uint16_t res;
+    uint32_t mtu;
+};
+
+PACKED_STRUCT_DEF pico_icmp6_opt_redirect
+{
+    uint8_t type;
+    uint8_t len;
+    uint16_t res0;
+    uint32_t res1;
+    uint8_t data[0];
+};
+
+PACKED_STRUCT_DEF pico_icmp6_opt_rdnss
+{
+    uint8_t type;
+    uint8_t len;
+    uint16_t res0;
+    uint32_t lifetime;
+    struct pico_ip6 addr[];
+};
+
+PACKED_STRUCT_DEF pico_icmp6_opt_na
+{
+    uint8_t type;
+    uint8_t len;
+    uint8_t options[0];
+};
+
+struct pico_icmp6_stats
+{
+    unsigned long size;
+    unsigned long seq;
+    pico_time time;
+    unsigned long ttl;
+    int err;
+    struct pico_ip6 dst;
+};
+
+int pico_icmp6_ping(char *dst, int count, int interval, int timeout, int size, void (*cb)(struct pico_icmp6_stats *), struct pico_device *dev);
+int pico_icmp6_ping_abort(int id);
+
+int pico_icmp6_neighbor_solicitation(struct pico_device *dev, struct pico_ip6 *dst, uint8_t type);
+int pico_icmp6_neighbor_advertisement(struct pico_frame *f, struct pico_ip6 *target);
+int pico_icmp6_router_solicitation(struct pico_device *dev, struct pico_ip6 *src);
+
+int pico_icmp6_port_unreachable(struct pico_frame *f);
+int pico_icmp6_proto_unreachable(struct pico_frame *f);
+int pico_icmp6_dest_unreachable(struct pico_frame *f);
+int pico_icmp6_ttl_expired(struct pico_frame *f);
+int pico_icmp6_packet_filtered(struct pico_frame *f);
+int pico_icmp6_parameter_problem(struct pico_frame *f, uint8_t problem, uint32_t ptr);
+int pico_icmp6_pkt_too_big(struct pico_frame *f);
+int pico_icmp6_frag_expired(struct pico_frame *f);
+
+uint16_t pico_icmp6_checksum(struct pico_frame *f);
+int pico_icmp6_router_advertisement(struct pico_device *dev, struct pico_ip6 *dst);
+
+#endif
diff --git a/net/picotcp/modules/pico_igmp.h b/net/picotcp/modules/pico_igmp.h
new file mode 100644
index 0000000..03d6102
--- /dev/null
+++ b/net/picotcp/modules/pico_igmp.h
@@ -0,0 +1,26 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   .
+
+   Authors: Kristof Roelants, Simon Maes, Brecht Van Cauwenberghe
+ *********************************************************************/
+
+#ifndef INCLUDE_PICO_IGMP
+#define INCLUDE_PICO_IGMP
+
+#define PICO_IGMPV1               1
+#define PICO_IGMPV2               2
+#define PICO_IGMPV3               3
+
+#define PICO_IGMP_STATE_CREATE    1
+#define PICO_IGMP_STATE_UPDATE    2
+#define PICO_IGMP_STATE_DELETE    3
+
+#define PICO_IGMP_QUERY_INTERVAL  125
+
+extern struct pico_protocol pico_proto_igmp;
+
+int pico_igmp_state_change(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t filter_mode, struct pico_tree *MCASTFilter, uint8_t state);
+#endif /* _INCLUDE_PICO_IGMP */
diff --git a/net/picotcp/modules/pico_ipfilter.h b/net/picotcp/modules/pico_ipfilter.h
new file mode 100644
index 0000000..fb92e67
--- /dev/null
+++ b/net/picotcp/modules/pico_ipfilter.h
@@ -0,0 +1,29 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   Authors: Simon Maes
+ *********************************************************************/
+#ifndef INCLUDE_PICO_IPFILTER
+#define INCLUDE_PICO_IPFILTER
+
+#include "pico_device.h"
+
+enum filter_action {
+    FILTER_PRIORITY = 0,
+    FILTER_REJECT,
+    FILTER_DROP,
+    FILTER_COUNT
+};
+
+uint32_t pico_ipv4_filter_add(struct pico_device *dev, uint8_t proto,
+                              struct pico_ip4 *out_addr, struct pico_ip4 *out_addr_netmask, struct pico_ip4 *in_addr,
+                              struct pico_ip4 *in_addr_netmask, uint16_t out_port, uint16_t in_port,
+                              int8_t priority, uint8_t tos, enum filter_action action);
+
+int pico_ipv4_filter_del(uint32_t filter_id);
+
+int ipfilter(struct pico_frame *f);
+
+#endif /* _INCLUDE_PICO_IPFILTER */
+
diff --git a/net/picotcp/modules/pico_ipv4.c b/net/picotcp/modules/pico_ipv4.c
new file mode 100644
index 0000000..f94d6e6
--- /dev/null
+++ b/net/picotcp/modules/pico_ipv4.c
@@ -0,0 +1,1574 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   Authors: Daniele Lacamera, Markian Yskout
+ *********************************************************************/
+
+
+#include "pico_config.h"
+#include "pico_ipfilter.h"
+#include "pico_ipv4.h"
+#include "pico_icmp4.h"
+#include "pico_stack.h"
+#include "pico_eth.h"
+#include "pico_udp.h"
+#include "pico_tcp.h"
+#include "pico_socket.h"
+#include "pico_device.h"
+#include "pico_nat.h"
+#include "pico_igmp.h"
+#include "pico_tree.h"
+#include "pico_aodv.h"
+#include "pico_socket_multicast.h"
+#include "pico_fragments.h"
+
+#ifdef PICO_SUPPORT_IPV4
+
+#ifdef PICO_SUPPORT_MCAST
+# define ip_mcast_dbg(...) do {} while(0) /* so_mcast_dbg in pico_socket.c */
+/* #define ip_mcast_dbg dbg */
+# define PICO_MCAST_ALL_HOSTS long_be(0xE0000001) /* 224.0.0.1 */
+/* Default network interface for multicast transmission */
+static struct pico_ipv4_link *mcast_default_link = NULL;
+#endif
+#ifdef PICO_SUPPORT_IPFRAG
+/* # define reassembly_dbg dbg */
+# define reassembly_dbg(...) do {} while(0)
+#endif
+
+/* Queues */
+static struct pico_queue in = {
+    0
+};
+static struct pico_queue out = {
+    0
+};
+
+/* Functions */
+static int ipv4_route_compare(void *ka, void *kb);
+static struct pico_frame *pico_ipv4_alloc(struct pico_protocol *self, uint16_t size);
+
+
+int pico_ipv4_compare(struct pico_ip4 *a, struct pico_ip4 *b)
+{
+    if (a->addr < b->addr)
+        return -1;
+
+    if (a->addr > b->addr)
+        return 1;
+
+    return 0;
+}
+
+int pico_ipv4_to_string(char *ipbuf, const uint32_t ip)
+{
+    const unsigned char *addr = (const unsigned char *) &ip;
+    int i;
+
+    if (!ipbuf) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    for(i = 0; i < 4; i++)
+    {
+        if (addr[i] > 99) {
+            *ipbuf++ = (char)('0' + (addr[i] / 100));
+            *ipbuf++ = (char)('0' + ((addr[i] % 100) / 10));
+            *ipbuf++ = (char)('0' + ((addr[i] % 100) % 10));
+        } else if (addr[i] > 9) {
+            *ipbuf++ = (char)('0' + (addr[i] / 10));
+            *ipbuf++ = (char)('0' + (addr[i] % 10));
+        } else {
+            *ipbuf++ = (char)('0' + addr[i]);
+        }
+
+        if (i < 3)
+            *ipbuf++ = '.';
+    }
+    *ipbuf = '\0';
+
+    return 0;
+}
+
+static int pico_string_check_null_args(const char *ipstr, uint32_t *ip)
+{
+
+    if (!ipstr || !ip) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    return 0;
+
+}
+
+int pico_string_to_ipv4(const char *ipstr, uint32_t *ip)
+{
+    unsigned char buf[PICO_SIZE_IP4] = {
+        0
+    };
+    int cnt = 0;
+    char p;
+
+    if (pico_string_check_null_args(ipstr, ip) < 0)
+        return -1;
+
+    while((p = *ipstr++) != 0 && cnt < PICO_SIZE_IP4)
+    {
+        if (pico_is_digit(p)) {
+            buf[cnt] = (uint8_t)((10 * buf[cnt]) + (p - '0'));
+        } else if (p == '.') {
+            cnt++;
+        } else {
+            return -1;
+        }
+    }
+    /* Handle short notation */
+    if (cnt == 1) {
+        buf[3] = buf[1];
+        buf[1] = 0;
+        buf[2] = 0;
+    } else if (cnt == 2) {
+        buf[3] = buf[2];
+        buf[2] = 0;
+    } else if (cnt != 3) {
+        /* String could not be parsed, return error */
+        return -1;
+    }
+
+    *ip = long_from(buf);
+
+    return 0;
+}
+
+int pico_ipv4_valid_netmask(uint32_t mask)
+{
+    int cnt = 0;
+    int end = 0;
+    int i;
+    uint32_t mask_swap = long_be(mask);
+
+    /*
+     * Swap bytes for convenient parsing
+     * e.g. 0x..f8ff will become 0xfff8..
+     * Then, we count the consecutive bits
+     *
+     * */
+
+    for(i = 0; i < 32; i++) {
+        if ((mask_swap << i) & 0x80000000) {
+            if (end) {
+                pico_err = PICO_ERR_EINVAL;
+                return -1;
+            }
+
+            cnt++;
+        } else {
+            end = 1;
+        }
+    }
+    return cnt;
+}
+
+int pico_ipv4_is_unicast(uint32_t address)
+{
+    const unsigned char *addr = (unsigned char *) &address;
+    if ((addr[0] & 0xe0) == 0xe0)
+        return 0; /* multicast */
+
+    return 1;
+}
+
+int pico_ipv4_is_multicast(uint32_t address)
+{
+    const unsigned char *addr = (unsigned char *) &address;
+    if ((addr[0] != 0xff) && ((addr[0] & 0xe0) == 0xe0))
+        return 1; /* multicast */
+
+    return 0;
+}
+
+int pico_ipv4_is_loopback(uint32_t address)
+{
+    const unsigned char *addr = (unsigned char *) &address;
+    if (addr[0] == 0x7f)
+        return 1;
+
+    return 0;
+}
+
+static int pico_ipv4_is_invalid_loopback(uint32_t address, struct pico_device *dev)
+{
+    return pico_ipv4_is_loopback(address) && ((!dev) || strcmp(dev->name, "loop"));
+}
+
+int pico_ipv4_is_valid_src(uint32_t address, struct pico_device *dev)
+{
+    if (pico_ipv4_is_broadcast(address)) {
+        dbg("Source is a broadcast address, discard packet\n");
+        return 0;
+    } else if ( pico_ipv4_is_multicast(address)) {
+        dbg("Source is a multicast address, discard packet\n");
+        return 0;
+    } else if (pico_ipv4_is_invalid_loopback(address, dev)) {
+        dbg("Source is a loopback address, discard packet\n");
+        return 0;
+    } else {
+#ifdef PICO_SUPPORT_AODV
+        union pico_address src;
+        src.ip4.addr = address;
+        pico_aodv_refresh(&src);
+#endif
+        return 1;
+    }
+}
+
+static int pico_ipv4_checksum(struct pico_frame *f)
+{
+    struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+    if (!hdr)
+        return -1;
+
+    hdr->crc = 0;
+    hdr->crc = short_be(pico_checksum(hdr, f->net_len));
+    return 0;
+}
+
+
+#ifdef PICO_SUPPORT_CRC
+static inline int pico_ipv4_crc_check(struct pico_frame *f)
+{
+    uint16_t checksum_invalid = 1;
+    struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+
+    checksum_invalid = short_be(pico_checksum(hdr, f->net_len));
+    if (checksum_invalid) {
+        dbg("IP: checksum failed!\n");
+        pico_frame_discard(f);
+        return 0;
+    }
+
+    return 1;
+}
+#else
+static inline int pico_ipv4_crc_check(struct pico_frame *f)
+{
+    IGNORE_PARAMETER(f);
+    return 1;
+}
+#endif /* PICO_SUPPORT_CRC */
+
+static int pico_ipv4_forward(struct pico_frame *f);
+#ifdef PICO_SUPPORT_MCAST
+static int pico_ipv4_mcast_filter(struct pico_frame *f);
+#endif
+
+static int ipv4_link_compare(void *ka, void *kb)
+{
+    struct pico_ipv4_link *a = ka, *b = kb;
+    int cmp = pico_ipv4_compare(&a->address, &b->address);
+    if (cmp)
+        return cmp;
+
+    /* zero can be assigned multiple times (e.g. for DHCP) */
+    if (a->dev != NULL && b->dev != NULL && a->address.addr == PICO_IP4_ANY && b->address.addr == PICO_IP4_ANY) {
+        if (a->dev < b->dev)
+            return -1;
+
+        if (a->dev > b->dev)
+            return 1;
+    }
+
+    return 0;
+}
+
+PICO_TREE_DECLARE(Tree_dev_link, ipv4_link_compare);
+
+static int pico_ipv4_process_bcast_in(struct pico_frame *f)
+{
+    struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+#ifdef PICO_SUPPORT_UDP
+    if (pico_ipv4_is_broadcast(hdr->dst.addr) && (hdr->proto == PICO_PROTO_UDP)) {
+        /* Receiving UDP broadcast datagram */
+        f->flags |= PICO_FRAME_FLAG_BCAST;
+        pico_enqueue(pico_proto_udp.q_in, f);
+        return 1;
+    }
+
+#endif
+
+#ifdef PICO_SUPPORT_ICMP4
+    if (pico_ipv4_is_broadcast(hdr->dst.addr) && (hdr->proto == PICO_PROTO_ICMP4)) {
+        /* Receiving ICMP4 bcast packet */
+        f->flags |= PICO_FRAME_FLAG_BCAST;
+        pico_enqueue(pico_proto_icmp4.q_in, f);
+        return 1;
+    }
+
+#endif
+    return 0;
+}
+
+static int pico_ipv4_process_mcast_in(struct pico_frame *f)
+{
+    struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+    if (pico_ipv4_is_multicast(hdr->dst.addr)) {
+#ifdef PICO_SUPPORT_MCAST
+        /* Receiving UDP multicast datagram TODO set f->flags? */
+        if (hdr->proto == PICO_PROTO_IGMP) {
+            ip_mcast_dbg("MCAST: received IGMP message\n");
+            pico_transport_receive(f, PICO_PROTO_IGMP);
+            return 1;
+        } else if ((pico_ipv4_mcast_filter(f) == 0) && (hdr->proto == PICO_PROTO_UDP)) {
+            pico_enqueue(pico_proto_udp.q_in, f);
+            return 1;
+        }
+
+#endif
+        pico_frame_discard(f);
+        return 1;
+    }
+
+    return 0;
+}
+
+static int pico_ipv4_process_local_unicast_in(struct pico_frame *f)
+{
+    struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+    struct pico_ipv4_link test = {
+        .address = {.addr = PICO_IP4_ANY}, .dev = NULL
+    };
+    if (pico_ipv4_link_find(&hdr->dst)) {
+        if (pico_ipv4_nat_inbound(f, &hdr->dst) == 0)
+            pico_enqueue(pico_proto_ipv4.q_in, f); /* dst changed, reprocess */
+        else
+            pico_transport_receive(f, hdr->proto);
+
+        return 1;
+    } else if (pico_tree_findKey(&Tree_dev_link, &test)) {
+#ifdef PICO_SUPPORT_UDP
+        /* address of this device is apparently 0.0.0.0; might be a DHCP packet */
+        /* XXX KRO: is obsolete. Broadcast flag is set on outgoing DHCP messages.
+         * incomming DHCP messages are to be broadcasted. Our current DHCP server
+         * implementation does not take this flag into account yet though ... */
+        pico_enqueue(pico_proto_udp.q_in, f);
+        return 1;
+#endif
+    }
+
+    return 0;
+}
+
+static void pico_ipv4_process_finally_try_forward(struct pico_frame *f)
+{
+    struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+    if ((pico_ipv4_is_broadcast(hdr->dst.addr))) {
+        /* don't forward broadcast frame, discard! */
+        pico_frame_discard(f);
+    } else if (pico_ipv4_forward(f) != 0) {
+        pico_frame_discard(f);
+        /* dbg("Forward failed.\n"); */
+    }
+}
+
+
+
+static int pico_ipv4_process_in(struct pico_protocol *self, struct pico_frame *f)
+{
+    uint8_t option_len = 0;
+    int ret = 0;
+    struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+    uint16_t max_allowed = (uint16_t) ((int)f->buffer_len - (f->net_hdr - f->buffer) - (int)PICO_SIZE_IP4HDR);
+    uint16_t flag = short_be(hdr->frag);
+
+    (void)self;
+    /* NAT needs transport header information */
+    if (((hdr->vhl) & 0x0F) > 5) {
+        option_len =  (uint8_t)(4 * (((hdr->vhl) & 0x0F) - 5));
+    }
+
+    f->transport_hdr = ((uint8_t *)f->net_hdr) + PICO_SIZE_IP4HDR + option_len;
+    f->transport_len = (uint16_t)(short_be(hdr->len) - PICO_SIZE_IP4HDR - option_len);
+    f->net_len = (uint16_t)(PICO_SIZE_IP4HDR + option_len);
+
+    if (f->transport_len > max_allowed) {
+        pico_frame_discard(f);
+        return 0; /* Packet is discarded due to unfeasible length */
+    }
+
+#ifdef PICO_SUPPORT_IPFILTER
+    if (ipfilter(f)) {
+        /*pico_frame is discarded as result of the filtering*/
+        return 0;
+    }
+
+#endif
+
+
+    /* ret == 1 indicates to continue the function */
+    ret = pico_ipv4_crc_check(f);
+    if (ret < 1)
+        return ret;
+
+    /* Validate source IP address. Discard quietly if invalid */
+    if (!pico_ipv4_is_valid_src(hdr->src.addr, f->dev)) {
+        pico_frame_discard(f);
+        return 0;
+    }
+
+    if (hdr->frag & short_be(PICO_IPV4_EVIL)) {
+        (void)pico_icmp4_param_problem(f, 0);
+        pico_frame_discard(f); /* RFC 3514 */
+        return 0;
+    }
+
+    if ((hdr->vhl & 0x0f) < 5) {
+        /* RFC 791: IHL minimum value is 5 */
+        (void)pico_icmp4_param_problem(f, 0);
+        pico_frame_discard(f);
+        return 0;
+    }
+
+    if ((flag & PICO_IPV4_MOREFRAG) || (flag & PICO_IPV4_FRAG_MASK))
+    {
+#ifdef PICO_SUPPORT_IPFRAG
+        pico_ipv4_process_frag(hdr, f, hdr ? hdr->proto : 0 );
+        /* Frame can be discarded, frag will handle its own copy */
+#endif
+        /* We do not support fragmentation, discard frame quietly */
+        pico_frame_discard(f);
+        return 0;
+    }
+
+    if (pico_ipv4_process_bcast_in(f) > 0)
+        return 0;
+
+    if (pico_ipv4_process_mcast_in(f) > 0)
+        return 0;
+
+    if (pico_ipv4_process_local_unicast_in(f) > 0)
+        return 0;
+
+    pico_ipv4_process_finally_try_forward(f);
+
+    return 0;
+}
+
+PICO_TREE_DECLARE(Routes, ipv4_route_compare);
+
+
+static int pico_ipv4_process_out(struct pico_protocol *self, struct pico_frame *f)
+{
+    IGNORE_PARAMETER(self);
+    f->start = (uint8_t*) f->net_hdr;
+#ifdef PICO_SUPPORT_IPFILTER
+    if (ipfilter(f)) {
+        /*pico_frame is discarded as result of the filtering*/
+        return 0;
+    }
+
+#endif
+    return pico_sendto_dev(f);
+}
+
+
+static struct pico_frame *pico_ipv4_alloc(struct pico_protocol *self, uint16_t size)
+{
+    struct pico_frame *f =  pico_frame_alloc(size + PICO_SIZE_IP4HDR + PICO_SIZE_ETHHDR);
+    IGNORE_PARAMETER(self);
+
+    if (!f)
+        return NULL;
+
+    f->datalink_hdr = f->buffer;
+    f->net_hdr = f->buffer + PICO_SIZE_ETHHDR;
+    f->net_len = PICO_SIZE_IP4HDR;
+    f->transport_hdr = f->net_hdr + PICO_SIZE_IP4HDR;
+    f->transport_len = size;
+    f->len =  size + PICO_SIZE_IP4HDR;
+    return f;
+}
+
+static int pico_ipv4_frame_sock_push(struct pico_protocol *self, struct pico_frame *f);
+
+/* Interface: protocol definition */
+struct pico_protocol pico_proto_ipv4 = {
+    .name = "ipv4",
+    .proto_number = PICO_PROTO_IPV4,
+    .layer = PICO_LAYER_NETWORK,
+    .alloc = pico_ipv4_alloc,
+    .process_in = pico_ipv4_process_in,
+    .process_out = pico_ipv4_process_out,
+    .push = pico_ipv4_frame_sock_push,
+    .q_in = &in,
+    .q_out = &out,
+};
+
+
+static int ipv4_route_compare(void *ka, void *kb)
+{
+    struct pico_ipv4_route *a = ka, *b = kb;
+    uint32_t a_nm, b_nm;
+    int cmp;
+
+    a_nm = long_be(a->netmask.addr);
+    b_nm = long_be(b->netmask.addr);
+
+    /* Routes are sorted by (host side) netmask len, then by addr, then by metric. */
+    if (a_nm < b_nm)
+        return -1;
+
+    if (b_nm < a_nm)
+        return 1;
+
+    cmp = pico_ipv4_compare(&a->dest, &b->dest);
+    if (cmp)
+        return cmp;
+
+    if (a->metric < b->metric)
+        return -1;
+
+    if (a->metric > b->metric)
+        return 1;
+
+    return 0;
+}
+
+
+static struct pico_ipv4_route default_bcast_route = {
+    .dest = {PICO_IP4_BCAST},
+    .netmask = {PICO_IP4_BCAST},
+    .gateway  = { 0 },
+    .link = NULL,
+    .metric = 1000
+};
+
+static struct pico_ipv4_route *route_find_default_bcast(void)
+{
+    return &default_bcast_route;
+}
+
+
+static struct pico_ipv4_route *route_find(const struct pico_ip4 *addr)
+{
+    struct pico_ipv4_route *r;
+    struct pico_tree_node *index;
+
+    if (addr->addr != PICO_IP4_BCAST) {
+        pico_tree_foreach_reverse(index, &Routes) {
+            r = index->keyValue;
+            if ((addr->addr & (r->netmask.addr)) == (r->dest.addr)) {
+                return r;
+            }
+        }
+        return NULL;
+    }
+
+    return route_find_default_bcast();
+}
+
+struct pico_ip4 pico_ipv4_route_get_gateway(struct pico_ip4 *addr)
+{
+    struct pico_ip4 nullip;
+    struct pico_ipv4_route *route;
+    nullip.addr = 0U;
+
+    if (!addr) {
+        pico_err = PICO_ERR_EINVAL;
+        return nullip;
+    }
+
+    route = route_find(addr);
+    if (!route) {
+        pico_err = PICO_ERR_EHOSTUNREACH;
+        return nullip;
+    }
+    else
+        return route->gateway;
+}
+
+struct pico_ip4 *pico_ipv4_source_find(const struct pico_ip4 *dst)
+{
+    struct pico_ip4 *myself = NULL;
+    struct pico_ipv4_route *rt;
+#ifdef PICO_SUPPORT_AODV
+    union pico_address node_address;
+    node_address.ip4.addr = dst->addr;
+    if (dst->addr && pico_ipv4_is_unicast(dst->addr))
+        pico_aodv_lookup(&node_address);
+
+#endif
+
+    if (!dst) {
+        pico_err = PICO_ERR_EINVAL;
+        return NULL;
+    }
+
+    rt = route_find(dst);
+    if (rt && rt->link) {
+        myself = &rt->link->address;
+    } else {
+        pico_err = PICO_ERR_EHOSTUNREACH;
+    }
+
+    return myself;
+}
+
+struct pico_device *pico_ipv4_source_dev_find(const struct pico_ip4 *dst)
+{
+    struct pico_device *dev = NULL;
+    struct pico_ipv4_route *rt;
+
+    if (!dst) {
+        pico_err = PICO_ERR_EINVAL;
+        return NULL;
+    }
+
+    rt = route_find(dst);
+    if (rt && rt->link) {
+        dev = rt->link->dev;
+    } else {
+        pico_err = PICO_ERR_EHOSTUNREACH;
+    }
+
+    return dev;
+}
+
+
+#ifdef PICO_SUPPORT_MCAST
+/*                        link
+ *                         |
+ *                    MCASTGroups
+ *                    |    |     |
+ *         ------------    |     ------------
+ *         |               |                |
+ *   MCASTSources    MCASTSources     MCASTSources
+ *   |  |  |  |      |  |  |  |       |  |  |  |
+ *   S  S  S  S      S  S  S  S       S  S  S  S
+ *
+ *   MCASTGroups: RBTree(mcast_group)
+ *   MCASTSources: RBTree(source)
+ */
+static int ipv4_mcast_groups_cmp(void *ka, void *kb)
+{
+    struct pico_mcast_group *a = ka, *b = kb;
+    return pico_ipv4_compare(&a->mcast_addr, &b->mcast_addr);
+}
+
+static int ipv4_mcast_sources_cmp(void *ka, void *kb)
+{
+    struct pico_ip4 *a = ka, *b = kb;
+    return pico_ipv4_compare(a, b);
+}
+
+static void pico_ipv4_mcast_print_groups(struct pico_ipv4_link *mcast_link)
+{
+    uint16_t i = 0;
+    struct pico_mcast_group *g = NULL;
+    struct pico_ip4 *source = NULL;
+    struct pico_tree_node *index = NULL, *index2 = NULL;
+    (void) source;
+
+    ip_mcast_dbg("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
+    ip_mcast_dbg("+                           MULTICAST list interface %-16s             +\n", mcast_link->dev->name);
+    ip_mcast_dbg("+---------------------------------------------------------------------------------+\n");
+    ip_mcast_dbg("+  nr  |    interface     | host group | reference count | filter mode |  source  +\n");
+    ip_mcast_dbg("+---------------------------------------------------------------------------------+\n");
+
+    pico_tree_foreach(index, mcast_link->MCASTGroups) {
+        g = index->keyValue;
+        ip_mcast_dbg("+ %04d | %16s |  %08X  |      %05u      |      %u      | %8s +\n", i, mcast_link->dev->name, g->mcast_addr.addr, g->reference_count, g->filter_mode, "");
+        pico_tree_foreach(index2, &g->MCASTSources) {
+            source = index2->keyValue;
+            ip_mcast_dbg("+ %4s | %16s |  %8s  |      %5s      |      %s      | %08X +\n", "", "", "", "", "", source->addr);
+        }
+        i++;
+    }
+    ip_mcast_dbg("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
+}
+
+static int mcast_group_update(struct pico_mcast_group *g, struct pico_tree *MCASTFilter, uint8_t filter_mode)
+{
+    struct pico_tree_node *index = NULL, *_tmp = NULL;
+    struct pico_ip4 *source = NULL;
+    /* cleanup filter */
+    pico_tree_foreach_safe(index, &g->MCASTSources, _tmp) {
+        source = index->keyValue;
+        pico_tree_delete(&g->MCASTSources, source);
+        PICO_FREE(source);
+    }
+    /* insert new filter */
+    if (MCASTFilter) {
+        pico_tree_foreach(index, MCASTFilter) {
+            if (index->keyValue) {
+                source = PICO_ZALLOC(sizeof(struct pico_ip4));
+                if (!source) {
+                    pico_err = PICO_ERR_ENOMEM;
+                    return -1;
+                }
+
+                source->addr = ((struct pico_ip4 *)index->keyValue)->addr;
+                pico_tree_insert(&g->MCASTSources, source);
+            }
+        }
+    }
+
+    g->filter_mode = filter_mode;
+    return 0;
+}
+
+int pico_ipv4_mcast_join(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter)
+{
+    struct pico_mcast_group *g = NULL, test = {
+        0
+    };
+    struct pico_ipv4_link *link = NULL;
+
+    if (mcast_link)
+        link = pico_ipv4_link_get(mcast_link);
+
+    if (!link)
+        link = mcast_default_link;
+
+    test.mcast_addr = *mcast_group;
+    g = pico_tree_findKey(link->MCASTGroups, &test);
+    if (g) {
+        if (reference_count)
+            g->reference_count++;
+
+        pico_igmp_state_change(mcast_link, mcast_group, filter_mode, MCASTFilter, PICO_IGMP_STATE_UPDATE);
+    } else {
+        g = PICO_ZALLOC(sizeof(struct pico_mcast_group));
+        if (!g) {
+            pico_err = PICO_ERR_ENOMEM;
+            return -1;
+        }
+
+        /* "non-existent" state of filter mode INCLUDE and empty source list */
+        g->filter_mode = PICO_IP_MULTICAST_INCLUDE;
+        g->reference_count = 1;
+        g->mcast_addr = *mcast_group;
+        g->MCASTSources.root = &LEAF;
+        g->MCASTSources.compare = ipv4_mcast_sources_cmp;
+        pico_tree_insert(link->MCASTGroups, g);
+        pico_igmp_state_change(mcast_link, mcast_group, filter_mode, MCASTFilter, PICO_IGMP_STATE_CREATE);
+    }
+
+    if (mcast_group_update(g, MCASTFilter, filter_mode) < 0) {
+        dbg("Error in mcast_group update\n");
+        return -1;
+    }
+
+    pico_ipv4_mcast_print_groups(link);
+    return 0;
+}
+
+int pico_ipv4_mcast_leave(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter)
+{
+
+    struct pico_mcast_group *g = NULL, test = {
+        0
+    };
+    struct pico_ipv4_link *link = NULL;
+    struct pico_tree_node *index = NULL, *_tmp = NULL;
+    struct pico_ip4 *source = NULL;
+
+    if (mcast_link)
+        link = pico_ipv4_link_get(mcast_link);
+
+    if (!link)
+        link = mcast_default_link;
+
+    test.mcast_addr = *mcast_group;
+    g = pico_tree_findKey(link->MCASTGroups, &test);
+    if (!g) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    } else {
+        if (reference_count && (--(g->reference_count) < 1)) {
+            pico_igmp_state_change(mcast_link, mcast_group, filter_mode, MCASTFilter, PICO_IGMP_STATE_DELETE);
+            /* cleanup filter */
+            pico_tree_foreach_safe(index, &g->MCASTSources, _tmp) {
+                source = index->keyValue;
+                pico_tree_delete(&g->MCASTSources, source);
+                PICO_FREE(source);
+            }
+            pico_tree_delete(link->MCASTGroups, g);
+            PICO_FREE(g);
+        } else {
+            pico_igmp_state_change(mcast_link, mcast_group, filter_mode, MCASTFilter, PICO_IGMP_STATE_UPDATE);
+            if (mcast_group_update(g, MCASTFilter, filter_mode) < 0)
+                return -1;
+        }
+    }
+
+    pico_ipv4_mcast_print_groups(link);
+    return 0;
+}
+
+struct pico_ipv4_link *pico_ipv4_get_default_mcastlink(void)
+{
+    return mcast_default_link;
+}
+
+static int pico_ipv4_mcast_filter(struct pico_frame *f)
+{
+    struct pico_ipv4_link *link = NULL;
+    struct pico_tree_node *index = NULL, *index2 = NULL;
+    struct pico_mcast_group *g = NULL, test = {
+        0
+    };
+    struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+
+    test.mcast_addr = hdr->dst;
+
+    pico_tree_foreach(index, &Tree_dev_link) {
+        link = index->keyValue;
+        g = pico_tree_findKey(link->MCASTGroups, &test);
+        if (g) {
+            if (f->dev == link->dev) {
+                ip_mcast_dbg("MCAST: IP %08X is group member of current link %s\n", hdr->dst.addr, f->dev->name);
+                /* perform source filtering */
+                switch (g->filter_mode) {
+                case PICO_IP_MULTICAST_INCLUDE:
+                    pico_tree_foreach(index2, &g->MCASTSources) {
+                        if (hdr->src.addr == ((struct pico_ip4 *)index2->keyValue)->addr) {
+                            ip_mcast_dbg("MCAST: IP %08X in included interface source list\n", hdr->src.addr);
+                            return 0;
+                        }
+                    }
+                    ip_mcast_dbg("MCAST: IP %08X NOT in included interface source list\n", hdr->src.addr);
+                    return -1;
+
+                case PICO_IP_MULTICAST_EXCLUDE:
+                    pico_tree_foreach(index2, &g->MCASTSources) {
+                        if (hdr->src.addr == ((struct pico_ip4 *)index2->keyValue)->addr) {
+                            ip_mcast_dbg("MCAST: IP %08X in excluded interface source list\n", hdr->src.addr);
+                            return -1;
+                        }
+                    }
+                    ip_mcast_dbg("MCAST: IP %08X NOT in excluded interface source list\n", hdr->src.addr);
+                    return 0;
+
+                default:
+                    return -1;
+                }
+            } else {
+                ip_mcast_dbg("MCAST: IP %08X is group member of different link %s\n", hdr->dst.addr, link->dev->name);
+            }
+        } else {
+            ip_mcast_dbg("MCAST: IP %08X is not a group member of link %s\n", hdr->dst.addr, f->dev->name);
+        }
+    }
+    return -1;
+}
+
+#else
+
+int pico_ipv4_mcast_join(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter)
+{
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+    return -1;
+}
+
+int pico_ipv4_mcast_leave(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter)
+{
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+    return -1;
+}
+
+struct pico_ipv4_link *pico_ipv4_get_default_mcastlink(void)
+{
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+    return NULL;
+}
+#endif /* PICO_SUPPORT_MCAST */
+
+/* #define DEBUG_ROUTE */
+#ifdef DEBUG_ROUTE
+void dbg_route(void)
+{
+    struct pico_ipv4_route *r;
+    struct pico_tree_node *index;
+    int count_hosts = 0;
+    dbg("==== ROUTING TABLE =====\n");
+    pico_tree_foreach(index, &Routes) {
+        r = index->keyValue;
+        dbg("Route to %08x/%08x, gw %08x, dev: %s, metric: %d\n", r->dest.addr, r->netmask.addr, r->gateway.addr, r->link->dev->name, r->metric);
+        if (r->netmask.addr == 0xFFFFFFFF)
+            count_hosts++;
+    }
+    dbg("================ total HOST nodes: %d ======\n\n\n", count_hosts);
+}
+#else
+#define dbg_route() do { } while(0)
+#endif
+
+int pico_ipv4_frame_push(struct pico_frame *f, struct pico_ip4 *dst, uint8_t proto)
+{
+
+    struct pico_ipv4_route *route;
+    struct pico_ipv4_link *link;
+    struct pico_ipv4_hdr *hdr;
+    uint8_t ttl = PICO_IPV4_DEFAULT_TTL;
+    uint8_t vhl = 0x45; /* version 4, header length 20 */
+    static uint16_t ipv4_progressive_id = 0x91c0;
+#ifdef PICO_SUPPORT_MCAST
+    struct pico_tree_node *index;
+#endif
+
+    if (!f || !dst) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+
+    hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+    if (!hdr) {
+        dbg("IP header error\n");
+        pico_err = PICO_ERR_EINVAL;
+        goto drop;
+    }
+
+    if (dst->addr == 0) {
+        dbg("IP destination addr error\n");
+        pico_err = PICO_ERR_EINVAL;
+        goto drop;
+    }
+
+    route = route_find(dst);
+    if (!route) {
+        /* dbg("Route to %08x not found.\n", long_be(dst->addr)); */
+
+
+        pico_err = PICO_ERR_EHOSTUNREACH;
+        goto drop;
+    } else {
+        link = route->link;
+#ifdef PICO_SUPPORT_MCAST
+        if (pico_ipv4_is_multicast(dst->addr)) { /* if multicast */
+            switch (proto) {
+            case PICO_PROTO_UDP:
+                if (pico_udp_get_mc_ttl(f->sock, &ttl) < 0)
+                    ttl = PICO_IP_DEFAULT_MULTICAST_TTL;
+
+                break;
+            case PICO_PROTO_IGMP:
+                vhl = 0x46; /* header length 24 */
+                ttl = 1;
+                /* router alert (RFC 2113) */
+                hdr->options[0] = 0x94;
+                hdr->options[1] = 0x04;
+                hdr->options[2] = 0x00;
+                hdr->options[3] = 0x00;
+                if (f->dev && link->dev != f->dev) { /* default link is not requested link */
+                    pico_tree_foreach(index, &Tree_dev_link) {
+                        link = index->keyValue;
+                        if (link->dev == f->dev)
+                            break;
+                    }
+                }
+
+                break;
+            default:
+                ttl = PICO_IPV4_DEFAULT_TTL;
+            }
+        }
+
+#endif
+    }
+
+    hdr->vhl = vhl;
+    hdr->len = short_be((uint16_t)(f->transport_len + f->net_len));
+    if ((f->transport_hdr != f->payload)  &&
+#ifdef PICO_SUPPORT_IPFRAG
+        (0 == (f->frag & PICO_IPV4_MOREFRAG)) &&
+        (0 == (f->frag & PICO_IPV4_FRAG_MASK)) &&
+#endif
+        1 )
+        ipv4_progressive_id++;
+
+    if (f->send_ttl > 0) {
+        ttl = f->send_ttl;
+    }
+
+    hdr->id = short_be(ipv4_progressive_id);
+    hdr->dst.addr = dst->addr;
+    hdr->src.addr = link->address.addr;
+    hdr->ttl = ttl;
+    hdr->tos = f->send_tos;
+    hdr->proto = proto;
+    hdr->frag = short_be(PICO_IPV4_DONTFRAG);
+
+#ifdef PICO_SUPPORT_IPFRAG
+#  ifdef PICO_SUPPORT_UDP
+    if (proto == PICO_PROTO_UDP) {
+        /* first fragment, can not use transport_len to calculate IP length */
+        if (f->transport_hdr != f->payload)
+            hdr->len = short_be((uint16_t)(f->payload_len + sizeof(struct pico_udp_hdr) + f->net_len));
+
+        /* set fragmentation flags and offset calculated in socket layer */
+        hdr->frag = short_be(f->frag);
+    }
+
+    if (proto == PICO_PROTO_ICMP4)
+    {
+        hdr->frag = short_be(f->frag);
+    }
+
+#   endif
+#endif /* PICO_SUPPORT_IPFRAG */
+    pico_ipv4_checksum(f);
+
+    if (f->sock && f->sock->dev) {
+        /* if the socket has its device set, use that (currently used for DHCP) */
+        f->dev = f->sock->dev;
+    } else {
+        f->dev = link->dev;
+        if (f->sock)
+            f->sock->dev = f->dev;
+    }
+
+#ifdef PICO_SUPPORT_MCAST
+    if (pico_ipv4_is_multicast(hdr->dst.addr)) {
+        struct pico_frame *cpy;
+        /* Sending UDP multicast datagram, am I member? If so, loopback copy */
+        if ((proto != PICO_PROTO_IGMP) && (pico_ipv4_mcast_filter(f) == 0)) {
+            ip_mcast_dbg("MCAST: sender is member of group, loopback copy\n");
+            cpy = pico_frame_copy(f);
+            pico_enqueue(&in, cpy);
+        }
+    }
+
+#endif
+
+/* #ifdef PICO_SUPPORT_AODV */
+#if 0
+    {
+        union pico_address node_address;
+        node_address.ip4.addr = hdr->dst.addr;
+        if(hdr->dst.addr && pico_ipv4_is_unicast(hdr->dst.addr))
+            pico_aodv_lookup(&node_address);
+    }
+#endif
+
+    if (pico_ipv4_link_get(&hdr->dst)) {
+        /* it's our own IP */
+        return pico_enqueue(&in, f);
+    } else{
+        /* TODO: Check if there are members subscribed here */
+        return pico_enqueue(&out, f);
+    }
+
+drop:
+    pico_frame_discard(f);
+    return -1;
+}
+
+
+static int pico_ipv4_frame_sock_push(struct pico_protocol *self, struct pico_frame *f)
+{
+    struct pico_ip4 *dst;
+    struct pico_remote_endpoint *remote_endpoint = (struct pico_remote_endpoint *) f->info;
+    IGNORE_PARAMETER(self);
+
+    if (!f->sock) {
+        pico_frame_discard(f);
+        return -1;
+    }
+
+    if (remote_endpoint) {
+        dst = &remote_endpoint->remote_addr.ip4;
+    } else {
+        dst = &f->sock->remote_addr.ip4;
+    }
+
+    return pico_ipv4_frame_push(f, dst, (uint8_t)f->sock->proto->proto_number);
+}
+
+
+int MOCKABLE pico_ipv4_route_add(struct pico_ip4 address, struct pico_ip4 netmask, struct pico_ip4 gateway, int metric, struct pico_ipv4_link *link)
+{
+    struct pico_ipv4_route test, *new;
+    test.dest.addr = address.addr;
+    test.netmask.addr = netmask.addr;
+    test.metric = (uint32_t)metric;
+
+    if (pico_tree_findKey(&Routes, &test)) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    new = PICO_ZALLOC(sizeof(struct pico_ipv4_route));
+    if (!new) {
+        pico_err = PICO_ERR_ENOMEM;
+        return -1;
+    }
+
+    new->dest.addr = address.addr;
+    new->netmask.addr = netmask.addr;
+    new->gateway.addr = gateway.addr;
+    new->metric = (uint32_t)metric;
+    if (gateway.addr == 0) {
+        /* No gateway provided, use the link */
+        new->link = link;
+    } else {
+        struct pico_ipv4_route *r = route_find(&gateway);
+        if (!r ) { /* Specified Gateway is unreachable */
+            pico_err = PICO_ERR_EHOSTUNREACH;
+            PICO_FREE(new);
+            return -1;
+        }
+
+        if (r->gateway.addr) { /* Specified Gateway is not a neighbor */
+            pico_err = PICO_ERR_ENETUNREACH;
+            PICO_FREE(new);
+            return -1;
+        }
+
+        new->link = r->link;
+    }
+
+    if (!new->link) {
+        pico_err = PICO_ERR_EINVAL;
+        PICO_FREE(new);
+        return -1;
+    }
+
+    pico_tree_insert(&Routes, new);
+    dbg_route();
+    return 0;
+}
+
+int pico_ipv4_route_del(struct pico_ip4 address, struct pico_ip4 netmask, int metric)
+{
+    struct pico_ipv4_route test, *found;
+
+    test.dest.addr = address.addr;
+    test.netmask.addr = netmask.addr;
+    test.metric = (uint32_t)metric;
+
+    found = pico_tree_findKey(&Routes, &test);
+    if (found) {
+
+        pico_tree_delete(&Routes, found);
+        PICO_FREE(found);
+
+        dbg_route();
+        return 0;
+    }
+
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+}
+
+
+int pico_ipv4_link_add(struct pico_device *dev, struct pico_ip4 address, struct pico_ip4 netmask)
+{
+    struct pico_ipv4_link test, *new;
+    struct pico_ip4 network, gateway;
+    char ipstr[30];
+
+    if (!dev) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    test.address.addr = address.addr;
+    test.netmask.addr = netmask.addr;
+    test.dev = dev;
+    /** XXX: Valid netmask / unicast address test **/
+
+    if (pico_tree_findKey(&Tree_dev_link, &test)) {
+        pico_err = PICO_ERR_EADDRINUSE;
+        return -1;
+    }
+
+    /** XXX: Check for network already in use (e.g. trying to assign 10.0.0.1/24 where 10.1.0.1/8 is in use) **/
+    new = PICO_ZALLOC(sizeof(struct pico_ipv4_link));
+    if (!new) {
+        dbg("IPv4: Out of memory!\n");
+        pico_err = PICO_ERR_ENOMEM;
+        return -1;
+    }
+
+    new->address.addr = address.addr;
+    new->netmask.addr = netmask.addr;
+    new->dev = dev;
+#ifdef PICO_SUPPORT_MCAST
+    new->MCASTGroups = PICO_ZALLOC(sizeof(struct pico_tree));
+    if (!new->MCASTGroups) {
+        PICO_FREE(new);
+        dbg("IPv4: Out of memory!\n");
+        pico_err = PICO_ERR_ENOMEM;
+        return -1;
+    }
+
+    new->MCASTGroups->root = &LEAF;
+    new->MCASTGroups->compare = ipv4_mcast_groups_cmp;
+    new->mcast_compatibility = PICO_IGMPV3; /* default RFC 3376 $7.2.1 */
+    new->mcast_last_query_interval = PICO_IGMP_QUERY_INTERVAL;
+#endif
+
+    pico_tree_insert(&Tree_dev_link, new);
+#ifdef PICO_SUPPORT_MCAST
+    do {
+        struct pico_ip4 mcast_all_hosts, mcast_addr, mcast_nm, mcast_gw;
+        if (!mcast_default_link) {
+            mcast_addr.addr = long_be(0xE0000000); /* 224.0.0.0 */
+            mcast_nm.addr = long_be(0xF0000000); /* 15.0.0.0 */
+            mcast_gw.addr = long_be(0x00000000);
+            mcast_default_link = new;
+            pico_ipv4_route_add(mcast_addr, mcast_nm, mcast_gw, 1, new);
+        }
+
+        mcast_all_hosts.addr = PICO_MCAST_ALL_HOSTS;
+        pico_ipv4_mcast_join(&address, &mcast_all_hosts, 1, PICO_IP_MULTICAST_EXCLUDE, NULL);
+    } while(0);
+#endif
+
+    network.addr = address.addr & netmask.addr;
+    gateway.addr = 0U;
+    pico_ipv4_route_add(network, netmask, gateway, 1, new);
+    pico_ipv4_to_string(ipstr, new->address.addr);
+    dbg("Assigned ipv4 %s to device %s\n", ipstr, new->dev->name);
+    if (default_bcast_route.link == NULL)
+        default_bcast_route.link = new;
+
+    return 0;
+}
+
+static int pico_ipv4_cleanup_routes(struct pico_ipv4_link *link)
+{
+    struct pico_tree_node *index = NULL, *tmp = NULL;
+    struct pico_ipv4_route *route = NULL;
+
+    pico_tree_foreach_safe(index, &Routes, tmp) {
+        route = index->keyValue;
+        if (link == route->link)
+            pico_ipv4_route_del(route->dest, route->netmask, (int)route->metric);
+    }
+    return 0;
+}
+
+void MOCKABLE pico_ipv4_route_set_bcast_link(struct pico_ipv4_link *link)
+{
+    if (link)
+        default_bcast_route.link = link;
+}
+
+int pico_ipv4_link_del(struct pico_device *dev, struct pico_ip4 address)
+{
+    struct pico_ipv4_link test, *found;
+
+    if (!dev) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    test.address.addr = address.addr;
+    test.dev = dev;
+    found = pico_tree_findKey(&Tree_dev_link, &test);
+    if (!found) {
+        pico_err = PICO_ERR_ENXIO;
+        return -1;
+    }
+
+#ifdef PICO_SUPPORT_MCAST
+    do {
+        struct pico_ip4 mcast_all_hosts, mcast_addr, mcast_nm;
+        struct pico_mcast_group *g = NULL;
+        struct pico_tree_node *index, *_tmp;
+        if (found == mcast_default_link) {
+            mcast_addr.addr = long_be(0xE0000000); /* 224.0.0.0 */
+            mcast_nm.addr = long_be(0xF0000000); /* 15.0.0.0 */
+            mcast_default_link = NULL;
+            pico_ipv4_route_del(mcast_addr, mcast_nm, 1);
+        }
+
+        mcast_all_hosts.addr = PICO_MCAST_ALL_HOSTS;
+        pico_ipv4_mcast_leave(&address, &mcast_all_hosts, 1, PICO_IP_MULTICAST_EXCLUDE, NULL);
+        pico_tree_foreach_safe(index, found->MCASTGroups, _tmp) {
+            g = index->keyValue;
+            pico_tree_delete(found->MCASTGroups, g);
+            PICO_FREE(g);
+        }
+    } while(0);
+#endif
+
+    pico_ipv4_cleanup_routes(found);
+    pico_tree_delete(&Tree_dev_link, found);
+    if (default_bcast_route.link == found)
+        default_bcast_route.link = NULL;
+
+    PICO_FREE(found);
+
+    return 0;
+}
+
+
+struct pico_ipv4_link *pico_ipv4_link_get(struct pico_ip4 *address)
+{
+    struct pico_ipv4_link test = {
+        0
+    }, *found = NULL;
+    test.address.addr = address->addr;
+
+    found = pico_tree_findKey(&Tree_dev_link, &test);
+    if (!found)
+        return NULL;
+    else
+        return found;
+}
+
+struct pico_ipv4_link *MOCKABLE pico_ipv4_link_by_dev(struct pico_device *dev)
+{
+    struct pico_tree_node *index = NULL;
+    struct pico_ipv4_link *link = NULL;
+
+    pico_tree_foreach(index, &Tree_dev_link) {
+        link = index->keyValue;
+        if (link->dev == dev)
+            return link;
+    }
+    return NULL;
+}
+
+struct pico_ipv4_link *pico_ipv4_link_by_dev_next(struct pico_device *dev, struct pico_ipv4_link *last)
+{
+    struct pico_tree_node *index = NULL;
+    struct pico_ipv4_link *link = NULL;
+    int valid = 0;
+
+    if (last == NULL)
+        valid = 1;
+
+    pico_tree_foreach(index, &Tree_dev_link) {
+        link = index->keyValue;
+        if (link->dev == dev) {
+            if (last == link)
+                valid = 1;
+            else if (valid > 0)
+                return link;
+        }
+    }
+    return NULL;
+}
+
+struct pico_device *MOCKABLE pico_ipv4_link_find(struct pico_ip4 *address)
+{
+    struct pico_ipv4_link test, *found;
+    if (!address) {
+        pico_err = PICO_ERR_EINVAL;
+        return NULL;
+    }
+
+    test.dev = NULL;
+    test.address.addr = address->addr;
+    found = pico_tree_findKey(&Tree_dev_link, &test);
+    if (!found) {
+        pico_err = PICO_ERR_ENXIO;
+        return NULL;
+    }
+
+    return found->dev;
+}
+
+
+static int pico_ipv4_rebound_large(struct pico_frame *f)
+{
+#ifdef PICO_SUPPORT_IPFRAG
+    uint16_t total_payload_written = 0;
+    uint32_t len = f->transport_len;
+    struct pico_frame *fr;
+    struct pico_ip4 dst;
+    struct pico_ipv4_hdr *hdr;
+    hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+    dst.addr = hdr->src.addr;
+
+    while(total_payload_written < len) {
+        uint32_t space = (uint32_t)len - total_payload_written;
+        if (space > PICO_IPV4_MAXPAYLOAD)
+            space = PICO_IPV4_MAXPAYLOAD;
+
+        fr = pico_ipv4_alloc(&pico_proto_ipv4, (uint16_t)space);
+        if (!fr) {
+            pico_err = PICO_ERR_ENOMEM;
+            return -1;
+        }
+
+        if (space + total_payload_written < len)
+        {
+            fr->frag |= PICO_IPV4_MOREFRAG;
+        }
+        else
+        {
+            fr->frag &= PICO_IPV4_FRAG_MASK;
+        }
+
+        fr->frag = (((total_payload_written) >> 3u) & 0xffffu) | fr->frag;
+
+        memcpy(fr->transport_hdr, f->transport_hdr + total_payload_written, fr->transport_len);
+        if (pico_ipv4_frame_push(fr, &dst, hdr->proto) > 0) {
+            total_payload_written = (uint16_t)((uint16_t)fr->transport_len + total_payload_written);
+        } else {
+            pico_frame_discard(fr);
+            break;
+        }
+    } /* while() */
+    return (int)total_payload_written;
+#else
+    (void)f;
+    return -1;
+#endif
+}
+
+int pico_ipv4_rebound(struct pico_frame *f)
+{
+    struct pico_ip4 dst;
+    struct pico_ipv4_hdr *hdr;
+    if (!f) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+    if (!hdr) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    dst.addr = hdr->src.addr;
+    if (f->transport_len > PICO_IPV4_MAXPAYLOAD) {
+        return pico_ipv4_rebound_large(f);
+    }
+
+    return pico_ipv4_frame_push(f, &dst, hdr->proto);
+}
+
+static int pico_ipv4_pre_forward_checks(struct pico_frame *f)
+{
+    static uint16_t last_id = 0;
+    static uint16_t last_proto = 0;
+    static struct pico_ip4 last_src = {
+        0
+    };
+    static struct pico_ip4 last_dst = {
+        0
+    };
+    struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *)f->net_hdr;
+
+    /* Decrease TTL, check if expired */
+    hdr->ttl = (uint8_t)(hdr->ttl - 1);
+    if (hdr->ttl < 1) {
+        pico_notify_ttl_expired(f);
+        dbg(" ------------------- TTL EXPIRED\n");
+        return -1;
+    }
+
+    /* HACK: increase crc to compensate decreased TTL */
+    hdr->crc++;
+
+    /* If source is local, discard anyway (packets bouncing back and forth) */
+    if (pico_ipv4_link_get(&hdr->src))
+        return -1;
+
+    /* If this was the last forwarded packet, silently discard to prevent duplications */
+    if ((last_src.addr == hdr->src.addr) && (last_id == hdr->id)
+        && (last_dst.addr == hdr->dst.addr) && (last_proto == hdr->proto)) {
+        return -1;
+    } else {
+        last_src.addr = hdr->src.addr;
+        last_dst.addr = hdr->dst.addr;
+        last_id = hdr->id;
+        last_proto = hdr->proto;
+    }
+
+    return 0;
+}
+
+static int pico_ipv4_forward_check_dev(struct pico_frame *f)
+{
+    if (f->dev->eth != NULL)
+        f->len -= PICO_SIZE_ETHHDR;
+
+    if (f->len > f->dev->mtu) {
+        pico_notify_pkt_too_big(f);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int pico_ipv4_forward(struct pico_frame *f)
+{
+    struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *)f->net_hdr;
+    struct pico_ipv4_route *rt;
+    if (!hdr) {
+        return -1;
+    }
+
+    rt = route_find(&hdr->dst);
+    if (!rt) {
+        pico_notify_dest_unreachable(f);
+        return -1;
+    }
+
+    f->dev = rt->link->dev;
+
+    if (pico_ipv4_pre_forward_checks(f) < 0)
+        return -1;
+
+    pico_ipv4_nat_outbound(f, &rt->link->address);
+
+    f->start = f->net_hdr;
+
+    if (pico_ipv4_forward_check_dev(f) < 0)
+        return -1;
+
+    pico_sendto_dev(f);
+    return 0;
+
+}
+
+int pico_ipv4_is_broadcast(uint32_t addr)
+{
+    struct pico_ipv4_link *link;
+    struct pico_tree_node *index;
+    if (addr == PICO_IP4_BCAST)
+        return 1;
+
+    pico_tree_foreach(index, &Tree_dev_link) {
+        link = index->keyValue;
+        if ((link->address.addr | (~link->netmask.addr)) == addr)
+            return 1;
+    }
+    return 0;
+}
+
+void pico_ipv4_unreachable(struct pico_frame *f, int err)
+{
+    struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+#if defined PICO_SUPPORT_TCP || defined PICO_SUPPORT_UDP
+    f->transport_hdr = ((uint8_t *)f->net_hdr) + PICO_SIZE_IP4HDR;
+    pico_transport_error(f, hdr->proto, err);
+#endif
+}
+
+int pico_ipv4_cleanup_links(struct pico_device *dev)
+{
+    struct pico_tree_node *index = NULL, *_tmp = NULL;
+    struct pico_ipv4_link *link = NULL;
+
+    pico_tree_foreach_safe(index, &Tree_dev_link, _tmp) {
+        link = index->keyValue;
+        if (dev == link->dev)
+            pico_ipv4_link_del(dev, link->address);
+    }
+    return 0;
+}
+
+
+#endif
diff --git a/net/picotcp/modules/pico_ipv4.h b/net/picotcp/modules/pico_ipv4.h
new file mode 100644
index 0000000..2429815
--- /dev/null
+++ b/net/picotcp/modules/pico_ipv4.h
@@ -0,0 +1,124 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   .
+
+ *********************************************************************/
+#ifndef INCLUDE_PICO_IPV4
+#define INCLUDE_PICO_IPV4
+#include "pico_addressing.h"
+#include "pico_protocol.h"
+#include "pico_tree.h"
+#include "pico_device.h"
+
+#define PICO_IPV4_INADDR_ANY 0x00000000U
+
+#define PICO_IPV4_MTU (1500u)
+#define PICO_SIZE_IP4HDR (uint32_t)((sizeof(struct pico_ipv4_hdr)))
+#define PICO_IPV4_MAXPAYLOAD (PICO_IPV4_MTU - PICO_SIZE_IP4HDR)
+#define PICO_IPV4_DONTFRAG 0x4000U
+#define PICO_IPV4_MOREFRAG 0x2000U
+#define PICO_IPV4_EVIL      0x8000U
+#define PICO_IPV4_FRAG_MASK 0x1FFFU
+#define PICO_IPV4_DEFAULT_TTL 64
+#ifndef MBED
+    #define PICO_IPV4_FRAG_MAX_SIZE (uint32_t)(63 * 1024)
+#else
+    #define PICO_IPV4_FRAG_MAX_SIZE PICO_DEFAULT_SOCKETQ
+#endif
+
+extern struct pico_protocol pico_proto_ipv4;
+
+PACKED_STRUCT_DEF pico_ipv4_hdr {
+    uint8_t vhl;
+    uint8_t tos;
+    uint16_t len;
+    uint16_t id;
+    uint16_t frag;
+    uint8_t ttl;
+    uint8_t proto;
+    uint16_t crc;
+    struct pico_ip4 src;
+    struct pico_ip4 dst;
+    uint8_t options[];
+};
+
+PACKED_STRUCT_DEF pico_ipv4_pseudo_hdr
+{
+    struct pico_ip4 src;
+    struct pico_ip4 dst;
+    uint8_t zeros;
+    uint8_t proto;
+    uint16_t len;
+};
+
+/* Interface: link to device */
+struct pico_mcast_list;
+
+struct pico_ipv4_link
+{
+    struct pico_device *dev;
+    struct pico_ip4 address;
+    struct pico_ip4 netmask;
+#ifdef PICO_SUPPORT_MCAST
+    struct pico_tree *MCASTGroups;
+    uint8_t mcast_compatibility;
+    uint8_t mcast_last_query_interval;
+#endif
+};
+
+#ifdef PICO_SUPPORT_MCAST
+struct pico_mcast_group {
+    uint8_t filter_mode;
+    uint16_t reference_count;
+    struct pico_ip4 mcast_addr;
+    struct pico_tree MCASTSources;
+};
+#endif
+
+struct pico_ipv4_route
+{
+    struct pico_ip4 dest;
+    struct pico_ip4 netmask;
+    struct pico_ip4 gateway;
+    struct pico_ipv4_link *link;
+    uint32_t metric;
+};
+
+extern struct pico_tree Routes;
+
+
+int pico_ipv4_compare(struct pico_ip4 *a, struct pico_ip4 *b);
+int pico_ipv4_to_string(char *ipbuf, const uint32_t ip);
+int pico_string_to_ipv4(const char *ipstr, uint32_t *ip);
+int pico_ipv4_valid_netmask(uint32_t mask);
+int pico_ipv4_is_unicast(uint32_t address);
+int pico_ipv4_is_multicast(uint32_t address);
+int pico_ipv4_is_broadcast(uint32_t addr);
+int pico_ipv4_is_loopback(uint32_t addr);
+int pico_ipv4_is_valid_src(uint32_t addr, struct pico_device *dev);
+
+int pico_ipv4_link_add(struct pico_device *dev, struct pico_ip4 address, struct pico_ip4 netmask);
+int pico_ipv4_link_del(struct pico_device *dev, struct pico_ip4 address);
+int pico_ipv4_rebound(struct pico_frame *f);
+
+int pico_ipv4_frame_push(struct pico_frame *f, struct pico_ip4 *dst, uint8_t proto);
+struct pico_ipv4_link *pico_ipv4_link_get(struct pico_ip4 *address);
+struct pico_ipv4_link *pico_ipv4_link_by_dev(struct pico_device *dev);
+struct pico_ipv4_link *pico_ipv4_link_by_dev_next(struct pico_device *dev, struct pico_ipv4_link *last);
+struct pico_device *pico_ipv4_link_find(struct pico_ip4 *address);
+struct pico_ip4 *pico_ipv4_source_find(const struct pico_ip4 *dst);
+struct pico_device *pico_ipv4_source_dev_find(const struct pico_ip4 *dst);
+int pico_ipv4_route_add(struct pico_ip4 address, struct pico_ip4 netmask, struct pico_ip4 gateway, int metric, struct pico_ipv4_link *link);
+int pico_ipv4_route_del(struct pico_ip4 address, struct pico_ip4 netmask, int metric);
+struct pico_ip4 pico_ipv4_route_get_gateway(struct pico_ip4 *addr);
+void pico_ipv4_route_set_bcast_link(struct pico_ipv4_link *link);
+void pico_ipv4_unreachable(struct pico_frame *f, int err);
+
+int pico_ipv4_mcast_join(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter);
+int pico_ipv4_mcast_leave(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter);
+struct pico_ipv4_link *pico_ipv4_get_default_mcastlink(void);
+int pico_ipv4_cleanup_links(struct pico_device *dev);
+
+#endif /* _INCLUDE_PICO_IPV4 */
diff --git a/net/picotcp/modules/pico_ipv6.h b/net/picotcp/modules/pico_ipv6.h
new file mode 100644
index 0000000..a753531
--- /dev/null
+++ b/net/picotcp/modules/pico_ipv6.h
@@ -0,0 +1,153 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   .
+
+ *********************************************************************/
+#ifndef _INCLUDE_PICO_IPV6
+#define _INCLUDE_PICO_IPV6
+#include "pico_addressing.h"
+#include "pico_protocol.h"
+
+#define PICO_SIZE_IP6HDR ((uint32_t)(sizeof(struct pico_ipv6_hdr)))
+#define PICO_IPV6_DEFAULT_HOP 64
+#define PICO_IPV6_MIN_MTU 1280
+
+
+#define PICO_IPV6_EXTHDR_HOPBYHOP 0
+#define PICO_IPV6_EXTHDR_ROUTING 43
+#define PICO_IPV6_EXTHDR_FRAG 44
+#define PICO_IPV6_EXTHDR_ESP 50
+#define PICO_IPV6_EXTHDR_AUTH 51
+#define PICO_IPV6_EXTHDR_NONE 59
+#define PICO_IPV6_EXTHDR_DESTOPT 60
+
+
+
+
+extern const uint8_t PICO_IP6_ANY[PICO_SIZE_IP6];
+extern struct pico_protocol pico_proto_ipv6;
+extern struct pico_tree IPV6Routes;
+
+PACKED_STRUCT_DEF pico_ipv6_hdr {
+    uint32_t vtf;
+    uint16_t len;
+    uint8_t nxthdr;
+    uint8_t hop;
+    struct pico_ip6 src;
+    struct pico_ip6 dst;
+    uint8_t extensions[0];
+};
+
+PACKED_STRUCT_DEF pico_ipv6_pseudo_hdr
+{
+    struct pico_ip6 src;
+    struct pico_ip6 dst;
+    uint32_t len;
+    uint8_t zero[3];
+    uint8_t nxthdr;
+};
+
+struct pico_ipv6_link
+{
+    struct pico_device *dev;
+    struct pico_ip6 address;
+    struct pico_ip6 netmask;
+    uint8_t istentative : 1;
+    uint8_t isduplicate : 1;
+    struct pico_timer *dad_timer;
+    uint16_t dup_detect_retrans;
+    pico_time expire_time;
+};
+
+struct pico_ipv6_hbhoption {
+    uint8_t type;
+    uint8_t len;
+    uint8_t options[0];
+};
+
+struct pico_ipv6_destoption {
+    uint8_t type;
+    uint8_t len;
+    uint8_t options[0];
+};
+
+struct pico_ipv6_route
+{
+    struct pico_ip6 dest;
+    struct pico_ip6 netmask;
+    struct pico_ip6 gateway;
+    struct pico_ipv6_link *link;
+    uint32_t metric;
+};
+
+PACKED_STRUCT_DEF pico_ipv6_exthdr {
+    uint8_t nxthdr;
+
+    PACKED_UNION_DEF ipv6_ext_u {
+        PEDANTIC_STRUCT_DEF hopbyhop_s {
+            uint8_t len;
+            uint8_t options[0];
+        } hopbyhop;
+
+        PEDANTIC_STRUCT_DEF destopt_s {
+            uint8_t len;
+            uint8_t options[0];
+        } destopt;
+
+        PEDANTIC_STRUCT_DEF routing_s {
+            uint8_t len;
+            uint8_t routtype;
+            uint8_t segleft;
+        } routing;
+
+        PEDANTIC_STRUCT_DEF fragmentation_s {
+            uint8_t res;
+            uint8_t om[2];
+            uint8_t id[4];
+        } frag;
+    } ext;
+};
+
+int pico_ipv6_compare(struct pico_ip6 *a, struct pico_ip6 *b);
+int pico_string_to_ipv6(const char *ipstr, uint8_t *ip);
+int pico_ipv6_to_string(char *ipbuf, const uint8_t ip[PICO_SIZE_IP6]);
+int pico_ipv6_is_unicast(struct pico_ip6 *a);
+int pico_ipv6_is_multicast(const uint8_t addr[PICO_SIZE_IP6]);
+int pico_ipv6_is_allhosts_multicast(const uint8_t addr[PICO_SIZE_IP6]);
+int pico_ipv6_is_solnode_multicast(const uint8_t addr[PICO_SIZE_IP6], struct pico_device *dev);
+int pico_ipv6_is_global(const uint8_t addr[PICO_SIZE_IP6]);
+int pico_ipv6_is_uniquelocal(const uint8_t addr[PICO_SIZE_IP6]);
+int pico_ipv6_is_sitelocal(const uint8_t addr[PICO_SIZE_IP6]);
+int pico_ipv6_is_linklocal(const uint8_t addr[PICO_SIZE_IP6]);
+int pico_ipv6_is_solicited(const uint8_t addr[PICO_SIZE_IP6]);
+int pico_ipv6_is_unspecified(const uint8_t addr[PICO_SIZE_IP6]);
+int pico_ipv6_is_localhost(const uint8_t addr[PICO_SIZE_IP6]);
+
+int pico_ipv6_frame_push(struct pico_frame *f, struct pico_ip6 *src, struct pico_ip6 *dst, uint8_t proto, int is_dad);
+int pico_ipv6_route_add(struct pico_ip6 address, struct pico_ip6 netmask, struct pico_ip6 gateway, int metric, struct pico_ipv6_link *link);
+int pico_ipv6_route_del(struct pico_ip6 address, struct pico_ip6 netmask, struct pico_ip6 gateway, int metric, struct pico_ipv6_link *link);
+void pico_ipv6_unreachable(struct pico_frame *f, uint8_t code);
+
+struct pico_ipv6_link *pico_ipv6_link_add(struct pico_device *dev, struct pico_ip6 address, struct pico_ip6 netmask);
+int pico_ipv6_link_del(struct pico_device *dev, struct pico_ip6 address);
+int pico_ipv6_cleanup_links(struct pico_device *dev);
+struct pico_ipv6_link *pico_ipv6_link_istentative(struct pico_ip6 *address);
+struct pico_ipv6_link *pico_ipv6_link_get(struct pico_ip6 *address);
+struct pico_device *pico_ipv6_link_find(struct pico_ip6 *address);
+struct pico_ip6 pico_ipv6_route_get_gateway(struct pico_ip6 *addr);
+struct pico_ip6 *pico_ipv6_source_find(const struct pico_ip6 *dst);
+struct pico_device *pico_ipv6_source_dev_find(const struct pico_ip6 *dst);
+struct pico_ipv6_link *pico_ipv6_link_by_dev(struct pico_device *dev);
+struct pico_ipv6_link *pico_ipv6_link_by_dev_next(struct pico_device *dev, struct pico_ipv6_link *last);
+struct pico_ipv6_link *pico_ipv6_global_get(struct pico_device *dev);
+struct pico_ipv6_link *pico_ipv6_linklocal_get(struct pico_device *dev);
+struct pico_ipv6_link *pico_ipv6_sitelocal_get(struct pico_device *dev);
+struct pico_ipv6_link *pico_ipv6_prefix_configured(struct pico_ip6 *prefix);
+int pico_ipv6_lifetime_set(struct pico_ipv6_link *l, pico_time expire);
+void pico_ipv6_check_lifetime_expired(pico_time now, void *arg);
+int pico_ipv6_dev_routing_enable(struct pico_device *dev);
+int pico_ipv6_dev_routing_disable(struct pico_device *dev);
+void pico_ipv6_router_down(struct pico_ip6 *address);
+#endif
diff --git a/net/picotcp/modules/pico_ipv6_nd.h b/net/picotcp/modules/pico_ipv6_nd.h
new file mode 100644
index 0000000..f709b1e
--- /dev/null
+++ b/net/picotcp/modules/pico_ipv6_nd.h
@@ -0,0 +1,26 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+ *********************************************************************/
+#ifndef _INCLUDE_PICO_ND
+#define _INCLUDE_PICO_ND
+#include "pico_frame.h"
+
+/* RFC constants */
+#define PICO_ND_REACHABLE_TIME         30000 /* msec */
+#define PICO_ND_RETRANS_TIMER          1000 /* msec */
+
+struct pico_nd_hostvars {
+    uint8_t routing;
+    uint8_t hoplimit;
+    pico_time basetime;
+    pico_time reachabletime;
+    pico_time retranstime;
+};
+
+void pico_ipv6_nd_init(void);
+struct pico_eth *pico_ipv6_get_neighbor(struct pico_frame *f);
+void pico_ipv6_nd_postpone(struct pico_frame *f);
+int pico_ipv6_nd_recv(struct pico_frame *f);
+#endif
diff --git a/net/picotcp/modules/pico_mm.h b/net/picotcp/modules/pico_mm.h
new file mode 100644
index 0000000..29366d0
--- /dev/null
+++ b/net/picotcp/modules/pico_mm.h
@@ -0,0 +1,98 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   Authors: Gustav Janssens, Jonas Van Nieuwenberg, Sam Van Den Berge
+ *********************************************************************/
+
+
+#ifndef _INCLUDE_PICO_MM
+#define _INCLUDE_PICO_MM
+
+#include "pico_config.h"
+
+/*
+ * Memory init function, this will create a memory manager instance
+ * A memory_manager page will be created, along with one page of memory
+ * Memory can be asked for via the pico_mem_zalloc function
+ * More memory will be allocated to the memory manager according to its needs
+ * A maximum amount of memory of uint32_t memsize can be allocated
+ */
+void pico_mem_init(uint32_t memsize);
+/*
+ * Memory deinit function, this will free all memory occupied by the current
+ * memory manager instance.
+ */
+void pico_mem_deinit(void);
+/*
+ * Zero-initialized malloc function, will reserve a memory segment of length uint32_t len
+ * This memory will be quickly allocated in a slab of fixed size if possible
+ * or less optimally in the heap for a small variable size
+ * The fixed size of the slabs can be changed dynamically via a statistics engine
+ */
+void*pico_mem_zalloc(size_t len);
+/*
+ * Free function, free a block of memory pointed to by ptr.
+ * Unused memory is only returned to the system's control by pico_mem_cleanup
+ */
+void pico_mem_free(void*ptr);
+/*
+ * This cleanup function will be provided by the memory manager
+ * It can be called during processor downtime
+ * This function will return unused pages to the system's control
+ * Pages are unused if they no longer contain slabs or heap, and they have been idle for a longer time
+ */
+void pico_mem_cleanup(uint32_t timestamp);
+
+
+
+#ifdef PICO_SUPPORT_MM_PROFILING
+/***********************************************************************************************************************
+ ***********************************************************************************************************************
+   MEMORY PROFILING FUNCTIONS
+ ***********************************************************************************************************************
+ ***********************************************************************************************************************/
+/* General info struct */
+struct profiling_data
+{
+    uint32_t free_heap_space;
+    uint32_t free_slab_space;
+    uint32_t used_heap_space;
+    uint32_t used_slab_space;
+};
+
+/*
+ * This function fills up a struct with used and free slab and heap space in the memory manager
+ * The user is responsible for resource managment
+ */
+void pico_mem_profile_collect_data(struct profiling_data*profiling_page_struct);
+
+/*
+ * This function prints the general structure of the memory manager
+ * Printf in this function can be rerouted to send this data over a serial port, or to write it away to memory
+ */
+void pico_mem_profile_scan_data(void);
+
+/*
+ * This function returns the total size that the manager has received from the system
+ * This can give an indication of the total system resource commitment, but keep in mind that
+ * there can be many free blocks in this "used" size
+ * Together with pico_mem_profile_collect_data, this can give a good estimation of the total
+ * resource commitment
+ */
+uint32_t pico_mem_profile_used_size(void);
+
+/*
+ * This function returns a pointer to page 0, the main memory manager housekeeping (struct pico_mem_manager).
+ * This can be used to collect data about the memory in user defined functions.
+ * Use with care!
+ */
+void*pico_mem_profile_manager(void);
+
+/*
+ * paramter manager is a pointer to a struct pico_mem_manager
+ */
+void pico_mem_init_profiling(void*manager, uint32_t memsize);
+#endif /* PICO_SUPPORT_MM_PROFILING */
+
+#endif /* _INCLUDE_PICO_MM */
diff --git a/net/picotcp/modules/pico_nat.h b/net/picotcp/modules/pico_nat.h
new file mode 100644
index 0000000..5237501
--- /dev/null
+++ b/net/picotcp/modules/pico_nat.h
@@ -0,0 +1,90 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   .
+
+   Authors: Kristof Roelants, Simon Maes, Brecht Van Cauwenberghe
+ *********************************************************************/
+
+#ifndef INCLUDE_PICO_NAT
+#define INCLUDE_PICO_NAT
+#include "pico_frame.h"
+
+#define PICO_NAT_PORT_FORWARD_DEL 0
+#define PICO_NAT_PORT_FORWARD_ADD 1
+
+#ifdef PICO_SUPPORT_NAT
+void pico_ipv4_nat_print_table(void);
+int pico_ipv4_nat_find(uint16_t nat_port, struct pico_ip4 *src_addr, uint16_t src_port, uint8_t proto);
+int pico_ipv4_port_forward(struct pico_ip4 nat_addr, uint16_t nat_port, struct pico_ip4 src_addr, uint16_t src_port, uint8_t proto, uint8_t flag);
+
+int pico_ipv4_nat_inbound(struct pico_frame *f, struct pico_ip4 *link_addr);
+int pico_ipv4_nat_outbound(struct pico_frame *f, struct pico_ip4 *link_addr);
+int pico_ipv4_nat_enable(struct pico_ipv4_link *link);
+int pico_ipv4_nat_disable(void);
+int pico_ipv4_nat_is_enabled(struct pico_ip4 *link_addr);
+#else
+
+#define pico_ipv4_nat_print_table() do {} while(0)
+static inline int pico_ipv4_nat_inbound(struct pico_frame *f, struct pico_ip4 *link_addr)
+{
+    (void)f;
+    (void)link_addr;
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+    return -1;
+}
+
+static inline int pico_ipv4_nat_outbound(struct pico_frame *f, struct pico_ip4 *link_addr)
+{
+    (void)f;
+    (void)link_addr;
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+    return -1;
+}
+
+static inline int pico_ipv4_nat_enable(struct pico_ipv4_link *link)
+{
+    (void)link;
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+    return -1;
+}
+
+static inline int pico_ipv4_nat_disable(void)
+{
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+    return -1;
+}
+
+static inline int pico_ipv4_nat_is_enabled(struct pico_ip4 *link_addr)
+{
+    (void)link_addr;
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+    return -1;
+}
+
+static inline int pico_ipv4_nat_find(uint16_t nat_port, struct pico_ip4 *src_addr, uint16_t src_port, uint8_t proto)
+{
+    (void)nat_port;
+    (void)src_addr;
+    (void)src_port;
+    (void)proto;
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+    return -1;
+}
+
+static inline int pico_ipv4_port_forward(struct pico_ip4 nat_addr, uint16_t nat_port, struct pico_ip4 src_addr, uint16_t src_port, uint8_t proto, uint8_t flag)
+{
+    (void)nat_addr;
+    (void)nat_port;
+    (void)src_addr;
+    (void)src_port;
+    (void)proto;
+    (void)flag;
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+    return -1;
+}
+#endif
+
+#endif /* _INCLUDE_PICO_NAT */
+
diff --git a/net/picotcp/modules/pico_olsr.h b/net/picotcp/modules/pico_olsr.h
new file mode 100644
index 0000000..11c3bf0
--- /dev/null
+++ b/net/picotcp/modules/pico_olsr.h
@@ -0,0 +1,32 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   Authors: Daniele Lacamera
+ *********************************************************************/
+#ifndef PICO_OLSR_H
+#define PICO_OLSR_H
+
+
+/* Objects */
+struct olsr_route_entry
+{
+    struct olsr_route_entry         *next;
+    uint32_t time_left;
+    struct pico_ip4 destination;
+    struct olsr_route_entry         *gateway;
+    struct pico_device              *iface;
+    uint16_t metric;
+    uint8_t link_type;
+    struct olsr_route_entry         *children;
+    uint16_t ansn;
+    uint16_t seq;
+    uint8_t lq, nlq;
+    uint8_t                         *advertised_tc;
+};
+
+
+void pico_olsr_init(void);
+int pico_olsr_add(struct pico_device *dev);
+struct olsr_route_entry *olsr_get_ethentry(struct pico_device *vif);
+#endif
diff --git a/net/picotcp/modules/pico_socket_tcp.h b/net/picotcp/modules/pico_socket_tcp.h
new file mode 100644
index 0000000..6479103
--- /dev/null
+++ b/net/picotcp/modules/pico_socket_tcp.h
@@ -0,0 +1,33 @@
+#ifndef PICO_SOCKET_TCP_H
+#define PICO_SOCKET_TCP_H
+#include "pico_socket.h"
+
+#ifdef PICO_SUPPORT_TCP
+
+/* Functions/macros: conditional! */
+
+# define IS_NAGLE_ENABLED(s) (!(!(!(s->opt_flags & (1 << PICO_SOCKET_OPT_TCPNODELAY)))))
+int pico_setsockopt_tcp(struct pico_socket *s, int option, void *value);
+int pico_getsockopt_tcp(struct pico_socket *s, int option, void *value);
+int pico_socket_tcp_deliver(struct pico_sockport *sp, struct pico_frame *f);
+void pico_socket_tcp_delete(struct pico_socket *s);
+void pico_socket_tcp_cleanup(struct pico_socket *sock);
+struct pico_socket *pico_socket_tcp_open(uint16_t family);
+int pico_socket_tcp_read(struct pico_socket *s, void *buf, uint32_t len);
+void transport_flags_update(struct pico_frame *, struct pico_socket *);
+
+#else
+#   define pico_getsockopt_tcp(...) (-1)
+#   define pico_setsockopt_tcp(...) (-1)
+#   define pico_socket_tcp_deliver(...) (-1)
+#   define IS_NAGLE_ENABLED(s) (0)
+#   define pico_socket_tcp_delete(...) do {} while(0)
+#   define pico_socket_tcp_cleanup(...) do {} while(0)
+#   define pico_socket_tcp_open(f) (NULL)
+#   define pico_socket_tcp_read(...) (-1)
+#   define transport_flags_update(...) do {} while(0)
+
+#endif
+
+
+#endif
diff --git a/net/picotcp/modules/pico_socket_udp.c b/net/picotcp/modules/pico_socket_udp.c
new file mode 100644
index 0000000..ea7e9e1
--- /dev/null
+++ b/net/picotcp/modules/pico_socket_udp.c
@@ -0,0 +1,250 @@
+#include "pico_config.h"
+#include "pico_socket.h"
+#include "pico_udp.h"
+#include "pico_socket_multicast.h"
+#include "pico_ipv4.h"
+#include "pico_ipv6.h"
+#include "pico_socket_udp.h"
+
+#define UDP_FRAME_OVERHEAD (sizeof(struct pico_frame))
+
+
+struct pico_socket *pico_socket_udp_open(void)
+{
+    struct pico_socket *s = NULL;
+#ifdef PICO_SUPPORT_UDP
+    s = pico_udp_open();
+    if (!s) {
+        pico_err = PICO_ERR_ENOMEM;
+        return NULL;
+    }
+
+    s->proto = &pico_proto_udp;
+    s->q_in.overhead = UDP_FRAME_OVERHEAD;
+    s->q_out.overhead = UDP_FRAME_OVERHEAD;
+#endif
+    return s;
+}
+
+
+#ifdef PICO_SUPPORT_IPV4
+static inline int pico_socket_udp_deliver_ipv4_mcast_initial_checks(struct pico_socket *s, struct pico_frame *f)
+{
+    struct pico_ip4 p_dst;
+    struct pico_ipv4_hdr *ip4hdr;
+
+    ip4hdr = (struct pico_ipv4_hdr*)(f->net_hdr);
+    p_dst.addr = ip4hdr->dst.addr;
+    if (pico_ipv4_is_multicast(p_dst.addr) && (pico_socket_mcast_filter(s, (union pico_address *)&ip4hdr->dst, (union pico_address *)&ip4hdr->src) < 0))
+        return -1;
+
+
+    if ((pico_ipv4_link_get(&ip4hdr->src)) && (PICO_SOCKET_GETOPT(s, PICO_SOCKET_OPT_MULTICAST_LOOP) == 0u)) {
+        /* Datagram from ourselves, Loop disabled, discarding. */
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static int pico_socket_udp_deliver_ipv4_mcast(struct pico_socket *s, struct pico_frame *f)
+{
+    struct pico_ip4 s_local;
+    struct pico_frame *cpy;
+    struct pico_device *dev = pico_ipv4_link_find(&s->local_addr.ip4);
+
+    s_local.addr = s->local_addr.ip4.addr;
+
+    if (pico_socket_udp_deliver_ipv4_mcast_initial_checks(s, f) < 0)
+        return 0;
+
+
+
+    if ((s_local.addr == PICO_IPV4_INADDR_ANY) || /* If our local ip is ANY, or.. */
+        (dev == f->dev)) {     /* the source of the bcast packet is a neighbor... */
+        cpy = pico_frame_copy(f);
+        if (!cpy)
+            return -1;
+
+        if (pico_enqueue(&s->q_in, cpy) > 0) {
+            if (s->wakeup)
+                s->wakeup(PICO_SOCK_EV_RD, s);
+        }
+        else
+            pico_frame_discard(cpy);
+    }
+
+    return 0;
+}
+
+static int pico_socket_udp_deliver_ipv4_unicast(struct pico_socket *s, struct pico_frame *f)
+{
+    struct pico_frame *cpy;
+    /* Either local socket is ANY, or matches dst */
+    cpy = pico_frame_copy(f);
+    if (!cpy)
+        return -1;
+
+    if (pico_enqueue(&s->q_in, cpy) > 0) {
+        if (s->wakeup)
+            s->wakeup(PICO_SOCK_EV_RD, s);
+    } else {
+        pico_frame_discard(cpy);
+    }
+
+    return 0;
+}
+
+
+static int pico_socket_udp_deliver_ipv4(struct pico_socket *s, struct pico_frame *f)
+{
+    int ret = 0;
+    struct pico_ip4 s_local, p_dst;
+    struct pico_ipv4_hdr *ip4hdr;
+    ip4hdr = (struct pico_ipv4_hdr*)(f->net_hdr);
+    s_local.addr = s->local_addr.ip4.addr;
+    p_dst.addr = ip4hdr->dst.addr;
+    if ((pico_ipv4_is_broadcast(p_dst.addr)) || pico_ipv4_is_multicast(p_dst.addr)) {
+        ret = pico_socket_udp_deliver_ipv4_mcast(s, f);
+    } else if ((s_local.addr == PICO_IPV4_INADDR_ANY) || (s_local.addr == p_dst.addr)) {
+        ret = pico_socket_udp_deliver_ipv4_unicast(s, f);
+    }
+
+    pico_frame_discard(f);
+    return ret;
+}
+#endif
+
+#ifdef PICO_SUPPORT_IPV6
+static inline int pico_socket_udp_deliver_ipv6_mcast(struct pico_socket *s, struct pico_frame *f)
+{
+    struct pico_ipv6_hdr *ip6hdr;
+    struct pico_frame *cpy;
+    struct pico_device *dev = pico_ipv6_link_find(&s->local_addr.ip6);
+
+    ip6hdr = (struct pico_ipv6_hdr*)(f->net_hdr);
+
+    if ((pico_ipv6_link_get(&ip6hdr->src)) && (PICO_SOCKET_GETOPT(s, PICO_SOCKET_OPT_MULTICAST_LOOP) == 0u)) {
+        /* Datagram from ourselves, Loop disabled, discarding. */
+        return 0;
+    }
+
+
+    if (pico_ipv6_is_unspecified(s->local_addr.ip6.addr) || /* If our local ip is ANY, or.. */
+        (dev == f->dev)) {     /* the source of the bcast packet is a neighbor... */
+        cpy = pico_frame_copy(f);
+        if (!cpy)
+            return -1;
+
+        if (pico_enqueue(&s->q_in, cpy) > 0) {
+            if (s->wakeup)
+                s->wakeup(PICO_SOCK_EV_RD, s);
+        }
+        else
+            pico_frame_discard(cpy);
+    }
+
+    return 0;
+}
+
+static int pico_socket_udp_deliver_ipv6(struct pico_socket *s, struct pico_frame *f)
+{
+    struct pico_ip6 s_local, p_dst;
+    struct pico_ipv6_hdr *ip6hdr;
+    struct pico_frame *cpy;
+    ip6hdr = (struct pico_ipv6_hdr*)(f->net_hdr);
+    s_local = s->local_addr.ip6;
+    p_dst = ip6hdr->dst;
+    if ((pico_ipv6_is_multicast(p_dst.addr))) {
+        return pico_socket_udp_deliver_ipv6_mcast(s, f);
+    } else if (pico_ipv6_is_unspecified(s->local_addr.ip6.addr) || (pico_ipv6_compare(&s_local, &p_dst) == 0))
+    { /* Either local socket is ANY, or matches dst */
+        cpy = pico_frame_copy(f);
+        if (!cpy)
+            return -1;
+
+        if (pico_enqueue(&s->q_in, cpy) > 0) {
+            if (s->wakeup)
+                s->wakeup(PICO_SOCK_EV_RD, s);
+        }
+    }
+
+    pico_frame_discard(f);
+    return 0;
+}
+#endif
+
+
+int pico_socket_udp_deliver(struct pico_sockport *sp, struct pico_frame *f)
+{
+    struct pico_tree_node *index = NULL;
+    struct pico_tree_node *_tmp;
+    struct pico_socket *s = NULL;
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+    #ifdef PICO_SUPPORT_UDP
+    pico_err = PICO_ERR_NOERR;
+    pico_tree_foreach_safe(index, &sp->socks, _tmp){
+        s = index->keyValue;
+        if (IS_IPV4(f)) { /* IPV4 */
+#ifdef PICO_SUPPORT_IPV4
+            return pico_socket_udp_deliver_ipv4(s, f);
+#endif
+        } else if (IS_IPV6(f)) {
+#ifdef PICO_SUPPORT_IPV6
+            return pico_socket_udp_deliver_ipv6(s, f);
+#endif
+        } else {
+            /* something wrong in the packet header*/
+        }
+    } /* FOREACH */
+    pico_frame_discard(f);
+    if (s)
+        return 0;
+
+    pico_err = PICO_ERR_ENXIO;
+  #endif
+    return -1;
+}
+
+int pico_setsockopt_udp(struct pico_socket *s, int option, void *value)
+{
+    switch(option) {
+    case PICO_SOCKET_OPT_RCVBUF:
+        s->q_in.max_size = (*(uint32_t*)value);
+        return 0;
+    case PICO_SOCKET_OPT_SNDBUF:
+        s->q_out.max_size = (*(uint32_t*)value);
+        return 0;
+    }
+
+    /* switch's default */
+#ifdef PICO_SUPPORT_MCAST
+    return pico_setsockopt_mcast(s, option, value);
+#else
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+#endif
+}
+
+int pico_getsockopt_udp(struct pico_socket *s, int option, void *value)
+{
+    uint32_t *val = (uint32_t *)value;
+    switch(option) {
+    case PICO_SOCKET_OPT_RCVBUF:
+        *val = s->q_in.max_size;
+        return 0;
+    case PICO_SOCKET_OPT_SNDBUF:
+        *val = s->q_out.max_size;
+        return 0;
+    }
+
+    /* switch's default */
+#ifdef PICO_SUPPORT_MCAST
+    return pico_getsockopt_mcast(s, option, value);
+#else
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+#endif
+}
+
diff --git a/net/picotcp/modules/pico_socket_udp.h b/net/picotcp/modules/pico_socket_udp.h
new file mode 100644
index 0000000..6b3a4c9
--- /dev/null
+++ b/net/picotcp/modules/pico_socket_udp.h
@@ -0,0 +1,19 @@
+#ifndef PICO_SOCKET_UDP_H
+#define PICO_SOCKET_UDP_H
+
+struct pico_socket *pico_socket_udp_open(void);
+int pico_socket_udp_deliver(struct pico_sockport *sp, struct pico_frame *f);
+
+
+#ifdef PICO_SUPPORT_UDP
+int pico_setsockopt_udp(struct pico_socket *s, int option, void *value);
+int pico_getsockopt_udp(struct pico_socket *s, int option, void *value);
+#   define pico_socket_udp_recv(s, buf, len, addr, port) pico_udp_recv(s, buf, len, addr, port, NULL)
+#else
+#   define pico_socket_udp_recv(...) (0)
+#   define pico_getsockopt_udp(...) (-1)
+#   define pico_setsockopt_udp(...) (-1)
+#endif
+
+
+#endif
diff --git a/net/picotcp/modules/pico_tcp.h b/net/picotcp/modules/pico_tcp.h
new file mode 100644
index 0000000..d5003a8
--- /dev/null
+++ b/net/picotcp/modules/pico_tcp.h
@@ -0,0 +1,102 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   .
+
+ *********************************************************************/
+#ifndef INCLUDE_PICO_TCP
+#define INCLUDE_PICO_TCP
+#include "pico_addressing.h"
+#include "pico_protocol.h"
+#include "pico_socket.h"
+
+extern struct pico_protocol pico_proto_tcp;
+
+PACKED_STRUCT_DEF pico_tcp_hdr {
+    struct pico_trans trans;
+    uint32_t seq;
+    uint32_t ack;
+    uint8_t len;
+    uint8_t flags;
+    uint16_t rwnd;
+    uint16_t crc;
+    uint16_t urgent;
+};
+
+PACKED_STRUCT_DEF tcp_pseudo_hdr_ipv4
+{
+    struct pico_ip4 src;
+    struct pico_ip4 dst;
+    uint16_t tcp_len;
+    uint8_t res;
+    uint8_t proto;
+};
+
+#define PICO_TCPHDR_SIZE 20
+#define PICO_SIZE_TCPOPT_SYN 20
+#define PICO_SIZE_TCPHDR (uint32_t)(sizeof(struct pico_tcp_hdr))
+
+/* TCP options */
+#define PICO_TCP_OPTION_END         0x00
+#define PICO_TCPOPTLEN_END        1u
+#define PICO_TCP_OPTION_NOOP        0x01
+#define PICO_TCPOPTLEN_NOOP       1
+#define PICO_TCP_OPTION_MSS         0x02
+#define PICO_TCPOPTLEN_MSS        4
+#define PICO_TCP_OPTION_WS          0x03
+#define PICO_TCPOPTLEN_WS         3u
+#define PICO_TCP_OPTION_SACK_OK        0x04
+#define PICO_TCPOPTLEN_SACK_OK       2
+#define PICO_TCP_OPTION_SACK        0x05
+#define PICO_TCPOPTLEN_SACK       2 /* Plus the block */
+#define PICO_TCP_OPTION_TIMESTAMP   0x08
+#define PICO_TCPOPTLEN_TIMESTAMP  10u
+
+/* TCP flags */
+#define PICO_TCP_FIN 0x01u
+#define PICO_TCP_SYN 0x02u
+#define PICO_TCP_RST 0x04u
+#define PICO_TCP_PSH 0x08u
+#define PICO_TCP_ACK 0x10u
+#define PICO_TCP_URG 0x20u
+#define PICO_TCP_ECN 0x40u
+#define PICO_TCP_CWR 0x80u
+
+#define PICO_TCP_SYNACK    (PICO_TCP_SYN | PICO_TCP_ACK)
+#define PICO_TCP_PSHACK    (PICO_TCP_PSH | PICO_TCP_ACK)
+#define PICO_TCP_FINACK    (PICO_TCP_FIN | PICO_TCP_ACK)
+#define PICO_TCP_FINPSHACK (PICO_TCP_FIN | PICO_TCP_PSH | PICO_TCP_ACK)
+#define PICO_TCP_RSTACK    (PICO_TCP_RST | PICO_TCP_ACK)
+
+
+PACKED_STRUCT_DEF pico_tcp_option
+{
+    uint8_t kind;
+    uint8_t len;
+};
+
+struct pico_socket *pico_tcp_open(uint16_t family);
+uint32_t pico_tcp_read(struct pico_socket *s, void *buf, uint32_t len);
+int pico_tcp_initconn(struct pico_socket *s);
+int pico_tcp_input(struct pico_socket *s, struct pico_frame *f);
+uint16_t pico_tcp_checksum(struct pico_frame *f);
+uint16_t pico_tcp_checksum_ipv4(struct pico_frame *f);
+#ifdef PICO_SUPPORT_IPV6
+uint16_t pico_tcp_checksum_ipv6(struct pico_frame *f);
+#endif
+uint16_t pico_tcp_overhead(struct pico_socket *s);
+int pico_tcp_output(struct pico_socket *s, int loop_score);
+int pico_tcp_queue_in_is_empty(struct pico_socket *s);
+int pico_tcp_reply_rst(struct pico_frame *f);
+void pico_tcp_cleanup_queues(struct pico_socket *sck);
+void pico_tcp_notify_closing(struct pico_socket *sck);
+void pico_tcp_flags_update(struct pico_frame *f, struct pico_socket *s);
+int pico_tcp_set_bufsize_in(struct pico_socket *s, uint32_t value);
+int pico_tcp_set_bufsize_out(struct pico_socket *s, uint32_t value);
+int pico_tcp_get_bufsize_in(struct pico_socket *s, uint32_t *value);
+int pico_tcp_get_bufsize_out(struct pico_socket *s, uint32_t *value);
+uint16_t pico_tcp_get_socket_mss(struct pico_socket *s);
+int pico_tcp_check_listen_close(struct pico_socket *s);
+
+#endif
diff --git a/net/picotcp/modules/pico_udp.c b/net/picotcp/modules/pico_udp.c
new file mode 100644
index 0000000..b7c61e4
--- /dev/null
+++ b/net/picotcp/modules/pico_udp.c
@@ -0,0 +1,216 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   .
+
+   Authors: Daniele Lacamera
+ *********************************************************************/
+
+
+#include "pico_udp.h"
+#include "pico_config.h"
+#include "pico_eth.h"
+#include "pico_socket.h"
+#include "pico_stack.h"
+
+#define UDP_FRAME_OVERHEAD (sizeof(struct pico_frame))
+#define udp_dbg(...) do {} while(0)
+
+/* Queues */
+static struct pico_queue udp_in = {
+    0
+};
+static struct pico_queue udp_out = {
+    0
+};
+
+
+/* Functions */
+
+uint16_t pico_udp_checksum_ipv4(struct pico_frame *f)
+{
+    struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+    struct pico_udp_hdr *udp_hdr = (struct pico_udp_hdr *) f->transport_hdr;
+    struct pico_socket *s = f->sock;
+    struct pico_ipv4_pseudo_hdr pseudo;
+
+    if (s) {
+        /* Case of outgoing frame */
+        udp_dbg("UDP CRC: on outgoing frame\n");
+        pseudo.src.addr = s->local_addr.ip4.addr;
+        pseudo.dst.addr = s->remote_addr.ip4.addr;
+    } else {
+        /* Case of incomming frame */
+        udp_dbg("UDP CRC: on incomming frame\n");
+        pseudo.src.addr = hdr->src.addr;
+        pseudo.dst.addr = hdr->dst.addr;
+    }
+
+    pseudo.zeros = 0;
+    pseudo.proto = PICO_PROTO_UDP;
+    pseudo.len = short_be(f->transport_len);
+
+    return pico_dualbuffer_checksum(&pseudo, sizeof(struct pico_ipv4_pseudo_hdr), udp_hdr, f->transport_len);
+}
+
+#ifdef PICO_SUPPORT_IPV6
+uint16_t pico_udp_checksum_ipv6(struct pico_frame *f)
+{
+    struct pico_ipv6_hdr *ipv6_hdr = (struct pico_ipv6_hdr *)f->net_hdr;
+    struct pico_udp_hdr *udp_hdr = (struct pico_udp_hdr *)f->transport_hdr;
+    struct pico_ipv6_pseudo_hdr pseudo = {
+        .src = {{0}}, .dst = {{0}}, .len = 0, .zero = {0}, .nxthdr = 0
+    };
+    struct pico_socket *s = f->sock;
+    struct pico_remote_endpoint *remote_endpoint = (struct pico_remote_endpoint *)f->info;
+
+    /* XXX If the IPv6 packet contains a Routing header, the Destination
+     *     Address used in the pseudo-header is that of the final destination */
+    if (s) {
+        /* Case of outgoing frame */
+        pseudo.src = s->local_addr.ip6;
+        if (remote_endpoint)
+            pseudo.dst = remote_endpoint->remote_addr.ip6;
+        else
+            pseudo.dst = s->remote_addr.ip6;
+    } else {
+        /* Case of incomming frame */
+        pseudo.src = ipv6_hdr->src;
+        pseudo.dst = ipv6_hdr->dst;
+    }
+
+    pseudo.len = long_be(f->transport_len);
+    pseudo.nxthdr = PICO_PROTO_UDP;
+
+    return pico_dualbuffer_checksum(&pseudo, sizeof(struct pico_ipv6_pseudo_hdr), udp_hdr, f->transport_len);
+}
+#endif
+
+
+
+static int pico_udp_process_out(struct pico_protocol *self, struct pico_frame *f)
+{
+    IGNORE_PARAMETER(self);
+    return (int)pico_network_send(f);
+}
+
+static int pico_udp_push(struct pico_protocol *self, struct pico_frame *f)
+{
+    struct pico_udp_hdr *hdr = (struct pico_udp_hdr *) f->transport_hdr;
+    struct pico_remote_endpoint *remote_endpoint = (struct pico_remote_endpoint *) f->info;
+
+    /* this (fragmented) frame should contain a transport header */
+    if (f->transport_hdr != f->payload) {
+        hdr->trans.sport = f->sock->local_port;
+        if (remote_endpoint) {
+            hdr->trans.dport = remote_endpoint->remote_port;
+        } else {
+            hdr->trans.dport = f->sock->remote_port;
+        }
+
+        hdr->len = short_be(f->transport_len);
+
+        /* do not perform CRC validation. If you want to, a system needs to be
+           implemented to calculate the CRC over the total payload of a
+           fragmented payload
+         */
+        hdr->crc = 0;
+    }
+
+    if (pico_enqueue(self->q_out, f) > 0) {
+        return f->payload_len;
+    } else {
+        return 0;
+    }
+}
+
+/* Interface: protocol definition */
+struct pico_protocol pico_proto_udp = {
+    .name = "udp",
+    .proto_number = PICO_PROTO_UDP,
+    .layer = PICO_LAYER_TRANSPORT,
+    .process_in = pico_transport_process_in,
+    .process_out = pico_udp_process_out,
+    .push = pico_udp_push,
+    .q_in = &udp_in,
+    .q_out = &udp_out,
+};
+
+
+
+struct pico_socket *pico_udp_open(void)
+{
+    struct pico_socket_udp *u = PICO_ZALLOC(sizeof(struct pico_socket_udp));
+    if (!u)
+        return NULL;
+
+    u->mode = PICO_UDP_MODE_UNICAST;
+
+#ifdef PICO_SUPPORT_MCAST
+    u->mc_ttl = PICO_IP_DEFAULT_MULTICAST_TTL;
+    /* enable multicast loopback by default */
+    u->sock.opt_flags |= (1 << PICO_SOCKET_OPT_MULTICAST_LOOP);
+#endif
+
+    return &u->sock;
+}
+
+static void pico_udp_get_msginfo(struct pico_frame *f, struct pico_msginfo *msginfo)
+{
+    msginfo->dev = f->dev;
+    if (!msginfo || !f->net_hdr)
+        return;
+
+    if (IS_IPV4(f)) { /* IPV4 */
+#ifdef PICO_SUPPORT_IPV4
+        struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *)(f->net_hdr);
+        msginfo->ttl = hdr->ttl;
+        msginfo->tos = hdr->tos;
+#endif
+    } else {
+#ifdef PICO_SUPPORT_IPV6
+        struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)(f->net_hdr);
+        msginfo->ttl = hdr->hop;
+        msginfo->tos = (hdr->vtf >> 20) & 0xFF; /* IPv6 traffic class */
+#endif
+    }
+}
+
+uint16_t pico_udp_recv(struct pico_socket *s, void *buf, uint16_t len, void *src, uint16_t *port, struct pico_msginfo *msginfo)
+{
+    struct pico_frame *f = pico_queue_peek(&s->q_in);
+    if (f) {
+        if(!f->payload_len) {
+            f->payload = f->transport_hdr + sizeof(struct pico_udp_hdr);
+            f->payload_len = (uint16_t)(f->transport_len - sizeof(struct pico_udp_hdr));
+        }
+
+        udp_dbg("expected: %d, got: %d\n", len, f->payload_len);
+        if (src)
+            pico_store_network_origin(src, f);
+
+        if (port) {
+            struct pico_trans *hdr = (struct pico_trans *)f->transport_hdr;
+            *port = hdr->sport;
+        }
+
+        if (msginfo) {
+            pico_udp_get_msginfo(f, msginfo);
+        }
+
+        if (f->payload_len > len) {
+            memcpy(buf, f->payload, len);
+            f->payload += len;
+            f->payload_len = (uint16_t)(f->payload_len - len);
+            return len;
+        } else {
+            uint16_t ret = f->payload_len;
+            memcpy(buf, f->payload, f->payload_len);
+            f = pico_dequeue(&s->q_in);
+            pico_frame_discard(f);
+            return ret;
+        }
+    } else return 0;
+}
+
diff --git a/net/picotcp/modules/pico_udp.h b/net/picotcp/modules/pico_udp.h
new file mode 100644
index 0000000..80b97a9
--- /dev/null
+++ b/net/picotcp/modules/pico_udp.h
@@ -0,0 +1,45 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   .
+
+ *********************************************************************/
+#ifndef INCLUDE_PICO_UDP
+#define INCLUDE_PICO_UDP
+#include "pico_addressing.h"
+#include "pico_protocol.h"
+#include "pico_socket.h"
+#define PICO_UDP_MODE_UNICAST 0x01
+#define PICO_UDP_MODE_MULTICAST 0x02
+#define PICO_UDP_MODE_BROADCAST 0xFF
+
+struct pico_socket_udp
+{
+    struct pico_socket sock;
+    int mode;
+    uint8_t mc_ttl; /* Multicasting TTL */
+};
+
+
+extern struct pico_protocol pico_proto_udp;
+
+PACKED_STRUCT_DEF pico_udp_hdr {
+    struct pico_trans trans;
+    uint16_t len;
+    uint16_t crc;
+};
+#define PICO_UDPHDR_SIZE 8
+
+struct pico_socket *pico_udp_open(void);
+uint16_t pico_udp_recv(struct pico_socket *s, void *buf, uint16_t len, void *src, uint16_t *port, struct pico_msginfo *msginfo);
+uint16_t pico_udp_checksum_ipv4(struct pico_frame *f);
+
+#ifdef PICO_SUPPORT_IPV6
+uint16_t pico_udp_checksum_ipv6(struct pico_frame *f);
+#endif
+
+
+int pico_udp_setsockopt(struct pico_socket *s, int option, void *value);
+
+#endif
diff --git a/net/picotcp/stack/pico_device.c b/net/picotcp/stack/pico_device.c
new file mode 100644
index 0000000..224b37f
--- /dev/null
+++ b/net/picotcp/stack/pico_device.c
@@ -0,0 +1,408 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   .
+
+   Authors: Daniele Lacamera
+ *********************************************************************/
+
+#include "pico_config.h"
+#include "pico_device.h"
+#include "pico_stack.h"
+#include "pico_protocol.h"
+#include "pico_tree.h"
+#include "pico_ipv6.h"
+#include "pico_ipv4.h"
+#include "pico_icmp6.h"
+#include "pico_eth.h"
+#define PICO_DEVICE_DEFAULT_MTU (1500)
+
+struct pico_devices_rr_info {
+    struct pico_tree_node *node_in, *node_out;
+};
+
+static struct pico_devices_rr_info Devices_rr_info = {
+    NULL, NULL
+};
+
+static int pico_dev_cmp(void *ka, void *kb)
+{
+    struct pico_device *a = ka, *b = kb;
+    if (a->hash < b->hash)
+        return -1;
+
+    if (a->hash > b->hash)
+        return 1;
+
+    return 0;
+}
+
+PICO_TREE_DECLARE(Device_tree, pico_dev_cmp);
+
+#ifdef PICO_SUPPORT_IPV6
+static void device_init_ipv6_final(struct pico_device *dev, struct pico_ip6 *linklocal)
+{
+    dev->hostvars.basetime = PICO_ND_REACHABLE_TIME;
+    /* RFC 4861 $6.3.2 value between 0.5 and 1.5 times basetime */
+    dev->hostvars.reachabletime = ((5 + (pico_rand() % 10)) * PICO_ND_REACHABLE_TIME) / 10;
+    dev->hostvars.retranstime = PICO_ND_RETRANS_TIMER;
+    pico_icmp6_router_solicitation(dev, linklocal);
+    dev->hostvars.hoplimit = PICO_IPV6_DEFAULT_HOP;
+}
+
+struct pico_ipv6_link *pico_ipv6_link_add_local(struct pico_device *dev, const struct pico_ip6 *prefix)
+{
+    struct pico_ip6 newaddr;
+    struct pico_ip6 netmask64 = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
+    struct pico_ipv6_link *link;
+    memcpy(newaddr.addr, prefix->addr, PICO_SIZE_IP6);
+    /* modified EUI-64 + invert universal/local bit */
+    newaddr.addr[8] = (dev->eth->mac.addr[0] ^ 0x02);
+    newaddr.addr[9] = dev->eth->mac.addr[1];
+    newaddr.addr[10] = dev->eth->mac.addr[2];
+    newaddr.addr[11] = 0xff;
+    newaddr.addr[12] = 0xfe;
+    newaddr.addr[13] = dev->eth->mac.addr[3];
+    newaddr.addr[14] = dev->eth->mac.addr[4];
+    newaddr.addr[15] = dev->eth->mac.addr[5];
+    link = pico_ipv6_link_add(dev, newaddr, netmask64);
+    if (link) {
+        device_init_ipv6_final(dev, &newaddr);
+    }
+
+    return link;
+}
+#endif
+
+static int device_init_mac(struct pico_device *dev, uint8_t *mac)
+{
+    #ifdef PICO_SUPPORT_IPV6
+    struct pico_ip6 linklocal = {{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xff, 0xfe, 0xaa, 0xaa, 0xaa}};
+    #endif
+    dev->eth = PICO_ZALLOC(sizeof(struct pico_ethdev));
+    if (dev->eth) {
+        memcpy(dev->eth->mac.addr, mac, PICO_SIZE_ETH);
+        #ifdef PICO_SUPPORT_IPV6
+        if (pico_ipv6_link_add_local(dev, &linklocal) == NULL) {
+            PICO_FREE(dev->q_in);
+            PICO_FREE(dev->q_out);
+            PICO_FREE(dev->eth);
+            return -1;
+        }
+
+        #endif
+    } else {
+        pico_err = PICO_ERR_ENOMEM;
+        return -1;
+    }
+
+    return 0;
+}
+
+int pico_device_ipv6_random_ll(struct pico_device *dev)
+{
+    #ifdef PICO_SUPPORT_IPV6
+    struct pico_ip6 linklocal = {{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xff, 0xfe, 0xaa, 0xaa, 0xaa}};
+    struct pico_ip6 netmask6 = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
+    uint32_t len = (uint32_t)strlen(dev->name);
+    if (strcmp(dev->name, "loop")) {
+        do {
+            /* privacy extension + unset universal/local and individual/group bit */
+            len = pico_rand();
+            linklocal.addr[8]  = (uint8_t)((len & 0xffu) & (uint8_t)(~0x03));
+            linklocal.addr[9]  = (uint8_t)(len >> 8);
+            linklocal.addr[10] = (uint8_t)(len >> 16);
+            linklocal.addr[11] = (uint8_t)(len >> 24);
+            len = pico_rand();
+            linklocal.addr[12] = (uint8_t)len;
+            linklocal.addr[13] = (uint8_t)(len >> 8);
+            linklocal.addr[14] = (uint8_t)(len >> 16);
+            linklocal.addr[15] = (uint8_t)(len >> 24);
+            pico_rand_feed(dev->hash);
+        } while (pico_ipv6_link_get(&linklocal));
+
+        if (pico_ipv6_link_add(dev, linklocal, netmask6) == NULL) {
+            return -1;
+        }
+    }
+
+    #endif
+    return 0;
+}
+
+static int device_init_nomac(struct pico_device *dev)
+{
+    if (pico_device_ipv6_random_ll(dev) < 0) {
+        PICO_FREE(dev->q_in);
+        PICO_FREE(dev->q_out);
+        return -1;
+    }
+
+    dev->eth = NULL;
+    return 0;
+}
+
+int pico_device_init(struct pico_device *dev, const char *name, uint8_t *mac)
+{
+
+    uint32_t len = (uint32_t)strlen(name);
+    int ret = 0;
+    if(len > MAX_DEVICE_NAME)
+        len = MAX_DEVICE_NAME;
+
+    memcpy(dev->name, name, len);
+    dev->hash = pico_hash(dev->name, len);
+
+    Devices_rr_info.node_in  = NULL;
+    Devices_rr_info.node_out = NULL;
+    dev->q_in = PICO_ZALLOC(sizeof(struct pico_queue));
+    if (!dev->q_in)
+        return -1;
+
+    dev->q_out = PICO_ZALLOC(sizeof(struct pico_queue));
+    if (!dev->q_out) {
+        PICO_FREE(dev->q_in);
+        return -1;
+    }
+
+    pico_tree_insert(&Device_tree, dev);
+    if (!dev->mtu)
+        dev->mtu = PICO_DEVICE_DEFAULT_MTU;
+
+    if (mac) {
+        ret = device_init_mac(dev, mac);
+    } else {
+        ret = device_init_nomac(dev);
+    }
+
+    return ret;
+}
+
+static void pico_queue_destroy(struct pico_queue *q)
+{
+    if (q) {
+        pico_queue_empty(q);
+        PICO_FREE(q);
+    }
+}
+
+void pico_device_destroy(struct pico_device *dev)
+{
+    if (dev->destroy)
+        dev->destroy(dev);
+
+    pico_queue_destroy(dev->q_in);
+    pico_queue_destroy(dev->q_out);
+
+    if (dev->eth)
+        PICO_FREE(dev->eth);
+
+#ifdef PICO_SUPPORT_IPV4
+    pico_ipv4_cleanup_links(dev);
+#endif
+#ifdef PICO_SUPPORT_IPV6
+    pico_ipv6_cleanup_links(dev);
+#endif
+    pico_tree_delete(&Device_tree, dev);
+
+    Devices_rr_info.node_in  = NULL;
+    Devices_rr_info.node_out = NULL;
+    PICO_FREE(dev);
+}
+
+static int check_dev_serve_interrupt(struct pico_device *dev, int loop_score)
+{
+    if ((dev->__serving_interrupt) && (dev->dsr)) {
+        /* call dsr routine */
+        loop_score = dev->dsr(dev, loop_score);
+    }
+
+    return loop_score;
+}
+
+static int check_dev_serve_polling(struct pico_device *dev, int loop_score)
+{
+    if (dev->poll) {
+        loop_score = dev->poll(dev, loop_score);
+    }
+
+    return loop_score;
+}
+
+static int devloop_in(struct pico_device *dev, int loop_score)
+{
+    struct pico_frame *f;
+    while(loop_score > 0) {
+        if (dev->q_in->frames <= 0)
+            break;
+
+        /* Receive */
+        f = pico_dequeue(dev->q_in);
+        if (f) {
+            if (dev->eth) {
+                f->datalink_hdr = f->buffer;
+                (void)pico_ethernet_receive(f);
+            } else {
+                f->net_hdr = f->buffer;
+                pico_network_receive(f);
+            }
+
+            loop_score--;
+        }
+    }
+    return loop_score;
+}
+
+static int devloop_sendto_dev(struct pico_device *dev, struct pico_frame *f)
+{
+
+    if (dev->eth) {
+        /* Ethernet: pass management of the frame to the pico_ethernet_send() rdv function */
+        return pico_ethernet_send(f);
+    } else {
+        /* non-ethernet: no post-processing needed */
+        return (dev->send(dev, f->start, (int)f->len) <= 0); /* Return 0 upon success, which is dev->send() > 0 */
+    }
+}
+
+static int devloop_out(struct pico_device *dev, int loop_score)
+{
+    struct pico_frame *f;
+    while(loop_score > 0) {
+        if (dev->q_out->frames <= 0)
+            break;
+
+        /* Device dequeue + send */
+        f = pico_queue_peek(dev->q_out);
+        if (!f)
+            break;
+
+        if (devloop_sendto_dev(dev, f) == 0) { /* success. */
+            f = pico_dequeue(dev->q_out);
+            pico_frame_discard(f); /* SINGLE POINT OF DISCARD for OUTGOING FRAMES */
+            loop_score--;
+        } else
+            break; /* Don't discard */
+
+    }
+    return loop_score;
+}
+
+static int devloop(struct pico_device *dev, int loop_score, int direction)
+{
+    /* If device supports interrupts, read the value of the condition and trigger the dsr */
+    loop_score = check_dev_serve_interrupt(dev, loop_score);
+
+    /* If device supports polling, give control. Loop score is managed internally,
+     * remaining loop points are returned. */
+    loop_score = check_dev_serve_polling(dev, loop_score);
+
+    if (direction == PICO_LOOP_DIR_OUT)
+        loop_score = devloop_out(dev, loop_score);
+    else
+        loop_score = devloop_in(dev, loop_score);
+
+    return loop_score;
+}
+
+
+static struct pico_tree_node *pico_dev_roundrobin_start(int direction)
+{
+    if (Devices_rr_info.node_in == NULL)
+        Devices_rr_info.node_in = pico_tree_firstNode(Device_tree.root);
+
+    if (Devices_rr_info.node_out == NULL)
+        Devices_rr_info.node_out = pico_tree_firstNode(Device_tree.root);
+
+    if (direction == PICO_LOOP_DIR_IN)
+        return Devices_rr_info.node_in;
+    else
+        return Devices_rr_info.node_out;
+}
+
+static void pico_dev_roundrobin_end(int direction, struct pico_tree_node *last)
+{
+    if (direction == PICO_LOOP_DIR_IN)
+        Devices_rr_info.node_in = last;
+    else
+        Devices_rr_info.node_out = last;
+}
+
+#define DEV_LOOP_MIN  16
+
+int pico_devices_loop(int loop_score, int direction)
+{
+    struct pico_device *start, *next;
+    struct pico_tree_node *next_node  = pico_dev_roundrobin_start(direction);
+
+    if (!next_node)
+        return loop_score;
+
+    next = next_node->keyValue;
+    start = next;
+
+    /* round-robin all devices, break if traversed all devices */
+    while ((loop_score > DEV_LOOP_MIN) && (next != NULL)) {
+        loop_score = devloop(next, loop_score, direction);
+        next_node = pico_tree_next(next_node);
+        next = next_node->keyValue;
+        if (next == NULL)
+        {
+            next_node = pico_tree_firstNode(Device_tree.root);
+            next = next_node->keyValue;
+        }
+
+        if (next == start)
+            break;
+    }
+    pico_dev_roundrobin_end(direction, next_node);
+    return loop_score;
+}
+
+struct pico_device *pico_get_device(const char*name)
+{
+    struct pico_device *dev;
+    struct pico_tree_node *index;
+    pico_tree_foreach(index, &Device_tree){
+        dev = index->keyValue;
+        if(strcmp(name, dev->name) == 0)
+            return dev;
+    }
+    return NULL;
+}
+
+int32_t pico_device_broadcast(struct pico_frame *f)
+{
+    struct pico_tree_node *index;
+    int32_t ret = -1;
+
+    pico_tree_foreach(index, &Device_tree)
+    {
+        struct pico_device *dev = index->keyValue;
+        if(dev != f->dev)
+        {
+            struct pico_frame *copy = pico_frame_copy(f);
+
+            if(!copy)
+                break;
+
+            copy->dev = dev;
+            copy->dev->send(copy->dev, copy->start, (int)copy->len);
+            pico_frame_discard(copy);
+        }
+        else
+        {
+            ret = f->dev->send(f->dev, f->start, (int)f->len);
+        }
+    }
+    return ret;
+}
+
+int pico_device_link_state(struct pico_device *dev)
+{
+    if (!dev->link_state)
+        return 1; /* Not supported, assuming link is always up */
+
+    return dev->link_state(dev);
+}
diff --git a/net/picotcp/stack/pico_frame.c b/net/picotcp/stack/pico_frame.c
new file mode 100644
index 0000000..482edfe
--- /dev/null
+++ b/net/picotcp/stack/pico_frame.c
@@ -0,0 +1,286 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   .
+
+   Authors: Daniele Lacamera
+ *********************************************************************/
+
+
+#include "pico_config.h"
+#include "pico_frame.h"
+#include "pico_protocol.h"
+#include "pico_stack.h"
+
+#ifdef PICO_SUPPORT_DEBUG_MEMORY
+static int n_frames_allocated;
+#endif
+
+/** frame alloc/dealloc/copy **/
+void pico_frame_discard(struct pico_frame *f)
+{
+    if (!f)
+        return;
+
+    (*f->usage_count)--;
+    if (*f->usage_count <= 0) {
+        if (f->flags & PICO_FRAME_FLAG_EXT_USAGE_COUNTER)
+            PICO_FREE(f->usage_count);
+
+#ifdef PICO_SUPPORT_DEBUG_MEMORY
+        dbg("Discarded buffer @%p, caller: %p\n", f->buffer, __builtin_return_address(3));
+        dbg("DEBUG MEMORY: %d frames in use.\n", --n_frames_allocated);
+#endif
+        if (!(f->flags & PICO_FRAME_FLAG_EXT_BUFFER))
+            PICO_FREE(f->buffer);
+        else if (f->notify_free)
+            f->notify_free(f->buffer);
+
+        if (f->info)
+            PICO_FREE(f->info);
+    }
+
+#ifdef PICO_SUPPORT_DEBUG_MEMORY
+    else {
+        dbg("Removed frame @%p(copy), usage count now: %d\n", f, *f->usage_count);
+    }
+#endif
+    PICO_FREE(f);
+}
+
+struct pico_frame *pico_frame_copy(struct pico_frame *f)
+{
+    struct pico_frame *new = PICO_ZALLOC(sizeof(struct pico_frame));
+    if (!new)
+        return NULL;
+
+    memcpy(new, f, sizeof(struct pico_frame));
+    *(new->usage_count) += 1;
+#ifdef PICO_SUPPORT_DEBUG_MEMORY
+    dbg("Copied frame @%p, into %p, usage count now: %d\n", f, new, *new->usage_count);
+#endif
+    new->next = NULL;
+    return new;
+}
+
+
+static struct pico_frame *pico_frame_do_alloc(uint32_t size, int zerocopy, int ext_buffer)
+{
+    struct pico_frame *p = PICO_ZALLOC(sizeof(struct pico_frame));
+    uint32_t frame_buffer_size = size;
+    if (!p)
+        return NULL;
+
+    if (ext_buffer && !zerocopy) {
+        /* external buffer implies zerocopy flag! */
+        PICO_FREE(p);
+        return NULL;
+    }
+
+    if (!zerocopy) {
+        unsigned int align = size % sizeof(uint32_t);
+        /* Ensure that usage_count starts on an aligned address */
+        if (align) {
+            frame_buffer_size += (uint32_t)sizeof(uint32_t) - align;
+        }
+
+        p->buffer = PICO_ZALLOC(frame_buffer_size + sizeof(uint32_t));
+        if (!p->buffer) {
+            PICO_FREE(p);
+            return NULL;
+        }
+
+        p->usage_count = (uint32_t *)(((uint8_t*)p->buffer) + frame_buffer_size);
+    } else {
+        p->buffer = NULL;
+        p->flags |= PICO_FRAME_FLAG_EXT_USAGE_COUNTER;
+        p->usage_count = PICO_ZALLOC(sizeof(uint32_t));
+        if (!p->usage_count) {
+            PICO_FREE(p);
+            return NULL;
+        }
+    }
+
+
+    p->buffer_len = size;
+
+    /* By default, frame content is the full buffer. */
+    p->start = p->buffer;
+    p->len = p->buffer_len;
+    *p->usage_count = 1;
+
+    if (ext_buffer)
+        p->flags |= PICO_FRAME_FLAG_EXT_BUFFER;
+
+#ifdef PICO_SUPPORT_DEBUG_MEMORY
+    dbg("Allocated buffer @%p, len= %d caller: %p\n", p->buffer, p->buffer_len, __builtin_return_address(2));
+    dbg("DEBUG MEMORY: %d frames in use.\n", ++n_frames_allocated);
+#endif
+    return p;
+}
+
+struct pico_frame *pico_frame_alloc(uint32_t size)
+{
+    return pico_frame_do_alloc(size, 0, 0);
+}
+
+int pico_frame_grow(struct pico_frame *f, uint32_t size)
+{
+    uint8_t *oldbuf;
+    uint32_t usage_count, *p_old_usage;
+    uint32_t frame_buffer_size;
+    uint32_t oldsize;
+    unsigned int align;
+    int addr_diff = 0;
+
+    if (!f || (size < f->buffer_len)) {
+        return -1;
+    }
+
+    align = size % sizeof(uint32_t);
+    frame_buffer_size = size;
+    if (align) {
+        frame_buffer_size += (uint32_t)sizeof(uint32_t) - align;
+    }
+
+    oldbuf = f->buffer;
+    oldsize = f->buffer_len;
+    usage_count = *(f->usage_count);
+    p_old_usage = f->usage_count;
+    f->buffer = PICO_ZALLOC(frame_buffer_size + sizeof(uint32_t));
+    if (!f->buffer) {
+        f->buffer = oldbuf;
+        return -1;
+    }
+
+    f->usage_count = (uint32_t *)(((uint8_t*)f->buffer) + frame_buffer_size);
+    *f->usage_count = usage_count;
+    f->buffer_len = size;
+    memcpy(f->buffer, oldbuf, oldsize);
+
+    /* Update hdr fields to new buffer*/
+    addr_diff = (int)(f->buffer - oldbuf);
+    f->net_hdr += addr_diff;
+    f->datalink_hdr += addr_diff;
+    f->transport_hdr += addr_diff;
+    f->app_hdr += addr_diff;
+    f->start += addr_diff;
+    f->payload += addr_diff;
+
+    if (f->flags & PICO_FRAME_FLAG_EXT_USAGE_COUNTER)
+        PICO_FREE(p_old_usage);
+
+    if (!(f->flags & PICO_FRAME_FLAG_EXT_BUFFER))
+        PICO_FREE(oldbuf);
+    else if (f->notify_free)
+        f->notify_free(oldbuf);
+
+    f->flags = 0;
+    /* Now, the frame is not zerocopy anymore, and the usage counter has been moved within it */
+    return 0;
+}
+
+struct pico_frame *pico_frame_alloc_skeleton(uint32_t size, int ext_buffer)
+{
+    return pico_frame_do_alloc(size, 1, ext_buffer);
+}
+
+int pico_frame_skeleton_set_buffer(struct pico_frame *f, void *buf)
+{
+    if (!buf)
+        return -1;
+
+    f->buffer = (uint8_t *) buf;
+    f->start = f->buffer;
+    return 0;
+}
+
+struct pico_frame *pico_frame_deepcopy(struct pico_frame *f)
+{
+    struct pico_frame *new = pico_frame_alloc(f->buffer_len);
+    int addr_diff;
+    unsigned char *buf;
+    uint32_t *uc;
+    if (!new)
+        return NULL;
+
+    /* Save the two key pointers... */
+    buf = new->buffer;
+    uc  = new->usage_count;
+
+    /* Overwrite all fields with originals */
+    memcpy(new, f, sizeof(struct pico_frame));
+
+    /* ...restore the two key pointers */
+    new->buffer = buf;
+    new->usage_count = uc;
+
+    /* Update in-buffer pointers with offset */
+    addr_diff = (int)(new->buffer - f->buffer);
+    new->datalink_hdr += addr_diff;
+    new->net_hdr += addr_diff;
+    new->transport_hdr += addr_diff;
+    new->app_hdr += addr_diff;
+    new->start += addr_diff;
+    new->payload += addr_diff;
+
+#ifdef PICO_SUPPORT_DEBUG_MEMORY
+    dbg("Deep-Copied frame @%p, into %p, usage count now: %d\n", f, new, *new->usage_count);
+#endif
+    new->next = NULL;
+    return new;
+}
+
+
+static inline uint32_t pico_checksum_adder(uint32_t sum, void *data, uint32_t len)
+{
+    uint16_t *buf = (uint16_t *)data;
+    uint16_t *stop;
+
+    if (len & 0x01) {
+        --len;
+#ifdef PICO_BIGENDIAN
+        sum += (((uint8_t *)data)[len]) << 8;
+#else
+        sum += ((uint8_t *)data)[len];
+#endif
+    }
+
+    stop = (uint16_t *)(((uint8_t *)data) + len);
+
+    while (buf < stop) {
+        sum += *buf++;
+    }
+    return sum;
+}
+
+static inline uint16_t pico_checksum_finalize(uint32_t sum)
+{
+    while (sum >> 16) { /* a second carry is possible! */
+        sum = (sum & 0x0000FFFF) + (sum >> 16);
+    }
+    return short_be((uint16_t) ~sum);
+}
+
+/**
+ * Calculate checksum of a given string
+ */
+uint16_t pico_checksum(void *inbuf, uint32_t len)
+{
+    uint32_t sum;
+
+    sum = pico_checksum_adder(0, inbuf, len);
+    return pico_checksum_finalize(sum);
+}
+
+/* WARNING: len1 MUST be an EVEN number */
+uint16_t pico_dualbuffer_checksum(void *inbuf1, uint32_t len1, void *inbuf2, uint32_t len2)
+{
+    uint32_t sum;
+
+    sum = pico_checksum_adder(0, inbuf1, len1);
+    sum = pico_checksum_adder(sum, inbuf2, len2);
+    return pico_checksum_finalize(sum);
+}
+
diff --git a/net/picotcp/stack/pico_protocol.c b/net/picotcp/stack/pico_protocol.c
new file mode 100644
index 0000000..98d2a20
--- /dev/null
+++ b/net/picotcp/stack/pico_protocol.c
@@ -0,0 +1,214 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   .
+
+   Authors: Daniele Lacamera
+ *********************************************************************/
+
+
+#include "pico_protocol.h"
+#include "pico_tree.h"
+
+struct pico_proto_rr
+{
+    struct pico_tree *t;
+    struct pico_tree_node *node_in, *node_out;
+};
+
+
+static int pico_proto_cmp(void *ka, void *kb)
+{
+    struct pico_protocol *a = ka, *b = kb;
+    if (a->hash < b->hash)
+        return -1;
+
+    if (a->hash > b->hash)
+        return 1;
+
+    return 0;
+}
+
+PICO_TREE_DECLARE(Datalink_proto_tree, pico_proto_cmp);
+PICO_TREE_DECLARE(Network_proto_tree, pico_proto_cmp);
+PICO_TREE_DECLARE(Transport_proto_tree, pico_proto_cmp);
+PICO_TREE_DECLARE(Socket_proto_tree, pico_proto_cmp);
+
+/* Static variables to keep track of the round robin loop */
+static struct pico_proto_rr proto_rr_datalink   = {
+    &Datalink_proto_tree,     NULL, NULL
+};
+static struct pico_proto_rr proto_rr_network    = {
+    &Network_proto_tree,      NULL, NULL
+};
+static struct pico_proto_rr proto_rr_transport  = {
+    &Transport_proto_tree,    NULL, NULL
+};
+static struct pico_proto_rr proto_rr_socket     = {
+    &Socket_proto_tree,       NULL, NULL
+};
+
+static int proto_loop_in(struct pico_protocol *proto, int loop_score)
+{
+    struct pico_frame *f;
+    while(loop_score > 0) {
+        if (proto->q_in->frames <= 0)
+            break;
+
+        f = pico_dequeue(proto->q_in);
+        if ((f) && (proto->process_in(proto, f) > 0)) {
+            loop_score--;
+        }
+    }
+    return loop_score;
+}
+
+static int proto_loop_out(struct pico_protocol *proto, int loop_score)
+{
+    struct pico_frame *f;
+    while(loop_score > 0) {
+        if (proto->q_out->frames <= 0)
+            break;
+
+        f = pico_dequeue(proto->q_out);
+        if ((f) && (proto->process_out(proto, f) > 0)) {
+            loop_score--;
+        }
+    }
+    return loop_score;
+}
+
+static int proto_loop(struct pico_protocol *proto, int loop_score, int direction)
+{
+
+    if (direction == PICO_LOOP_DIR_IN)
+        loop_score = proto_loop_in(proto, loop_score);
+    else if (direction == PICO_LOOP_DIR_OUT)
+        loop_score = proto_loop_out(proto, loop_score);
+
+    return loop_score;
+}
+
+static struct pico_tree_node *roundrobin_init(struct pico_proto_rr *rr, int direction)
+{
+    struct pico_tree_node *next_node = NULL;
+    /* Initialization (takes place only once) */
+    if (rr->node_in == NULL)
+        rr->node_in = pico_tree_firstNode(rr->t->root);
+
+    if (rr->node_out == NULL)
+        rr->node_out = pico_tree_firstNode(rr->t->root);
+
+    if (direction == PICO_LOOP_DIR_IN)
+        next_node = rr->node_in;
+    else
+        next_node = rr->node_out;
+
+    return next_node;
+}
+
+static void roundrobin_end(struct pico_proto_rr *rr, int direction, struct pico_tree_node *last)
+{
+    if (direction == PICO_LOOP_DIR_IN)
+        rr->node_in = last;
+    else
+        rr->node_out = last;
+}
+
+static int pico_protocol_generic_loop(struct pico_proto_rr *rr, int loop_score, int direction)
+{
+    struct pico_protocol *start, *next;
+    struct pico_tree_node *next_node = roundrobin_init(rr, direction);
+
+    if (!next_node)
+        return loop_score;
+
+    next = next_node->keyValue;
+
+    /* init start node */
+    start = next;
+
+    /* round-robin all layer protocols, break if traversed all protocols */
+    while (loop_score > 1 && next != NULL) {
+        loop_score = proto_loop(next, loop_score, direction);
+        next_node = pico_tree_next(next_node);
+        next = next_node->keyValue;
+        if (next == NULL)
+        {
+            next_node = pico_tree_firstNode(rr->t->root);
+            next = next_node->keyValue;
+        }
+
+        if (next == start)
+            break;
+    }
+    roundrobin_end(rr, direction, next_node);
+    return loop_score;
+}
+
+int pico_protocol_datalink_loop(int loop_score, int direction)
+{
+    return pico_protocol_generic_loop(&proto_rr_datalink, loop_score, direction);
+}
+
+int pico_protocol_network_loop(int loop_score, int direction)
+{
+    return pico_protocol_generic_loop(&proto_rr_network, loop_score, direction);
+}
+
+int pico_protocol_transport_loop(int loop_score, int direction)
+{
+    return pico_protocol_generic_loop(&proto_rr_transport, loop_score, direction);
+}
+
+int pico_protocol_socket_loop(int loop_score, int direction)
+{
+    return pico_protocol_generic_loop(&proto_rr_socket, loop_score, direction);
+}
+
+int pico_protocols_loop(int loop_score)
+{
+/*
+   loop_score = pico_protocol_datalink_loop(loop_score);
+   loop_score = pico_protocol_network_loop(loop_score);
+   loop_score = pico_protocol_transport_loop(loop_score);
+   loop_score = pico_protocol_socket_loop(loop_score);
+ */
+    return loop_score;
+}
+
+static void proto_layer_rr_reset(struct pico_proto_rr *rr)
+{
+    rr->node_in = NULL;
+    rr->node_out = NULL;
+}
+
+void pico_protocol_init(struct pico_protocol *p)
+{
+    if (!p)
+        return;
+
+    p->hash = pico_hash(p->name, (uint32_t)strlen(p->name));
+    switch (p->layer) {
+    case PICO_LAYER_DATALINK:
+        pico_tree_insert(&Datalink_proto_tree, p);
+        proto_layer_rr_reset(&proto_rr_datalink);
+        break;
+    case PICO_LAYER_NETWORK:
+        pico_tree_insert(&Network_proto_tree, p);
+        proto_layer_rr_reset(&proto_rr_network);
+        break;
+    case PICO_LAYER_TRANSPORT:
+        pico_tree_insert(&Transport_proto_tree, p);
+        proto_layer_rr_reset(&proto_rr_transport);
+        break;
+    case PICO_LAYER_SOCKET:
+        pico_tree_insert(&Socket_proto_tree, p);
+        proto_layer_rr_reset(&proto_rr_socket);
+        break;
+    }
+    dbg("Protocol %s registered (layer: %d).\n", p->name, p->layer);
+
+}
+
diff --git a/net/picotcp/stack/pico_socket.c b/net/picotcp/stack/pico_socket.c
new file mode 100644
index 0000000..cd0be1e
--- /dev/null
+++ b/net/picotcp/stack/pico_socket.c
@@ -0,0 +1,2213 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+
+   Authors: Daniele Lacamera
+ *********************************************************************/
+
+
+#include "pico_config.h"
+#include "pico_queue.h"
+#include "pico_socket.h"
+#include "pico_ipv4.h"
+#include "pico_ipv6.h"
+#include "pico_udp.h"
+#include "pico_tcp.h"
+#include "pico_stack.h"
+#include "pico_icmp4.h"
+#include "pico_nat.h"
+#include "pico_tree.h"
+#include "pico_device.h"
+#include "pico_socket_multicast.h"
+#include "pico_socket_tcp.h"
+#include "pico_socket_udp.h"
+
+#if defined (PICO_SUPPORT_IPV4) || defined (PICO_SUPPORT_IPV6)
+#if defined (PICO_SUPPORT_TCP) || defined (PICO_SUPPORT_UDP)
+
+
+#define PROTO(s) ((s)->proto->proto_number)
+#define PICO_MIN_MSS (1280)
+#define TCP_STATE(s) (s->state & PICO_SOCKET_STATE_TCP)
+
+#ifdef PICO_SUPPORT_MUTEX
+static void *Mutex = NULL;
+#endif
+
+
+#define PROTO(s) ((s)->proto->proto_number)
+
+#define PICO_SOCKET_MTU 1480 /* Ethernet MTU(1500) - IP header size(20) */
+
+# define frag_dbg(...) do {} while(0)
+
+
+static struct pico_sockport *sp_udp = NULL, *sp_tcp = NULL;
+
+struct pico_frame *pico_socket_frame_alloc(struct pico_socket *s, uint16_t len);
+
+static int socket_cmp_family(struct pico_socket *a, struct pico_socket *b)
+{
+    uint32_t a_is_ip6 = is_sock_ipv6(a);
+    uint32_t b_is_ip6 = is_sock_ipv6(b);
+    (void)a;
+    (void)b;
+    if (a_is_ip6 < b_is_ip6)
+        return -1;
+
+    if (a_is_ip6 > b_is_ip6)
+        return 1;
+
+    return 0;
+}
+
+
+static int socket_cmp_ipv6(struct pico_socket *a, struct pico_socket *b)
+{
+    int ret = 0;
+    (void)a;
+    (void)b;
+#ifdef PICO_SUPPORT_IPV6
+    if (!is_sock_ipv6(a) || !is_sock_ipv6(b))
+        return 0;
+
+    if ((memcmp(a->local_addr.ip6.addr, PICO_IP6_ANY, PICO_SIZE_IP6) == 0) || (memcmp(b->local_addr.ip6.addr, PICO_IP6_ANY, PICO_SIZE_IP6) == 0))
+        ret = 0;
+    else
+        ret = memcmp(a->local_addr.ip6.addr, b->local_addr.ip6.addr, PICO_SIZE_IP6);
+
+#endif
+    return ret;
+}
+
+static int socket_cmp_ipv4(struct pico_socket *a, struct pico_socket *b)
+{
+    int ret = 0;
+    (void)a;
+    (void)b;
+    if (!is_sock_ipv4(a) || !is_sock_ipv4(b))
+        return 0;
+
+#ifdef PICO_SUPPORT_IPV4
+    if ((a->local_addr.ip4.addr == PICO_IP4_ANY) || (b->local_addr.ip4.addr == PICO_IP4_ANY))
+        ret = 0;
+    else
+        ret = (int)(a->local_addr.ip4.addr - b->local_addr.ip4.addr);
+
+#endif
+    return ret;
+}
+
+static int socket_cmp_remotehost(struct pico_socket *a, struct pico_socket *b)
+{
+    int ret = 0;
+    if (is_sock_ipv6(a))
+        ret = memcmp(a->remote_addr.ip6.addr, b->remote_addr.ip6.addr, PICO_SIZE_IP6);
+    else
+        ret = (int)(a->remote_addr.ip4.addr - b->remote_addr.ip4.addr);
+
+    return ret;
+}
+
+static int socket_cmp_addresses(struct pico_socket *a, struct pico_socket *b)
+{
+    int ret = 0;
+    /* At this point, sort by local host */
+    ret = socket_cmp_ipv6(a, b);
+
+    if (ret == 0)
+        ret = socket_cmp_ipv4(a, b);
+
+    /* Sort by remote host */
+    if (ret == 0)
+        ret = socket_cmp_remotehost(a, b);
+
+    return 0;
+}
+
+static int socket_cmp(void *ka, void *kb)
+{
+    struct pico_socket *a = ka, *b = kb;
+    int ret = 0;
+
+    /* First, order by network family */
+    ret = socket_cmp_family(a, b);
+
+    /* Then, compare by source/destination addresses */
+    if (ret == 0)
+        ret = socket_cmp_addresses(a, b);
+
+    /* And finally by remote port. The two sockets are coincident if the quad is the same. */
+    if (ret == 0)
+        ret = b->remote_port - a->remote_port;
+
+    return ret;
+}
+
+
+#define INIT_SOCKPORT { {&LEAF, socket_cmp}, 0, 0 }
+
+static int sockport_cmp(void *ka, void *kb)
+{
+    struct pico_sockport *a = ka, *b = kb;
+    if (a->number < b->number)
+        return -1;
+
+    if (a->number > b->number)
+        return 1;
+
+    return 0;
+}
+
+PICO_TREE_DECLARE(UDPTable, sockport_cmp);
+PICO_TREE_DECLARE(TCPTable, sockport_cmp);
+
+struct pico_sockport *pico_get_sockport(uint16_t proto, uint16_t port)
+{
+    struct pico_sockport test = INIT_SOCKPORT;
+    test.number = port;
+
+    if (proto == PICO_PROTO_UDP)
+        return pico_tree_findKey(&UDPTable, &test);
+
+    else if (proto == PICO_PROTO_TCP)
+        return pico_tree_findKey(&TCPTable, &test);
+
+    else return NULL;
+}
+
+#ifdef PICO_SUPPORT_IPV4
+
+static int pico_port_in_use_by_nat(uint16_t proto, uint16_t port)
+{
+    int ret = 0;
+    (void) proto;
+    (void) port;
+#ifdef PICO_SUPPORT_NAT
+    if (pico_ipv4_nat_find(port, NULL, 0, (uint8_t)proto)) {
+        dbg("In use by nat....\n");
+        ret = 1;
+    }
+
+#endif
+    return ret;
+}
+
+static int pico_port_in_use_with_this_ipv4_address(struct pico_sockport *sp, struct pico_ip4 ip)
+{
+    if (sp) {
+        struct pico_ip4 *s_local;
+        struct pico_tree_node *idx;
+        struct pico_socket *s;
+        pico_tree_foreach(idx, &sp->socks) {
+            s = idx->keyValue;
+            if (s->net == &pico_proto_ipv4) {
+                s_local = (struct pico_ip4*) &s->local_addr;
+                if ((s_local->addr == PICO_IPV4_INADDR_ANY) || (s_local->addr == ip.addr)) {
+                    return 1;
+                }
+            }
+        }
+    }
+
+    return 0;
+}
+
+
+static int pico_port_in_use_ipv4(struct pico_sockport *sp, void *addr)
+{
+    struct pico_ip4 ip;
+    /* IPv4 */
+    if (addr)
+        ip.addr = ((struct pico_ip4 *)addr)->addr;
+    else
+        ip.addr = PICO_IPV4_INADDR_ANY;
+
+    if (ip.addr == PICO_IPV4_INADDR_ANY) {
+        if (!sp)
+            return 0;
+        else {
+            dbg("In use, and asked for ANY\n");
+            return 1;
+        }
+    }
+
+    return pico_port_in_use_with_this_ipv4_address(sp, ip);
+}
+#endif
+
+#ifdef PICO_SUPPORT_IPV6
+static int pico_port_in_use_with_this_ipv6_address(struct pico_sockport *sp, struct pico_ip6 ip)
+{
+    if (sp) {
+        struct pico_ip6 *s_local;
+        struct pico_tree_node *idx;
+        struct pico_socket *s;
+        pico_tree_foreach(idx, &sp->socks) {
+            s = idx->keyValue;
+            if (s->net == &pico_proto_ipv6) {
+                s_local = (struct pico_ip6*) &s->local_addr;
+                if ((pico_ipv6_is_unspecified(s_local->addr)) || (!memcmp(s_local->addr, ip.addr, PICO_SIZE_IP6))) {
+                    return 1;
+                }
+            }
+        }
+    }
+
+    return 0;
+}
+
+static int pico_port_in_use_ipv6(struct pico_sockport *sp, void *addr)
+{
+    struct pico_ip6 ip;
+    /* IPv6 */
+    if (addr)
+        memcpy(ip.addr, ((struct pico_ip6 *)addr)->addr, sizeof(struct pico_ip6));
+    else
+        memcpy(ip.addr, PICO_IP6_ANY, sizeof(struct pico_ip6));
+
+    if (memcmp(ip.addr, PICO_IP6_ANY, sizeof(struct pico_ip6)) ==  0) {
+        if (!sp)
+            return 0;
+        else {
+            dbg("In use, and asked for ANY\n");
+            return 1;
+        }
+    }
+
+    return pico_port_in_use_with_this_ipv6_address(sp, ip);
+}
+#endif
+
+
+
+static int pico_generic_port_in_use(uint16_t proto, uint16_t port, struct pico_sockport *sp, void *addr, void *net)
+{
+#ifdef PICO_SUPPORT_IPV4
+    if (net == &pico_proto_ipv4)
+    {
+        if (pico_port_in_use_by_nat(proto, port)) {
+            return 1;
+        }
+
+        if (pico_port_in_use_ipv4(sp, addr)) {
+            return 1;
+        }
+    }
+
+#endif
+
+#ifdef PICO_SUPPORT_IPV6
+    if (net == &pico_proto_ipv6)
+    {
+        if (pico_port_in_use_ipv6(sp, addr)) {
+            return 1;
+        }
+    }
+
+#endif
+
+    return 0;
+}
+
+int pico_is_port_free(uint16_t proto, uint16_t port, void *addr, void *net)
+{
+    struct pico_sockport *sp;
+    sp = pico_get_sockport(proto, port);
+
+    if (pico_generic_port_in_use(proto, port, sp, addr, net))
+        return 0;
+
+    return 1;
+}
+
+static int pico_check_socket(struct pico_socket *s)
+{
+    struct pico_sockport *test;
+    struct pico_socket *found;
+    struct pico_tree_node *index;
+
+    test = pico_get_sockport(PROTO(s), s->local_port);
+
+    if (!test) {
+        return -1;
+    }
+
+    pico_tree_foreach(index, &test->socks){
+        found = index->keyValue;
+        if (s == found) {
+            return 0;
+        }
+    }
+
+    return -1;
+}
+
+struct pico_socket *pico_sockets_find(uint16_t local, uint16_t remote)
+{
+    struct pico_socket *sock = NULL;
+    struct pico_tree_node *index = NULL;
+    struct pico_sockport *sp = NULL;
+
+    sp = pico_get_sockport(PICO_PROTO_TCP, local);
+    if(sp)
+    {
+        pico_tree_foreach(index, &sp->socks)
+        {
+            if(((struct pico_socket *)index->keyValue)->remote_port == remote)
+            {
+                sock = (struct pico_socket *)index->keyValue;
+                break;
+            }
+        }
+    }
+
+    return sock;
+}
+
+
+int8_t pico_socket_add(struct pico_socket *s)
+{
+    struct pico_sockport *sp = pico_get_sockport(PROTO(s), s->local_port);
+    PICOTCP_MUTEX_LOCK(Mutex);
+    if (!sp) {
+        /* dbg("Creating sockport..%04x\n", s->local_port); / * In comment due to spam during test * / */
+        sp = PICO_ZALLOC(sizeof(struct pico_sockport));
+
+        if (!sp) {
+            pico_err = PICO_ERR_ENOMEM;
+            PICOTCP_MUTEX_UNLOCK(Mutex);
+            return -1;
+        }
+
+        sp->proto = PROTO(s);
+        sp->number = s->local_port;
+        sp->socks.root = &LEAF;
+        sp->socks.compare = socket_cmp;
+
+        if (PROTO(s) == PICO_PROTO_UDP)
+        {
+            pico_tree_insert(&UDPTable, sp);
+        }
+        else if (PROTO(s) == PICO_PROTO_TCP)
+        {
+            pico_tree_insert(&TCPTable, sp);
+        }
+    }
+
+    pico_tree_insert(&sp->socks, s);
+    s->state |= PICO_SOCKET_STATE_BOUND;
+    PICOTCP_MUTEX_UNLOCK(Mutex);
+#ifdef DEBUG_SOCKET_TREE
+    {
+        struct pico_tree_node *index;
+        /* RB_FOREACH(s, socket_tree, &sp->socks) { */
+        pico_tree_foreach(index, &sp->socks){
+            s = index->keyValue;
+            dbg(">>>> List Socket lc=%hu rm=%hu\n", short_be(s->local_port), short_be(s->remote_port));
+        }
+
+    }
+#endif
+    return 0;
+}
+
+
+static void socket_clean_queues(struct pico_socket *sock)
+{
+    struct pico_frame *f_in = pico_dequeue(&sock->q_in);
+    struct pico_frame *f_out = pico_dequeue(&sock->q_out);
+    while(f_in || f_out)
+    {
+        if(f_in)
+        {
+            pico_frame_discard(f_in);
+            f_in = pico_dequeue(&sock->q_in);
+        }
+
+        if(f_out)
+        {
+            pico_frame_discard(f_out);
+            f_out = pico_dequeue(&sock->q_out);
+        }
+    }
+    pico_queue_deinit(&sock->q_in);
+    pico_queue_deinit(&sock->q_out);
+    pico_socket_tcp_cleanup(sock);
+}
+
+static void socket_garbage_collect(pico_time now, void *arg)
+{
+    struct pico_socket *s = (struct pico_socket *) arg;
+    IGNORE_PARAMETER(now);
+
+    socket_clean_queues(s);
+    PICO_FREE(s);
+}
+
+
+static void pico_socket_check_empty_sockport(struct pico_socket *s, struct pico_sockport *sp)
+{
+    if(pico_tree_empty(&sp->socks)) {
+        if (PROTO(s) == PICO_PROTO_UDP)
+        {
+            pico_tree_delete(&UDPTable, sp);
+        }
+        else if (PROTO(s) == PICO_PROTO_TCP)
+        {
+            pico_tree_delete(&TCPTable, sp);
+        }
+
+        if(sp_tcp == sp)
+            sp_tcp = NULL;
+
+        if(sp_udp == sp)
+            sp_udp = NULL;
+
+        PICO_FREE(sp);
+    }
+}
+
+int8_t pico_socket_del(struct pico_socket *s)
+{
+    struct pico_sockport *sp = pico_get_sockport(PROTO(s), s->local_port);
+    if (!sp) {
+        pico_err = PICO_ERR_ENXIO;
+        return -1;
+    }
+
+    PICOTCP_MUTEX_LOCK(Mutex);
+    pico_tree_delete(&sp->socks, s);
+    pico_socket_check_empty_sockport(s, sp);
+    pico_multicast_delete(s);
+    pico_socket_tcp_delete(s);
+    s->state = PICO_SOCKET_STATE_CLOSED;
+    pico_timer_add(3000, socket_garbage_collect, s);
+    PICOTCP_MUTEX_UNLOCK(Mutex);
+    return 0;
+}
+
+static void pico_socket_update_tcp_state(struct pico_socket *s, uint16_t tcp_state)
+{
+    if (tcp_state) {
+        s->state &= 0x00FF;
+        s->state |= tcp_state;
+    }
+}
+
+static int8_t pico_socket_alter_state(struct pico_socket *s, uint16_t more_states, uint16_t less_states, uint16_t tcp_state)
+{
+    struct pico_sockport *sp;
+    if (more_states & PICO_SOCKET_STATE_BOUND)
+        return pico_socket_add(s);
+
+    if (less_states & PICO_SOCKET_STATE_BOUND)
+        return pico_socket_del(s);
+
+    sp = pico_get_sockport(PROTO(s), s->local_port);
+    if (!sp) {
+        pico_err = PICO_ERR_ENXIO;
+        return -1;
+    }
+
+    s->state |= more_states;
+    s->state = (uint16_t)(s->state & (~less_states));
+    pico_socket_update_tcp_state(s, tcp_state);
+    return 0;
+}
+
+
+static int pico_socket_transport_deliver(struct pico_protocol *p, struct pico_sockport *sp, struct pico_frame *f)
+{
+#ifdef PICO_SUPPORT_TCP
+    if (p->proto_number == PICO_PROTO_TCP)
+        return pico_socket_tcp_deliver(sp, f);
+
+#endif
+
+#ifdef PICO_SUPPORT_UDP
+    if (p->proto_number == PICO_PROTO_UDP)
+        return pico_socket_udp_deliver(sp, f);
+
+#endif
+
+    return -1;
+}
+
+
+static int pico_socket_deliver(struct pico_protocol *p, struct pico_frame *f, uint16_t localport)
+{
+    struct pico_sockport *sp = NULL;
+    struct pico_trans *tr = (struct pico_trans *) f->transport_hdr;
+
+    if (!tr)
+        return -1;
+
+    sp = pico_get_sockport(p->proto_number, localport);
+    if (!sp) {
+        dbg("No such port %d\n", short_be(localport));
+        return -1;
+    }
+
+    return pico_socket_transport_deliver(p, sp, f);
+}
+
+int pico_socket_set_family(struct pico_socket *s, uint16_t family)
+{
+    (void) family;
+
+  #ifdef PICO_SUPPORT_IPV4
+    if (family == PICO_PROTO_IPV4)
+        s->net = &pico_proto_ipv4;
+
+  #endif
+
+  #ifdef PICO_SUPPORT_IPV6
+    if (family == PICO_PROTO_IPV6)
+        s->net = &pico_proto_ipv6;
+
+  #endif
+
+    if (s->net == NULL)
+        return -1;
+
+    return 0;
+}
+
+static struct pico_socket *pico_socket_transport_open(uint16_t proto, uint16_t family)
+{
+    struct pico_socket *s = NULL;
+    (void)family;
+#ifdef PICO_SUPPORT_UDP
+    if (proto == PICO_PROTO_UDP)
+        s = pico_socket_udp_open();
+
+#endif
+
+#ifdef PICO_SUPPORT_TCP
+    if (proto == PICO_PROTO_TCP)
+        s = pico_socket_tcp_open(family);
+
+#endif
+
+    return s;
+
+}
+
+struct pico_socket *pico_socket_open(uint16_t net, uint16_t proto, void (*wakeup)(uint16_t ev, struct pico_socket *))
+{
+
+    struct pico_socket *s = NULL;
+
+    s = pico_socket_transport_open(proto, net);
+
+    if (!s) {
+        pico_err = PICO_ERR_EPROTONOSUPPORT;
+        return NULL;
+    }
+
+    if (pico_socket_set_family(s, net) != 0) {
+        PICO_FREE(s);
+        pico_err = PICO_ERR_ENETUNREACH;
+        return NULL;
+    }
+
+    s->q_in.max_size = PICO_DEFAULT_SOCKETQ;
+    s->q_out.max_size = PICO_DEFAULT_SOCKETQ;
+
+    s->wakeup = wakeup;
+    return s;
+}
+
+
+static void pico_socket_clone_assign_address(struct pico_socket *s, struct pico_socket *facsimile)
+{
+
+#ifdef PICO_SUPPORT_IPV4
+    if (facsimile->net == &pico_proto_ipv4) {
+        s->net = &pico_proto_ipv4;
+        memcpy(&s->local_addr, &facsimile->local_addr, sizeof(struct pico_ip4));
+        memcpy(&s->remote_addr, &facsimile->remote_addr, sizeof(struct pico_ip4));
+    }
+
+#endif
+
+#ifdef PICO_SUPPORT_IPV6
+    if (facsimile->net == &pico_proto_ipv6) {
+        s->net = &pico_proto_ipv6;
+        memcpy(&s->local_addr, &facsimile->local_addr, sizeof(struct pico_ip6));
+        memcpy(&s->remote_addr, &facsimile->remote_addr, sizeof(struct pico_ip6));
+    }
+
+#endif
+
+}
+
+struct pico_socket *pico_socket_clone(struct pico_socket *facsimile)
+{
+    struct pico_socket *s = NULL;
+
+    s = pico_socket_transport_open(facsimile->proto->proto_number, facsimile->net->proto_number);
+    if (!s) {
+        pico_err = PICO_ERR_EPROTONOSUPPORT;
+        return NULL;
+    }
+
+    s->local_port = facsimile->local_port;
+    s->remote_port = facsimile->remote_port;
+    s->state = facsimile->state;
+    pico_socket_clone_assign_address(s, facsimile);
+    if (!s->net) {
+        PICO_FREE(s);
+        pico_err = PICO_ERR_ENETUNREACH;
+        return NULL;
+    }
+
+    s->q_in.max_size = PICO_DEFAULT_SOCKETQ;
+    s->q_out.max_size = PICO_DEFAULT_SOCKETQ;
+    s->wakeup = NULL;
+    return s;
+}
+
+static int pico_socket_transport_read(struct pico_socket *s, void *buf, int len)
+{
+    if (PROTO(s) == PICO_PROTO_UDP)
+    {
+        /* make sure cast to uint16_t doesn't give unexpected results */
+        if(len > 0xFFFF) {
+            pico_err = PICO_ERR_EINVAL;
+            return -1;
+        }
+
+        return pico_socket_udp_recv(s, buf, (uint16_t)len, NULL, NULL);
+    }
+    else if (PROTO(s) == PICO_PROTO_TCP)
+        return pico_socket_tcp_read(s, buf, (uint32_t)len);
+    else return 0;
+}
+
+int pico_socket_read(struct pico_socket *s, void *buf, int len)
+{
+    if (!s || buf == NULL) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    } else {
+        /* check if exists in tree */
+        /* See task #178 */
+        if (pico_check_socket(s) != 0) {
+            pico_err = PICO_ERR_EINVAL;
+            return -1;
+        }
+    }
+
+    if ((s->state & PICO_SOCKET_STATE_BOUND) == 0) {
+        pico_err = PICO_ERR_EIO;
+        return -1;
+    }
+
+    return pico_socket_transport_read(s, buf, len);
+}
+
+static int pico_socket_write_check_state(struct pico_socket *s)
+{
+    if ((s->state & PICO_SOCKET_STATE_BOUND) == 0) {
+        pico_err = PICO_ERR_EIO;
+        return -1;
+    }
+
+    if ((s->state & PICO_SOCKET_STATE_CONNECTED) == 0) {
+        pico_err = PICO_ERR_ENOTCONN;
+        return -1;
+    }
+
+    if (s->state & PICO_SOCKET_STATE_SHUT_LOCAL) { /* check if in shutdown state */
+        pico_err = PICO_ERR_ESHUTDOWN;
+        return -1;
+    }
+
+    return 0;
+}
+
+static int pico_socket_write_attempt(struct pico_socket *s, const void *buf, int len)
+{
+    if (pico_socket_write_check_state(s) < 0) {
+        return -1;
+    } else {
+        return pico_socket_sendto(s, buf, len, &s->remote_addr, s->remote_port);
+    }
+}
+
+int pico_socket_write(struct pico_socket *s, const void *buf, int len)
+{
+    if (!s || buf == NULL) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    } else {
+        /* check if exists in tree */
+        /* See task #178 */
+        if (pico_check_socket(s) != 0) {
+            pico_err = PICO_ERR_EINVAL;
+            return -1;
+        }
+    }
+
+    return pico_socket_write_attempt(s, buf, len);
+}
+
+static uint16_t pico_socket_high_port(uint16_t proto)
+{
+    uint16_t port;
+    if (0 ||
+#ifdef PICO_SUPPORT_TCP
+        (proto == PICO_PROTO_TCP) ||
+#endif
+#ifdef PICO_SUPPORT_UDP
+        (proto == PICO_PROTO_UDP) ||
+#endif
+        0) {
+        do {
+            uint32_t rand = pico_rand();
+            port = (uint16_t) (rand & 0xFFFFU);
+            port = (uint16_t)((port % (65535 - 1024)) + 1024U);
+            if (pico_is_port_free(proto, port, NULL, NULL)) {
+                return short_be(port);
+            }
+        } while(1);
+    }
+    else return 0U;
+}
+
+static void *pico_socket_sendto_get_ip4_src(struct pico_socket *s, struct pico_ip4 *dst)
+{
+    struct pico_ip4 *src4 = NULL;
+
+#ifdef PICO_SUPPORT_IPV4
+    /* Check if socket is connected: destination address MUST match the
+     * current connected endpoint
+     */
+    if ((s->state & PICO_SOCKET_STATE_CONNECTED)) {
+        src4 = &s->local_addr.ip4;
+        if  (s->remote_addr.ip4.addr != ((struct pico_ip4 *)dst)->addr ) {
+            pico_err = PICO_ERR_EADDRNOTAVAIL;
+            return NULL;
+        }
+    } else {
+
+        src4 = pico_ipv4_source_find(dst);
+        if (!src4) {
+            pico_err = PICO_ERR_EHOSTUNREACH;
+            return NULL;
+        }
+
+    }
+
+    if (src4->addr != PICO_IPV4_INADDR_ANY)
+        s->local_addr.ip4.addr = src4->addr;
+
+#else
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+#endif
+    return src4;
+}
+
+static void *pico_socket_sendto_get_ip6_src(struct pico_socket *s, struct pico_ip6 *dst)
+{
+    struct pico_ip6 *src6 = NULL;
+    (void)s;
+    (void)dst;
+
+#ifdef PICO_SUPPORT_IPV6
+
+    /* Check if socket is connected: destination address MUST match the
+     * current connected endpoint
+     */
+    if ((s->state & PICO_SOCKET_STATE_CONNECTED)) {
+        src6 = &s->local_addr.ip6;
+        if (memcmp(&s->remote_addr, dst, PICO_SIZE_IP6)) {
+            pico_err = PICO_ERR_EADDRNOTAVAIL;
+            return NULL;
+        }
+    } else {
+        src6 = pico_ipv6_source_find(dst);
+        if (!src6) {
+            pico_err = PICO_ERR_EHOSTUNREACH;
+            return NULL;
+        }
+
+        if (!pico_ipv6_is_unspecified(src6->addr))
+            s->local_addr.ip6 = *src6;
+    }
+
+#else
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+#endif
+    return src6;
+}
+
+
+static int pico_socket_sendto_dest_check(struct pico_socket *s, void *dst, uint16_t port)
+{
+
+    /* For the sendto call to be valid,
+     * dst and remote_port should be always populated.
+     */
+    if (!dst || !port) {
+        pico_err = PICO_ERR_EADDRNOTAVAIL;
+        return -1;
+    }
+
+    /* When coming from pico_socket_send (or _write),
+     * the destination is automatically assigned to the currently connected endpoint.
+     * This check will ensure that there is no mismatch when sendto() is called directly
+     * on a connected socket
+     */
+    if ((s->state & PICO_SOCKET_STATE_CONNECTED) != 0) {
+        if (port != s->remote_port) {
+            pico_err = PICO_ERR_EINVAL;
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+static int pico_socket_sendto_initial_checks(struct pico_socket *s, const void *buf, const int len, void *dst, uint16_t remote_port)
+{
+    if (len < 0) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    if (buf == NULL || s == NULL) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    return pico_socket_sendto_dest_check(s, dst, remote_port);
+}
+
+static void *pico_socket_sendto_get_src(struct pico_socket *s, void *dst)
+{
+    void *src = NULL;
+    if (is_sock_ipv4(s))
+        src = pico_socket_sendto_get_ip4_src(s, (struct pico_ip4 *)dst);
+
+    if (is_sock_ipv6(s))
+        src = pico_socket_sendto_get_ip6_src(s, (struct pico_ip6 *)dst);
+
+    return src;
+}
+
+static struct pico_remote_endpoint *pico_socket_sendto_destination_ipv4(struct pico_socket *s, struct pico_ip4 *dst, uint16_t port)
+{
+    struct pico_remote_endpoint *ep = NULL;
+    (void)s;
+    ep = PICO_ZALLOC(sizeof(struct pico_remote_endpoint));
+    if (!ep) {
+        pico_err = PICO_ERR_ENOMEM;
+        return NULL;
+    }
+
+    ep->remote_addr.ip4.addr = ((struct pico_ip4 *)dst)->addr;
+    ep->remote_port = port;
+    return ep;
+}
+
+static void pico_endpoint_free(struct pico_remote_endpoint *ep)
+{
+    if (ep)
+        PICO_FREE(ep);
+}
+
+static struct pico_remote_endpoint *pico_socket_sendto_destination_ipv6(struct pico_socket *s, struct pico_ip6 *dst, uint16_t port)
+{
+    struct pico_remote_endpoint *ep = NULL;
+    (void)s;
+    (void)dst;
+    (void)port;
+#ifdef PICO_SUPPORT_IPV6
+    ep = PICO_ZALLOC(sizeof(struct pico_remote_endpoint));
+    if (!ep) {
+        pico_err = PICO_ERR_ENOMEM;
+        return NULL;
+    }
+
+    memcpy(&ep->remote_addr.ip6, dst, sizeof(struct pico_ip6));
+    ep->remote_port = port;
+#endif
+    return ep;
+}
+
+
+static struct pico_remote_endpoint *pico_socket_sendto_destination(struct pico_socket *s, void *dst, uint16_t port)
+{
+    struct pico_remote_endpoint *ep = NULL;
+    (void)pico_socket_sendto_destination_ipv6;
+    /* socket remote info could change in a consecutive call, make persistent */
+#   ifdef PICO_SUPPORT_UDP
+    if (PROTO(s) == PICO_PROTO_UDP) {
+#       ifdef PICO_SUPPORT_IPV6
+        if (is_sock_ipv6(s))
+            ep = pico_socket_sendto_destination_ipv6(s, (struct pico_ip6 *)dst, port);
+
+#       endif
+#       ifdef PICO_SUPPORT_IPV4
+        if (is_sock_ipv4(s))
+            ep = pico_socket_sendto_destination_ipv4(s, (struct pico_ip4 *)dst, port);
+
+#       endif
+    }
+
+#  endif
+    return ep;
+}
+
+static int32_t pico_socket_sendto_set_localport(struct pico_socket *s)
+{
+
+    if ((s->state & PICO_SOCKET_STATE_BOUND) == 0) {
+        s->local_port = pico_socket_high_port(s->proto->proto_number);
+        if (s->local_port == 0) {
+            pico_err = PICO_ERR_EINVAL;
+            return -1;
+        }
+
+        s->state |= PICO_SOCKET_STATE_BOUND;
+    }
+
+    return s->local_port;
+}
+
+static int pico_socket_sendto_transport_offset(struct pico_socket *s)
+{
+    int header_offset = -1;
+    #ifdef PICO_SUPPORT_TCP
+    if (PROTO(s) == PICO_PROTO_TCP)
+        header_offset = pico_tcp_overhead(s);
+
+    #endif
+
+    #ifdef PICO_SUPPORT_UDP
+    if (PROTO(s) == PICO_PROTO_UDP)
+        header_offset = sizeof(struct pico_udp_hdr);
+
+    #endif
+    return header_offset;
+}
+
+
+static struct pico_remote_endpoint *pico_socket_set_info(struct pico_remote_endpoint *ep)
+{
+    struct pico_remote_endpoint *info;
+    info = PICO_ZALLOC(sizeof(struct pico_remote_endpoint));
+    if (!info) {
+        pico_err = PICO_ERR_ENOMEM;
+        return NULL;
+    }
+
+    memcpy(info, ep, sizeof(struct pico_remote_endpoint));
+    return info;
+}
+
+static void pico_xmit_frame_set_nofrag(struct pico_frame *f)
+{
+#ifdef PICO_SUPPORT_IPFRAG
+    f->frag = PICO_IPV4_DONTFRAG;
+#else
+    (void)f;
+#endif
+}
+
+static int pico_socket_final_xmit(struct pico_socket *s, struct pico_frame *f)
+{
+    if (s->proto->push(s->proto, f) > 0) {
+        return f->payload_len;
+    } else {
+        pico_frame_discard(f);
+        return 0;
+    }
+}
+
+static int pico_socket_xmit_one(struct pico_socket *s, const void *buf, const int len, void *src,
+                                struct pico_remote_endpoint *ep, struct pico_msginfo *msginfo)
+{
+    struct pico_frame *f;
+    uint16_t hdr_offset = (uint16_t)pico_socket_sendto_transport_offset(s);
+    int ret = 0;
+    (void)src;
+
+    f = pico_socket_frame_alloc(s, (uint16_t)(len + hdr_offset));
+    if (!f) {
+        pico_err = PICO_ERR_ENOMEM;
+        return -1;
+    }
+
+    f->payload += hdr_offset;
+    f->payload_len = (uint16_t)(len);
+    f->sock = s;
+    transport_flags_update(f, s);
+    pico_xmit_frame_set_nofrag(f);
+    if (ep && !f->info) {
+        f->info = pico_socket_set_info(ep);
+        if (!f->info) {
+            pico_frame_discard(f);
+            return -1;
+        }
+    }
+
+    if (msginfo) {
+        f->send_ttl = (uint8_t)msginfo->ttl;
+        f->send_tos = (uint8_t)msginfo->tos;
+        f->dev = msginfo->dev;
+    }
+
+    memcpy(f->payload, (const uint8_t *)buf, f->payload_len);
+    /* dbg("Pushing segment, hdr len: %d, payload_len: %d\n", header_offset, f->payload_len); */
+    ret = pico_socket_final_xmit(s, f);
+    return ret;
+}
+
+static int pico_socket_xmit_avail_space(struct pico_socket *s);
+
+#ifdef PICO_SUPPORT_IPFRAG
+static void pico_socket_xmit_first_fragment_setup(struct pico_frame *f, int space, int hdr_offset)
+{
+    frag_dbg("FRAG: first fragmented frame %p | len = %u offset = 0\n", f, f->payload_len);
+    /* transport header length field contains total length + header length */
+    f->transport_len = (uint16_t)(space);
+    f->frag = PICO_IPV4_MOREFRAG;
+    f->payload += hdr_offset;
+    f->payload_len = (uint16_t) space;
+}
+
+static void pico_socket_xmit_next_fragment_setup(struct pico_frame *f, int hdr_offset, int total_payload_written, int len)
+{
+    /* no transport header in fragmented IP */
+    f->payload = f->transport_hdr;
+    f->payload_len = (uint16_t)(f->payload_len - hdr_offset);
+    /* set offset in octets */
+    f->frag = (uint16_t)((total_payload_written + (uint16_t)hdr_offset) >> 3u);
+    if (total_payload_written + f->payload_len < len) {
+        frag_dbg("FRAG: intermediate fragmented frame %p | len = %u offset = %u\n", f, f->payload_len, short_be(f->frag));
+        f->frag |= PICO_IPV4_MOREFRAG;
+    } else {
+        frag_dbg("FRAG: last fragmented frame %p | len = %u offset = %u\n", f, f->payload_len, short_be(f->frag));
+        f->frag &= PICO_IPV4_FRAG_MASK;
+    }
+}
+#endif
+
+static int pico_socket_xmit_fragments(struct pico_socket *s, const void *buf, const int len,
+                                      void *src, struct pico_remote_endpoint *ep, struct pico_msginfo *msginfo)
+{
+    int space = pico_socket_xmit_avail_space(s);
+    int hdr_offset = pico_socket_sendto_transport_offset(s);
+    int total_payload_written = 0;
+    struct pico_frame *f = NULL;
+
+    if (space > len) {
+        return pico_socket_xmit_one(s, buf, len, src, ep, msginfo);
+    }
+
+#ifdef PICO_SUPPORT_IPV6
+    /* Can't fragment IPv6 */
+    if (is_sock_ipv6(s)) {
+        return pico_socket_xmit_one(s, buf, space, src, ep, msginfo);
+    }
+
+#endif
+
+#ifdef PICO_SUPPORT_IPFRAG
+    while(total_payload_written < len) {
+        /* Always allocate the max space available: space + offset */
+        if (len < space)
+            space = len;
+
+        if (space > len - total_payload_written)
+            space = len - total_payload_written;
+
+        f = pico_socket_frame_alloc(s, (uint16_t)(space + hdr_offset));
+        if (!f) {
+            pico_err = PICO_ERR_ENOMEM;
+            pico_endpoint_free(ep);
+            return -1;
+        }
+
+        f->sock = s;
+        if (ep) {
+            f->info = pico_socket_set_info(ep);
+            if (!f->info) {
+                pico_frame_discard(f);
+                pico_endpoint_free(ep);
+                return -1;
+            }
+        }
+
+        if (total_payload_written == 0) {
+            /* First fragment: no payload written yet! */
+            pico_socket_xmit_first_fragment_setup(f, space, hdr_offset);
+        } else {
+            /* Next fragment */
+            pico_socket_xmit_next_fragment_setup(f, hdr_offset, total_payload_written, len);
+        }
+
+        memcpy(f->payload, (const uint8_t *)buf + total_payload_written, f->payload_len);
+        transport_flags_update(f, s);
+        if (s->proto->push(s->proto, f) > 0) {
+            total_payload_written += f->payload_len;
+        } else {
+            pico_frame_discard(f);
+            break;
+        }
+    } /* while() */
+    pico_endpoint_free(ep);
+    return total_payload_written;
+
+#else
+    /* Careful with that axe, Eugene!
+     *
+     * cropping down datagrams to the MTU value.
+     */
+    (void) f;
+    (void) hdr_offset;
+    (void) total_payload_written;
+    return pico_socket_xmit_one(s, buf, space, src, ep, msginfo);
+
+#endif
+}
+
+static void get_sock_dev(struct pico_socket *s)
+{
+    if (0) {}
+
+#ifdef PICO_SUPPORT_IPV6
+    else if (is_sock_ipv6(s))
+        s->dev = pico_ipv6_source_dev_find(&s->remote_addr.ip6);
+#endif
+#ifdef PICO_SUPPORT_IPV4
+    else if (is_sock_ipv4(s))
+        s->dev = pico_ipv4_source_dev_find(&s->remote_addr.ip4);
+#endif
+
+}
+
+
+static uint32_t pico_socket_adapt_mss_to_proto(struct pico_socket *s, uint32_t mss)
+{
+#ifdef PICO_SUPPORT_IPV6
+    if (is_sock_ipv6(s))
+        mss -= PICO_SIZE_IP6HDR;
+    else
+#endif
+    mss -= PICO_SIZE_IP4HDR;
+    return mss;
+}
+
+uint32_t pico_socket_get_mss(struct pico_socket *s)
+{
+    uint32_t mss = PICO_MIN_MSS;
+    if (!s)
+        return mss;
+
+    if (!s->dev)
+        get_sock_dev(s);
+
+    if (!s->dev) {
+        mss = PICO_MIN_MSS;
+    } else {
+        mss = s->dev->mtu;
+    }
+
+    return pico_socket_adapt_mss_to_proto(s, mss);
+}
+
+
+static int pico_socket_xmit_avail_space(struct pico_socket *s)
+{
+    int transport_len;
+    int header_offset;
+
+#ifdef PICO_SUPPORT_TCP
+    if (PROTO(s) == PICO_PROTO_TCP) {
+        transport_len = (uint16_t)pico_tcp_get_socket_mss(s);
+    } else
+#endif
+    transport_len = (uint16_t)pico_socket_get_mss(s);
+    header_offset = pico_socket_sendto_transport_offset(s);
+    if (header_offset < 0) {
+        pico_err = PICO_ERR_EPROTONOSUPPORT;
+        return -1;
+    }
+
+    transport_len -= pico_socket_sendto_transport_offset(s);
+    return transport_len;
+}
+
+
+static int pico_socket_xmit(struct pico_socket *s, const void *buf, const int len, void *src,
+                            struct pico_remote_endpoint *ep, struct pico_msginfo *msginfo)
+{
+    int space = pico_socket_xmit_avail_space(s);
+    int total_payload_written = 0;
+
+    if (space < 0) {
+        pico_err = PICO_ERR_EPROTONOSUPPORT;
+        pico_endpoint_free(ep);
+        return -1;
+    }
+
+    if ((PROTO(s) == PICO_PROTO_UDP) && (len > space)) {
+        total_payload_written = pico_socket_xmit_fragments(s, buf, len, src, ep, msginfo);
+        /* Implies ep discarding */
+        return total_payload_written;
+    }
+
+    while (total_payload_written < len) {
+        int w, chunk_len = len - total_payload_written;
+        if (chunk_len > space)
+            chunk_len = space;
+
+        w = pico_socket_xmit_one(s, (const void *)((const uint8_t *)buf + total_payload_written), chunk_len, src, ep, msginfo);
+        if (w <= 0) {
+            break;
+        }
+
+        total_payload_written += w;
+        if (PROTO(s) == PICO_PROTO_UDP) {
+            /* Break after the first datagram sent with at most MTU bytes. */
+            break;
+        }
+    }
+    pico_endpoint_free(ep);
+    return total_payload_written;
+}
+
+static void pico_socket_sendto_set_dport(struct pico_socket *s, uint16_t port)
+{
+    if ((s->state & PICO_SOCKET_STATE_CONNECTED) == 0) {
+        s->remote_port = port;
+    }
+}
+
+
+int MOCKABLE pico_socket_sendto_extended(struct pico_socket *s, const void *buf, const int len,
+                                         void *dst, uint16_t remote_port, struct pico_msginfo *msginfo)
+{
+    struct pico_remote_endpoint *remote_endpoint = NULL;
+    void *src = NULL;
+
+    if(len == 0)
+        return 0;
+
+    if (pico_socket_sendto_initial_checks(s, buf, len, dst, remote_port) < 0)
+        return -1;
+
+
+    src = pico_socket_sendto_get_src(s, dst);
+    if (!src) {
+#ifdef PICO_SUPPORT_IPV6
+        if((s->net->proto_number == PICO_PROTO_IPV6)
+           && msginfo && msginfo->dev
+           && pico_ipv6_is_linklocal(((struct pico_ip6 *)dst)->addr))
+        {
+            src = &(pico_ipv6_linklocal_get(msginfo->dev)->address);
+            if(!src)
+                return -1;
+        }
+        else
+#endif
+        return -1;
+    }
+
+    remote_endpoint = pico_socket_sendto_destination(s, dst, remote_port);
+    if (pico_socket_sendto_set_localport(s) < 0) {
+        pico_endpoint_free(remote_endpoint);
+        return -1;
+    }
+
+    pico_socket_sendto_set_dport(s, remote_port);
+    return pico_socket_xmit(s, buf, len, src, remote_endpoint, msginfo); /* Implies discarding the endpoint */
+}
+
+int MOCKABLE pico_socket_sendto(struct pico_socket *s, const void *buf, const int len, void *dst, uint16_t remote_port)
+{
+    return pico_socket_sendto_extended(s, buf, len, dst, remote_port, NULL);
+}
+
+int pico_socket_send(struct pico_socket *s, const void *buf, int len)
+{
+    if (!s || buf == NULL) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    } else {
+        /* check if exists in tree */
+        /* See task #178 */
+        if (pico_check_socket(s) != 0) {
+            pico_err = PICO_ERR_EINVAL;
+            return -1;
+        }
+    }
+
+    if ((s->state & PICO_SOCKET_STATE_CONNECTED) == 0) {
+        pico_err = PICO_ERR_ENOTCONN;
+        return -1;
+    }
+
+    return pico_socket_sendto(s, buf, len, &s->remote_addr, s->remote_port);
+}
+
+int pico_socket_recvfrom_extended(struct pico_socket *s, void *buf, int len, void *orig,
+                                  uint16_t *remote_port, struct pico_msginfo *msginfo)
+{
+    if (!s || buf == NULL) { /* / || orig == NULL || remote_port == NULL) { */
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    } else {
+        /* check if exists in tree */
+        if (pico_check_socket(s) != 0) {
+            pico_err = PICO_ERR_EINVAL;
+            /* See task #178 */
+            return -1;
+        }
+    }
+
+    if ((s->state & PICO_SOCKET_STATE_BOUND) == 0) {
+        pico_err = PICO_ERR_EADDRNOTAVAIL;
+        return -1;
+    }
+
+#ifdef PICO_SUPPORT_UDP
+    if (PROTO(s) == PICO_PROTO_UDP) {
+        /* make sure cast to uint16_t doesn't give unexpected results */
+        if(len > 0xFFFF) {
+            pico_err = PICO_ERR_EINVAL;
+            return -1;
+        }
+
+        return pico_udp_recv(s, buf, (uint16_t)len, orig, remote_port, msginfo);
+    }
+
+#endif
+#ifdef PICO_SUPPORT_TCP
+    if (PROTO(s) == PICO_PROTO_TCP) {
+        /* check if in shutdown state and if tcpq_in empty */
+        if ((s->state & PICO_SOCKET_STATE_SHUT_REMOTE) && pico_tcp_queue_in_is_empty(s)) {
+            pico_err = PICO_ERR_ESHUTDOWN;
+            return -1;
+        } else {
+            /* dbg("socket tcp recv\n"); */
+            return (int)pico_tcp_read(s, buf, (uint32_t)len);
+        }
+    }
+
+#endif
+    /* dbg("socket return 0\n"); */
+    return 0;
+}
+
+int pico_socket_recvfrom(struct pico_socket *s, void *buf, int len, void *orig,
+                         uint16_t *remote_port)
+{
+    return pico_socket_recvfrom_extended(s, buf, len, orig, remote_port, NULL);
+
+}
+
+int pico_socket_recv(struct pico_socket *s, void *buf, int len)
+{
+    return pico_socket_recvfrom(s, buf, len, NULL, NULL);
+}
+
+
+int pico_socket_getname(struct pico_socket *s, void *local_addr, uint16_t *port, uint16_t *proto)
+{
+
+    if (!s || !local_addr || !port || !proto) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    if (is_sock_ipv4(s)) {
+    #ifdef PICO_SUPPORT_IPV4
+        struct pico_ip4 *ip = (struct pico_ip4 *)local_addr;
+        ip->addr = s->local_addr.ip4.addr;
+        *proto = PICO_PROTO_IPV4;
+    #endif
+    } else if (is_sock_ipv6(s)) {
+    #ifdef PICO_SUPPORT_IPV6
+        struct pico_ip6 *ip = (struct pico_ip6 *)local_addr;
+        memcpy(ip->addr, s->local_addr.ip6.addr, PICO_SIZE_IP6);
+        *proto = PICO_PROTO_IPV6;
+    #endif
+    } else {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    *port = s->local_port;
+    return 0;
+}
+
+int pico_socket_getpeername(struct pico_socket *s, void *remote_addr, uint16_t *port, uint16_t *proto)
+{
+    if (!s || !remote_addr || !port || !proto) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    if ((s->state & PICO_SOCKET_STATE_CONNECTED) == 0) {
+        pico_err = PICO_ERR_ENOTCONN;
+        return -1;
+    }
+
+    if (is_sock_ipv4(s)) {
+    #ifdef PICO_SUPPORT_IPV4
+        struct pico_ip4 *ip = (struct pico_ip4 *)remote_addr;
+        ip->addr = s->remote_addr.ip4.addr;
+        *proto = PICO_PROTO_IPV4;
+    #endif
+    } else if (is_sock_ipv6(s)) {
+    #ifdef PICO_SUPPORT_IPV6
+        struct pico_ip6 *ip = (struct pico_ip6 *)remote_addr;
+        memcpy(ip->addr, s->remote_addr.ip6.addr, PICO_SIZE_IP6);
+        *proto = PICO_PROTO_IPV6;
+    #endif
+    } else {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    *port = s->remote_port;
+    return 0;
+
+}
+
+int pico_socket_bind(struct pico_socket *s, void *local_addr, uint16_t *port)
+{
+    if (!s || !local_addr || !port) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    if (is_sock_ipv4(s)) {
+    #ifdef PICO_SUPPORT_IPV4
+        struct pico_ip4 *ip = (struct pico_ip4 *)local_addr;
+        if (ip->addr != PICO_IPV4_INADDR_ANY) {
+            if (!pico_ipv4_link_find(local_addr)) {
+                pico_err = PICO_ERR_EINVAL;
+                return -1;
+            }
+        }
+
+    #endif
+    } else if (is_sock_ipv6(s)) {
+    #ifdef PICO_SUPPORT_IPV6
+        struct pico_ip6 *ip = (struct pico_ip6 *)local_addr;
+        if (!pico_ipv6_is_unspecified(ip->addr)) {
+            if (!pico_ipv6_link_find(local_addr)) {
+                pico_err = PICO_ERR_EINVAL;
+                return -1;
+            }
+        }
+
+    #endif
+    } else {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+
+    /* When given port = 0, get a random high port to bind to. */
+    if (*port == 0) {
+        *port = pico_socket_high_port(PROTO(s));
+        if (*port == 0) {
+            pico_err = PICO_ERR_EINVAL;
+            return -1;
+        }
+    }
+
+    if (pico_is_port_free(PROTO(s), *port, local_addr, s->net) == 0) {
+        pico_err = PICO_ERR_EADDRINUSE;
+        return -1;
+    }
+
+    s->local_port = *port;
+
+    if (is_sock_ipv4(s)) {
+    #ifdef PICO_SUPPORT_IPV4
+        struct pico_ip4 *ip = (struct pico_ip4 *)local_addr;
+        s->local_addr.ip4 = *ip;
+    #endif
+    } else if (is_sock_ipv6(s)) {
+    #ifdef PICO_SUPPORT_IPV6
+        struct pico_ip6 *ip = (struct pico_ip6 *)local_addr;
+        s->local_addr.ip6 = *ip;
+    #endif
+    } else {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    return pico_socket_alter_state(s, PICO_SOCKET_STATE_BOUND, 0, 0);
+}
+
+
+int pico_socket_connect(struct pico_socket *s, const void *remote_addr, uint16_t remote_port)
+{
+    int ret = -1;
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+    if (!s || remote_addr == NULL || remote_port == 0) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    s->remote_port = remote_port;
+
+    if (s->local_port == 0) {
+        s->local_port = pico_socket_high_port(PROTO(s));
+        if (!s->local_port) {
+            pico_err = PICO_ERR_EINVAL;
+            return -1;
+        }
+    }
+
+    if (is_sock_ipv4(s)) {
+    #ifdef PICO_SUPPORT_IPV4
+        struct pico_ip4 *local = NULL;
+        const struct pico_ip4 *ip = (const struct pico_ip4 *)remote_addr;
+        s->remote_addr.ip4 = *ip;
+        local = pico_ipv4_source_find(ip);
+        if (local) {
+            get_sock_dev(s);
+            s->local_addr.ip4 = *local;
+        } else {
+            pico_err = PICO_ERR_EHOSTUNREACH;
+            return -1;
+        }
+
+    #endif
+    } else if (is_sock_ipv6(s)) {
+    #ifdef PICO_SUPPORT_IPV6
+        struct pico_ip6 *local = NULL;
+        const struct pico_ip6 *ip = (const struct pico_ip6 *)remote_addr;
+        s->remote_addr.ip6 = *ip;
+        local = pico_ipv6_source_find(ip);
+        if (local) {
+            get_sock_dev(s);
+            s->local_addr.ip6 = *local;
+        } else {
+            pico_err = PICO_ERR_EHOSTUNREACH;
+            return -1;
+        }
+
+    #endif
+    } else {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    pico_socket_alter_state(s, PICO_SOCKET_STATE_BOUND, 0, 0);
+
+#ifdef PICO_SUPPORT_UDP
+    if (PROTO(s) == PICO_PROTO_UDP) {
+        pico_socket_alter_state(s, PICO_SOCKET_STATE_CONNECTED, 0, 0);
+        pico_err = PICO_ERR_NOERR;
+        ret = 0;
+    }
+
+#endif
+
+#ifdef PICO_SUPPORT_TCP
+    if (PROTO(s) == PICO_PROTO_TCP) {
+        if (pico_tcp_initconn(s) == 0) {
+            pico_socket_alter_state(s, PICO_SOCKET_STATE_CONNECTED | PICO_SOCKET_STATE_TCP_SYN_SENT, PICO_SOCKET_STATE_CLOSED, 0);
+            pico_err = PICO_ERR_NOERR;
+            ret = 0;
+        } else {
+            pico_err = PICO_ERR_EHOSTUNREACH;
+        }
+    }
+
+#endif
+
+    return ret;
+}
+
+
+#ifdef PICO_SUPPORT_TCP
+
+int pico_socket_listen(struct pico_socket *s, int backlog)
+{
+    if (!s || backlog < 1) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    } else {
+        /* check if exists in tree */
+        /* See task #178 */
+        if (pico_check_socket(s) != 0) {
+            pico_err = PICO_ERR_EINVAL;
+            return -1;
+        }
+    }
+
+    if (PROTO(s) == PICO_PROTO_UDP) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    if ((s->state & PICO_SOCKET_STATE_BOUND) == 0) {
+        pico_err = PICO_ERR_EISCONN;
+        return -1;
+    }
+
+    if (PROTO(s) == PICO_PROTO_TCP)
+        pico_socket_alter_state(s, PICO_SOCKET_STATE_TCP_SYN_SENT, 0, PICO_SOCKET_STATE_TCP_LISTEN);
+
+    s->max_backlog = (uint16_t)backlog;
+
+    return 0;
+}
+
+struct pico_socket *pico_socket_accept(struct pico_socket *s, void *orig, uint16_t *port)
+{
+    if (!s || !orig || !port) {
+        pico_err = PICO_ERR_EINVAL;
+        return NULL;
+    }
+
+    pico_err = PICO_ERR_EINVAL;
+
+    if ((s->state & PICO_SOCKET_STATE_BOUND) == 0) {
+        return NULL;
+    }
+
+    if (PROTO(s) == PICO_PROTO_UDP) {
+        return NULL;
+    }
+
+    if (TCPSTATE(s) == PICO_SOCKET_STATE_TCP_LISTEN) {
+        struct pico_sockport *sp = pico_get_sockport(PICO_PROTO_TCP, s->local_port);
+        struct pico_socket *found;
+        uint32_t socklen = sizeof(struct pico_ip4);
+        /* If at this point no incoming connection socket is found,
+         * the accept call is valid, but no connection is established yet.
+         */
+        pico_err = PICO_ERR_EAGAIN;
+        if (sp) {
+            struct pico_tree_node *index;
+            /* RB_FOREACH(found, socket_tree, &sp->socks) { */
+            pico_tree_foreach(index, &sp->socks){
+                found = index->keyValue;
+                if ((s == found->parent) && ((found->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_ESTABLISHED)) {
+                    found->parent = NULL;
+                    pico_err = PICO_ERR_NOERR;
+                    #ifdef PICO_SUPPORT_IPV6
+                    if (is_sock_ipv6(s))
+                        socklen = sizeof(struct pico_ip6);
+
+                    #endif
+                    memcpy(orig, &found->remote_addr, socklen);
+                    *port = found->remote_port;
+                    s->number_of_pending_conn--;
+                    return found;
+                }
+            }
+        }
+    }
+
+    return NULL;
+}
+
+#else
+
+int pico_socket_listen(struct pico_socket *s, int backlog)
+{
+    IGNORE_PARAMETER(s);
+    IGNORE_PARAMETER(backlog);
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+}
+
+struct pico_socket *pico_socket_accept(struct pico_socket *s, void *orig, uint16_t *local_port)
+{
+    IGNORE_PARAMETER(s);
+    IGNORE_PARAMETER(orig);
+    IGNORE_PARAMETER(local_port);
+    pico_err = PICO_ERR_EINVAL;
+    return NULL;
+}
+
+#endif
+
+
+int pico_socket_setoption(struct pico_socket *s, int option, void *value)
+{
+
+    if (s == NULL) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+
+    if (PROTO(s) == PICO_PROTO_TCP)
+        return pico_setsockopt_tcp(s, option, value);
+
+    if (PROTO(s) == PICO_PROTO_UDP)
+        return pico_setsockopt_udp(s, option, value);
+
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+    return -1;
+}
+
+
+int pico_socket_getoption(struct pico_socket *s, int option, void *value)
+{
+    if (s == NULL) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+
+    if (PROTO(s) == PICO_PROTO_TCP)
+        return pico_getsockopt_tcp(s, option, value);
+
+    if (PROTO(s) == PICO_PROTO_UDP)
+        return pico_getsockopt_udp(s, option, value);
+
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+    return -1;
+}
+
+
+int pico_socket_shutdown(struct pico_socket *s, int mode)
+{
+    if (!s) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    /* Check if the socket has already been closed */
+    if (s->state & PICO_SOCKET_STATE_CLOSED) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+#ifdef PICO_SUPPORT_UDP
+    if (PROTO(s) == PICO_PROTO_UDP) {
+        if ((mode & PICO_SHUT_RDWR) == PICO_SHUT_RDWR)
+            pico_socket_alter_state(s, PICO_SOCKET_STATE_CLOSED, PICO_SOCKET_STATE_CLOSING | PICO_SOCKET_STATE_BOUND | PICO_SOCKET_STATE_CONNECTED, 0);
+        else if (mode & PICO_SHUT_RD)
+            pico_socket_alter_state(s, 0, PICO_SOCKET_STATE_BOUND, 0);
+    }
+
+#endif
+#ifdef PICO_SUPPORT_TCP
+    if (PROTO(s) == PICO_PROTO_TCP) {
+        if ((mode & PICO_SHUT_RDWR) == PICO_SHUT_RDWR)
+        {
+            pico_socket_alter_state(s, PICO_SOCKET_STATE_SHUT_LOCAL | PICO_SOCKET_STATE_SHUT_REMOTE, 0, 0);
+            pico_tcp_notify_closing(s);
+        }
+        else if (mode & PICO_SHUT_WR)
+            pico_socket_alter_state(s, PICO_SOCKET_STATE_SHUT_LOCAL, 0, 0);
+        else if (mode & PICO_SHUT_RD)
+            pico_socket_alter_state(s, PICO_SOCKET_STATE_SHUT_REMOTE, 0, 0);
+
+    }
+
+#endif
+    return 0;
+}
+
+int MOCKABLE pico_socket_close(struct pico_socket *s)
+{
+    if (!s)
+        return -1;
+
+#ifdef PICO_SUPPORT_TCP
+    if (PROTO(s) == PICO_PROTO_TCP) {
+        if (pico_tcp_check_listen_close(s) == 0)
+            return 0;
+    }
+
+#endif
+    return pico_socket_shutdown(s, PICO_SHUT_RDWR);
+}
+
+#ifdef PICO_SUPPORT_CRC
+static inline int pico_transport_crc_check(struct pico_frame *f)
+{
+    struct pico_ipv4_hdr *net_hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+    struct pico_udp_hdr *udp_hdr = NULL;
+    uint16_t checksum_invalid = 1;
+
+    switch (net_hdr->proto)
+    {
+#ifdef PICO_SUPPORT_TCP
+    case PICO_PROTO_TCP:
+        checksum_invalid = short_be(pico_tcp_checksum(f));
+        /* dbg("TCP CRC validation == %u\n", checksum_invalid); */
+        if (checksum_invalid) {
+            dbg("TCP CRC: validation failed!\n");
+            pico_frame_discard(f);
+            return 0;
+        }
+
+        break;
+#endif /* PICO_SUPPORT_TCP */
+
+#ifdef PICO_SUPPORT_UDP
+    case PICO_PROTO_UDP:
+        udp_hdr = (struct pico_udp_hdr *) f->transport_hdr;
+        if (short_be(udp_hdr->crc)) {
+#ifdef PICO_SUPPORT_IPV4
+            if (IS_IPV4(f))
+                checksum_invalid = short_be(pico_udp_checksum_ipv4(f));
+
+#endif
+#ifdef PICO_SUPPORT_IPV6
+            if (IS_IPV6(f))
+                checksum_invalid = short_be(pico_udp_checksum_ipv6(f));
+
+#endif
+            /* dbg("UDP CRC validation == %u\n", checksum_invalid); */
+            if (checksum_invalid) {
+                /* dbg("UDP CRC: validation failed!\n"); */
+                pico_frame_discard(f);
+                return 0;
+            }
+        }
+
+        break;
+#endif /* PICO_SUPPORT_UDP */
+
+    default:
+        /* Do nothing */
+        break;
+    }
+    return 1;
+}
+#else
+static inline int pico_transport_crc_check(struct pico_frame *f)
+{
+    IGNORE_PARAMETER(f);
+    return 1;
+}
+#endif /* PICO_SUPPORT_CRC */
+
+int pico_transport_process_in(struct pico_protocol *self, struct pico_frame *f)
+{
+    struct pico_trans *hdr = (struct pico_trans *) f->transport_hdr;
+    int ret = 0;
+
+    if (!hdr) {
+        pico_err = PICO_ERR_EFAULT;
+        return -1;
+    }
+
+    ret = pico_transport_crc_check(f);
+    if (ret < 1)
+        return ret;
+    else
+        ret = 0;
+
+    if ((hdr) && (pico_socket_deliver(self, f, hdr->dport) == 0))
+        return ret;
+
+    if (!IS_BCAST(f)) {
+        dbg("Socket not found... \n");
+        pico_notify_socket_unreachable(f);
+        ret = -1;
+        pico_err = PICO_ERR_ENOENT;
+    }
+
+    pico_frame_discard(f);
+    return ret;
+}
+
+#define SL_LOOP_MIN 1
+
+#ifdef PICO_SUPPORT_TCP
+static int checkSocketSanity(struct pico_socket *s)
+{
+
+    /* checking for pending connections */
+    if(TCP_STATE(s) == PICO_SOCKET_STATE_TCP_SYN_RECV) {
+        if((PICO_TIME_MS() - s->timestamp) >= PICO_SOCKET_BOUND_TIMEOUT)
+            return -1;
+    }
+
+    if((PICO_TIME_MS() - s->timestamp) >= PICO_SOCKET_TIMEOUT) {
+        /* checking for hanging sockets */
+        if((TCP_STATE(s) != PICO_SOCKET_STATE_TCP_LISTEN) && (TCP_STATE(s) != PICO_SOCKET_STATE_TCP_ESTABLISHED))
+            return -1;
+    }
+
+    return 0;
+}
+#endif
+
+
+static int pico_sockets_loop_udp(int loop_score)
+{
+
+#ifdef PICO_SUPPORT_UDP
+    static struct pico_tree_node *index_udp;
+    struct pico_sockport *start;
+    struct pico_socket *s;
+    struct pico_frame *f;
+
+    if (sp_udp == NULL)
+    {
+        index_udp = pico_tree_firstNode(UDPTable.root);
+        sp_udp = index_udp->keyValue;
+    }
+
+    /* init start node */
+    start = sp_udp;
+
+    /* round-robin all transport protocols, break if traversed all protocols */
+    while (loop_score > SL_LOOP_MIN && sp_udp != NULL) {
+        struct pico_tree_node *index;
+
+        pico_tree_foreach(index, &sp_udp->socks){
+            s = index->keyValue;
+            f = pico_dequeue(&s->q_out);
+            while (f && (loop_score > 0)) {
+                pico_proto_udp.push(&pico_proto_udp, f);
+                loop_score -= 1;
+                f = pico_dequeue(&s->q_out);
+            }
+        }
+
+        index_udp = pico_tree_next(index_udp);
+        sp_udp = index_udp->keyValue;
+
+        if (sp_udp == NULL)
+        {
+            index_udp = pico_tree_firstNode(UDPTable.root);
+            sp_udp = index_udp->keyValue;
+        }
+
+        if (sp_udp == start)
+            break;
+    }
+#endif
+    return loop_score;
+}
+
+static int pico_sockets_loop_tcp(int loop_score)
+{
+#ifdef PICO_SUPPORT_TCP
+    struct pico_sockport *start;
+    struct pico_socket *s;
+    static struct pico_tree_node *index_tcp;
+    if (sp_tcp == NULL)
+    {
+        index_tcp = pico_tree_firstNode(TCPTable.root);
+        sp_tcp = index_tcp->keyValue;
+    }
+
+    /* init start node */
+    start = sp_tcp;
+
+    while (loop_score > SL_LOOP_MIN && sp_tcp != NULL) {
+        struct pico_tree_node *index = NULL, *safe_index = NULL;
+        pico_tree_foreach_safe(index, &sp_tcp->socks, safe_index){
+            s = index->keyValue;
+            loop_score = pico_tcp_output(s, loop_score);
+            if ((s->ev_pending) && s->wakeup) {
+                s->wakeup(s->ev_pending, s);
+                if(!s->parent)
+                    s->ev_pending = 0;
+            }
+
+            if (loop_score <= 0) {
+                loop_score = 0;
+                break;
+            }
+
+            if(checkSocketSanity(s) < 0)
+            {
+                pico_socket_del(s);
+                index_tcp = NULL; /* forcing the restart of loop */
+                sp_tcp = NULL;
+                break;
+            }
+        }
+
+        /* check if RB_FOREACH ended, if not, break to keep the cur sp_tcp */
+        if (!index_tcp || (index && index->keyValue))
+            break;
+
+        index_tcp = pico_tree_next(index_tcp);
+        sp_tcp = index_tcp->keyValue;
+
+        if (sp_tcp == NULL)
+        {
+            index_tcp = pico_tree_firstNode(TCPTable.root);
+            sp_tcp = index_tcp->keyValue;
+        }
+
+        if (sp_tcp == start)
+            break;
+    }
+#endif
+    return loop_score;
+
+
+}
+
+int pico_sockets_loop(int loop_score)
+{
+    loop_score = pico_sockets_loop_udp(loop_score);
+    loop_score = pico_sockets_loop_tcp(loop_score);
+    return loop_score;
+}
+
+int pico_count_sockets(uint8_t proto)
+{
+    struct pico_sockport *sp;
+    struct pico_tree_node *idx_sp, *idx_s;
+    int count = 0;
+
+    if ((proto == 0) || (proto == PICO_PROTO_TCP)) {
+        pico_tree_foreach(idx_sp, &TCPTable) {
+            sp = idx_sp->keyValue;
+            if (sp) {
+                pico_tree_foreach(idx_s, &sp->socks)
+                count++;
+            }
+        }
+    }
+
+    if ((proto == 0) || (proto == PICO_PROTO_UDP)) {
+        pico_tree_foreach(idx_sp, &UDPTable) {
+            sp = idx_sp->keyValue;
+            if (sp) {
+                pico_tree_foreach(idx_s, &sp->socks)
+                count++;
+            }
+        }
+    }
+
+    return count;
+}
+
+
+struct pico_frame *pico_socket_frame_alloc(struct pico_socket *s, uint16_t len)
+{
+    struct pico_frame *f = NULL;
+
+#ifdef PICO_SUPPORT_IPV6
+    if (is_sock_ipv6(s))
+        f = pico_proto_ipv6.alloc(&pico_proto_ipv6, len);
+
+#endif
+
+#ifdef PICO_SUPPORT_IPV4
+    if (is_sock_ipv4(s))
+        f = pico_proto_ipv4.alloc(&pico_proto_ipv4, len);
+
+#endif
+    if (!f) {
+        pico_err = PICO_ERR_ENOMEM;
+        return f;
+    }
+
+    f->payload = f->transport_hdr;
+    f->payload_len = len;
+    f->sock = s;
+    return f;
+}
+
+static void pico_transport_error_set_picoerr(int code)
+{
+    /* dbg("SOCKET ERROR FROM ICMP NOTIFICATION. (icmp code= %d)\n\n", code); */
+    switch(code) {
+    case PICO_ICMP_UNREACH_NET:
+        pico_err = PICO_ERR_ENETUNREACH;
+        break;
+
+    case PICO_ICMP_UNREACH_HOST:
+        pico_err = PICO_ERR_EHOSTUNREACH;
+        break;
+
+    case PICO_ICMP_UNREACH_PROTOCOL:
+        pico_err = PICO_ERR_ENOPROTOOPT;
+        break;
+
+    case PICO_ICMP_UNREACH_PORT:
+        pico_err = PICO_ERR_ECONNREFUSED;
+        break;
+
+    case PICO_ICMP_UNREACH_NET_UNKNOWN:
+        pico_err = PICO_ERR_ENETUNREACH;
+        break;
+
+    case PICO_ICMP_UNREACH_HOST_UNKNOWN:
+        pico_err = PICO_ERR_EHOSTDOWN;
+        break;
+
+    case PICO_ICMP_UNREACH_ISOLATED:
+        pico_err = PICO_ERR_ENONET;
+        break;
+
+    case PICO_ICMP_UNREACH_NET_PROHIB:
+    case PICO_ICMP_UNREACH_HOST_PROHIB:
+        pico_err = PICO_ERR_EHOSTUNREACH;
+        break;
+
+    default:
+        pico_err = PICO_ERR_EOPNOTSUPP;
+    }
+}
+
+int pico_transport_error(struct pico_frame *f, uint8_t proto, int code)
+{
+    int ret = -1;
+    struct pico_trans *trans = (struct pico_trans*) f->transport_hdr;
+    struct pico_sockport *port = NULL;
+    struct pico_socket *s = NULL;
+    switch (proto) {
+
+
+#ifdef PICO_SUPPORT_UDP
+    case PICO_PROTO_UDP:
+        port = pico_get_sockport(proto, trans->sport);
+        break;
+#endif
+
+#ifdef PICO_SUPPORT_TCP
+    case PICO_PROTO_TCP:
+        port = pico_get_sockport(proto, trans->sport);
+        break;
+#endif
+
+    default:
+        /* Protocol not available */
+        ret = -1;
+    }
+    if (port) {
+        struct pico_tree_node *index;
+        ret = 0;
+
+        pico_tree_foreach(index, &port->socks) {
+            s = index->keyValue;
+            if (trans->dport == s->remote_port) {
+                if (s->wakeup) {
+                    pico_transport_error_set_picoerr(code);
+                    s->state |= PICO_SOCKET_STATE_SHUT_REMOTE;
+                    s->wakeup(PICO_SOCK_EV_ERR, s);
+                }
+
+                break;
+            }
+        }
+    }
+
+    pico_frame_discard(f);
+    return ret;
+}
+#endif
+#endif
diff --git a/net/picotcp/stack/pico_socket_multicast.c b/net/picotcp/stack/pico_socket_multicast.c
new file mode 100644
index 0000000..f23327d
--- /dev/null
+++ b/net/picotcp/stack/pico_socket_multicast.c
@@ -0,0 +1,956 @@
+#include "pico_config.h"
+#include "pico_stack.h"
+#include "pico_socket.h"
+#include "pico_socket_multicast.h"
+#include "pico_tree.h"
+#include "pico_ipv4.h"
+#include "pico_udp.h"
+
+#ifdef PICO_SUPPORT_MCAST
+# define so_mcast_dbg(...) do {} while(0) /* ip_mcast_dbg in pico_ipv4.c */
+/* #define so_mcast_dbg dbg */
+
+/*                       socket
+ *                         |
+ *                    MCASTListen
+ *                    |    |     |
+ *         ------------    |     ------------
+ *         |               |                |
+ *   MCASTSources    MCASTSources     MCASTSources
+ *   |  |  |  |      |  |  |  |       |  |  |  |
+ *   S  S  S  S      S  S  S  S       S  S  S  S
+ *
+ *   MCASTListen: RBTree(mcast_link, mcast_group)
+ *   MCASTSources: RBTree(source)
+ */
+struct pico_mcast_listen
+{
+    uint8_t filter_mode;
+    union pico_address mcast_link;
+    union pico_address mcast_group;
+    struct pico_tree MCASTSources;
+    uint16_t proto;
+};
+
+static int mcast_listen_link_cmp(struct pico_mcast_listen *a, struct pico_mcast_listen *b)
+{
+
+    if (a->proto < b->proto)
+        return -1;
+
+    if (a->proto > b->proto)
+        return 1;
+
+    return pico_address_compare(&a->mcast_link, &b->mcast_link, a->proto);
+}
+
+static int mcast_listen_grp_cmp(struct pico_mcast_listen *a, struct pico_mcast_listen *b)
+{
+    if (a->mcast_group.ip4.addr < b->mcast_group.ip4.addr)
+        return -1;
+
+    if (a->mcast_group.ip4.addr > b->mcast_group.ip4.addr)
+        return 1;
+
+    return mcast_listen_link_cmp(a, b);
+}
+
+static int mcast_listen_cmp(void *ka, void *kb)
+{
+    struct pico_mcast_listen *a = ka, *b = kb;
+    if (a->proto < b->proto)
+        return -1;
+
+    if (a->proto > b->proto)
+        return 1;
+
+    return mcast_listen_grp_cmp(a, b);
+}
+
+static int mcast_sources_cmp(void *ka, void *kb)
+{
+    union pico_address *a = ka, *b = kb;
+    if (a->ip4.addr < b->ip4.addr)
+        return -1;
+
+    if (a->ip4.addr > b->ip4.addr)
+        return 1;
+
+    return 0;
+}
+
+static int mcast_socket_cmp(void *ka, void *kb)
+{
+    struct pico_socket *a = ka, *b = kb;
+    if (a < b)
+        return -1;
+
+    if (a > b)
+        return 1;
+
+    return 0;
+}
+
+/* gather all multicast sockets to hasten filter aggregation */
+PICO_TREE_DECLARE(MCASTSockets, mcast_socket_cmp);
+
+static int mcast_filter_cmp(void *ka, void *kb)
+{
+    union pico_address *a = ka, *b = kb;
+    if (a->ip4.addr < b->ip4.addr)
+        return -1;
+
+    if (a->ip4.addr > b->ip4.addr)
+        return 1;
+
+    return 0;
+}
+/* gather sources to be filtered */
+PICO_TREE_DECLARE(MCASTFilter, mcast_filter_cmp);
+
+static struct pico_mcast_listen *listen_find(struct pico_socket *s, union pico_address *lnk, union pico_address *grp)
+{
+    struct pico_mcast_listen ltest = {
+        0
+    };
+    ltest.mcast_link.ip4.addr = lnk->ip4.addr;
+    ltest.mcast_group.ip4.addr = grp->ip4.addr;
+    return pico_tree_findKey(s->MCASTListen, &ltest);
+}
+
+static uint8_t pico_mcast_filter_excl_excl(struct pico_mcast_listen *listen)
+{
+    /* filter = intersection of EXCLUDEs */
+    /* any record with filter mode EXCLUDE, causes the interface mode to be EXCLUDE */
+    /* remove from the interface EXCLUDE filter any source not in the socket EXCLUDE filter */
+    struct pico_tree_node *index = NULL, *_tmp = NULL;
+    union pico_address *source = NULL;
+    pico_tree_foreach_safe(index, &MCASTFilter, _tmp)
+    {
+        source = pico_tree_findKey(&listen->MCASTSources, index->keyValue);
+        if (!source)
+            pico_tree_delete(&MCASTFilter, index->keyValue);
+    }
+    return PICO_IP_MULTICAST_EXCLUDE;
+}
+
+static uint8_t pico_mcast_filter_excl_incl(struct pico_mcast_listen *listen)
+{
+    /* filter = EXCLUDE - INCLUDE */
+    /* any record with filter mode EXCLUDE, causes the interface mode to be EXCLUDE */
+    /* remove from the interface EXCLUDE filter any source in the socket INCLUDE filter */
+    struct pico_tree_node *index = NULL, *_tmp = NULL;
+    union pico_address *source = NULL;
+    pico_tree_foreach_safe(index, &listen->MCASTSources, _tmp)
+    {
+        source = pico_tree_findKey(&MCASTFilter, index->keyValue);
+        if (source)
+            pico_tree_delete(&MCASTFilter, source);
+    }
+    return PICO_IP_MULTICAST_EXCLUDE;
+}
+
+static uint8_t pico_mcast_filter_incl_excl(struct pico_mcast_listen *listen)
+{
+    /* filter = EXCLUDE - INCLUDE */
+    /* delete from the interface INCLUDE filter any source NOT in the socket EXCLUDE filter */
+    struct pico_tree_node *index = NULL, *_tmp = NULL, *index2 = NULL, *_tmp2 = NULL;
+    union pico_address *source = NULL;
+    pico_tree_foreach_safe(index2, &MCASTFilter, _tmp2)
+    {
+        source = pico_tree_findKey(&listen->MCASTSources, index2->keyValue);
+        if (!source)
+            pico_tree_delete(&MCASTFilter, index2->keyValue);
+    }
+    /* any record with filter mode EXCLUDE, causes the interface mode to be EXCLUDE */
+
+    /* add to the interface EXCLUDE filter any socket source NOT in the former interface INCLUDE filter */
+    pico_tree_foreach_safe(index, &listen->MCASTSources, _tmp)
+    {
+        source = pico_tree_insert(&MCASTFilter, index->keyValue);
+        if (source)
+            pico_tree_delete(&MCASTFilter, source);
+    }
+    return PICO_IP_MULTICAST_EXCLUDE;
+}
+
+static uint8_t pico_mcast_filter_incl_incl(struct pico_mcast_listen *listen)
+{
+    /* filter = summation of INCLUDEs */
+    /* mode stays INCLUDE, add all sources to filter */
+    struct pico_tree_node *index = NULL, *_tmp = NULL;
+    union pico_address *source = NULL;
+    pico_tree_foreach_safe(index, &listen->MCASTSources, _tmp)
+    {
+        source = index->keyValue;
+        pico_tree_insert(&MCASTFilter, source);
+    }
+    return PICO_IP_MULTICAST_INCLUDE;
+}
+
+struct pico_mcast_filter_aggregation
+{
+    uint8_t (*call)(struct pico_mcast_listen *);
+};
+
+static const struct pico_mcast_filter_aggregation mcast_filter_aggr_call[2][2] =
+{
+    {
+        /* EXCL + EXCL */ {.call = pico_mcast_filter_excl_excl},
+        /* EXCL + INCL */ {.call = pico_mcast_filter_excl_incl}
+    },
+
+    {
+        /* INCL + EXCL */ {.call = pico_mcast_filter_incl_excl},
+        /* INCL + INCL */ {.call = pico_mcast_filter_incl_incl}
+    }
+};
+
+static int mcast_aggr_validate(uint8_t fm, struct pico_mcast_listen *l)
+{
+    if (!l)
+        return -1;
+
+    if (fm > 1)
+        return -1;
+
+    if (l->filter_mode > 1)
+        return -1;
+
+    return 0;
+}
+
+
+/* MCASTFilter will be empty if no socket is listening on mcast_group on mcast_link anymore */
+static int pico_socket_aggregate_mcastfilters(union pico_address *mcast_link, union pico_address *mcast_group)
+{
+    uint8_t filter_mode = PICO_IP_MULTICAST_INCLUDE;
+    struct pico_mcast_listen *listen = NULL;
+    struct pico_socket *mcast_sock = NULL;
+    struct pico_tree_node *index = NULL, *_tmp = NULL;
+
+
+    /* cleanup old filter */
+    pico_tree_foreach_safe(index, &MCASTFilter, _tmp)
+    {
+        pico_tree_delete(&MCASTFilter, index->keyValue);
+    }
+
+    /* construct new filter */
+    pico_tree_foreach_safe(index, &MCASTSockets, _tmp)
+    {
+        mcast_sock = index->keyValue;
+        listen = listen_find(mcast_sock, mcast_link, mcast_group);
+        if (listen) {
+            if (mcast_aggr_validate(filter_mode, listen) < 0) {
+                pico_err = PICO_ERR_EINVAL;
+                return -1;
+            }
+
+            if (mcast_filter_aggr_call[filter_mode][listen->filter_mode].call) {
+                filter_mode = mcast_filter_aggr_call[filter_mode][listen->filter_mode].call(listen);
+                if (filter_mode > 1)
+                    return -1;
+            }
+        }
+    }
+    return filter_mode;
+}
+
+static int pico_socket_mcast_filter_include(struct pico_mcast_listen *listen, union pico_address *src)
+{
+    struct pico_tree_node *index = NULL;
+    pico_tree_foreach(index, &listen->MCASTSources)
+    {
+        if (src->ip4.addr == ((union pico_address *)index->keyValue)->ip4.addr) {
+            so_mcast_dbg("MCAST: IP %08X in included socket source list\n", src->ip4.addr);
+            return 0;
+        }
+    }
+    so_mcast_dbg("MCAST: IP %08X NOT in included socket source list\n", src->ip4.addr);
+    return -1;
+
+}
+
+static int pico_socket_mcast_filter_exclude(struct pico_mcast_listen *listen, union pico_address *src)
+{
+    struct pico_tree_node *index = NULL;
+    pico_tree_foreach(index, &listen->MCASTSources)
+    {
+        if (src->ip4.addr == ((union pico_address *)index->keyValue)->ip4.addr) {
+            so_mcast_dbg("MCAST: IP %08X in excluded socket source list\n", src->ip4.addr);
+            return -1;
+        }
+    }
+    so_mcast_dbg("MCAST: IP %08X NOT in excluded socket source list\n", src->ip4.addr);
+    return 0;
+}
+
+static int pico_socket_mcast_source_filtering(struct pico_mcast_listen *listen, union pico_address *src)
+{
+    /* perform source filtering */
+    if (listen->filter_mode == PICO_IP_MULTICAST_INCLUDE)
+        return pico_socket_mcast_filter_include(listen, src);
+
+    if (listen->filter_mode == PICO_IP_MULTICAST_EXCLUDE)
+        return pico_socket_mcast_filter_exclude(listen, src);
+
+    return -1;
+}
+
+static struct pico_ipv4_link *pico_socket_mcast_filter_link_get(struct pico_socket *s)
+{
+    /* check if no multicast enabled on socket */
+    if (!s->MCASTListen)
+        return NULL;
+
+    if (!s->local_addr.ip4.addr)
+        return pico_ipv4_get_default_mcastlink();
+
+    return pico_ipv4_link_get(&s->local_addr.ip4);
+}
+
+int pico_socket_mcast_filter(struct pico_socket *s, union pico_address *mcast_group, union pico_address *src)
+{
+    struct pico_ipv4_link *mcast_link = NULL;
+    struct pico_mcast_listen *listen = NULL;
+
+    mcast_link = pico_socket_mcast_filter_link_get(s);
+    if (!mcast_link)
+        return -1;
+
+    listen = listen_find(s, (union pico_address *)&mcast_link->address, mcast_group);
+    if (!listen)
+        return -1;
+
+    return pico_socket_mcast_source_filtering(listen, src);
+}
+
+static struct pico_ipv4_link *get_mcast_link(union pico_address *a)
+{
+    if (!a->ip4.addr)
+        return pico_ipv4_get_default_mcastlink();
+
+    return pico_ipv4_link_get(&a->ip4);
+}
+
+
+static int pico_socket_setoption_pre_validation(struct pico_ip_mreq *mreq)
+{
+    if (!mreq)
+        return -1;
+
+    if (!mreq->mcast_group_addr.addr)
+        return -1;
+
+    return 0;
+}
+
+static struct pico_ipv4_link *pico_socket_setoption_validate_mreq(struct pico_ip_mreq *mreq)
+{
+    if (pico_socket_setoption_pre_validation(mreq) < 0)
+        return NULL;
+
+    if (pico_ipv4_is_unicast(mreq->mcast_group_addr.addr))
+        return NULL;
+
+    return get_mcast_link((union pico_address *)&mreq->mcast_link_addr);
+}
+
+static int pico_socket_setoption_pre_validation_s(struct pico_ip_mreq_source *mreq)
+{
+    if (!mreq)
+        return -1;
+
+    if (!mreq->mcast_group_addr.addr)
+        return -1;
+
+    return 0;
+}
+
+static struct pico_ipv4_link *pico_socket_setoption_validate_s_mreq(struct pico_ip_mreq_source *mreq)
+{
+    if (pico_socket_setoption_pre_validation_s(mreq) < 0)
+        return NULL;
+
+    if (pico_ipv4_is_unicast(mreq->mcast_group_addr.addr))
+        return NULL;
+
+    if (!pico_ipv4_is_unicast(mreq->mcast_source_addr.addr))
+        return NULL;
+
+    return get_mcast_link((union pico_address *)&mreq->mcast_link_addr);
+}
+
+
+static struct pico_ipv4_link *setop_multicast_link_search(void *value, int bysource)
+{
+
+    struct pico_ip_mreq *mreq = NULL;
+    struct pico_ipv4_link *mcast_link = NULL;
+    struct pico_ip_mreq_source *mreq_src = NULL;
+    if (!bysource) {
+        mreq = (struct pico_ip_mreq *) value;
+        mcast_link = pico_socket_setoption_validate_mreq(mreq);
+        if (!mcast_link)
+            return NULL;
+
+        if (!mreq->mcast_link_addr.addr)
+            mreq->mcast_link_addr.addr = mcast_link->address.addr;
+    } else {
+        mreq_src = (struct pico_ip_mreq_source *) value;
+        if (!mreq_src)
+            return NULL;
+
+        mcast_link = pico_socket_setoption_validate_s_mreq(mreq_src);
+        if (!mcast_link)
+            return NULL;
+
+        if (!mreq_src->mcast_link_addr.addr)
+            mreq_src->mcast_link_addr.addr = mcast_link->address.addr;
+    }
+
+    return mcast_link;
+}
+
+static int setop_verify_listen_tree(struct pico_socket *s, int alloc)
+{
+    if(!alloc)
+        return -1;
+
+    s->MCASTListen = PICO_ZALLOC(sizeof(struct pico_tree));
+    if (!s->MCASTListen) {
+        pico_err = PICO_ERR_ENOMEM;
+        return -1;
+    }
+
+    s->MCASTListen->root = &LEAF;
+    s->MCASTListen->compare = mcast_listen_cmp;
+    return 0;
+}
+
+
+static struct pico_ipv4_link *setopt_multicast_check(struct pico_socket *s, void *value, int alloc, int bysource)
+{
+    struct pico_ipv4_link *mcast_link = NULL;
+
+    if (!value) {
+        pico_err = PICO_ERR_EINVAL;
+        return NULL;
+    }
+
+    mcast_link = setop_multicast_link_search(value, bysource);
+
+    if (!mcast_link) {
+        pico_err = PICO_ERR_EINVAL;
+        return NULL;
+    }
+
+    if (!s->MCASTListen) { /* No RBTree allocated yet */
+        if (setop_verify_listen_tree(s, alloc) < 0)
+            return NULL;
+    }
+
+    return mcast_link;
+}
+
+
+void pico_multicast_delete(struct pico_socket *s)
+{
+    int filter_mode;
+    struct pico_tree_node *index = NULL, *_tmp = NULL, *index2 = NULL, *_tmp2 = NULL;
+    struct pico_mcast_listen *listen = NULL;
+    union pico_address *source = NULL;
+    if (s->MCASTListen) {
+        pico_tree_delete(&MCASTSockets, s);
+        pico_tree_foreach_safe(index, s->MCASTListen, _tmp)
+        {
+            listen = index->keyValue;
+            pico_tree_foreach_safe(index2, &listen->MCASTSources, _tmp2)
+            {
+                source = index->keyValue;
+                pico_tree_delete(&listen->MCASTSources, source);
+                PICO_FREE(source);
+            }
+            filter_mode = pico_socket_aggregate_mcastfilters((union pico_address *)&listen->mcast_link, (union pico_address *)&listen->mcast_group);
+            if (filter_mode >= 0)
+                pico_ipv4_mcast_leave(&listen->mcast_link.ip4, &listen->mcast_group.ip4, 1, (uint8_t)filter_mode, &MCASTFilter);
+
+            pico_tree_delete(s->MCASTListen, listen);
+            PICO_FREE(listen);
+        }
+        PICO_FREE(s->MCASTListen);
+    }
+}
+
+
+int pico_getsockopt_mcast(struct pico_socket *s, int option, void *value)
+{
+    switch(option) {
+    case PICO_IP_MULTICAST_IF:
+        pico_err = PICO_ERR_EOPNOTSUPP;
+        return -1;
+
+    case PICO_IP_MULTICAST_TTL:
+        if (s->proto->proto_number == PICO_PROTO_UDP) {
+            pico_udp_get_mc_ttl(s, (uint8_t *) value);
+        } else {
+            *(uint8_t *)value = 0;
+            pico_err = PICO_ERR_EINVAL;
+            return -1;
+        }
+
+        break;
+
+    case PICO_IP_MULTICAST_LOOP:
+        if (s->proto->proto_number == PICO_PROTO_UDP) {
+            *(uint8_t *)value = (uint8_t)PICO_SOCKET_GETOPT(s, PICO_SOCKET_OPT_MULTICAST_LOOP);
+        } else {
+            *(uint8_t *)value = 0;
+            pico_err = PICO_ERR_EINVAL;
+            return -1;
+        }
+
+        break;
+    default:
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    return 0;
+}
+
+static int mcast_so_loop(struct pico_socket *s, void *value)
+{
+    uint8_t val = (*(uint8_t *)value);
+    if (val == 0u) {
+        PICO_SOCKET_SETOPT_DIS(s, PICO_SOCKET_OPT_MULTICAST_LOOP);
+        return 0;
+    } else if (val == 1u) {
+        PICO_SOCKET_SETOPT_EN(s, PICO_SOCKET_OPT_MULTICAST_LOOP);
+        return 0;
+    }
+
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+}
+
+static int mcast_so_addm(struct pico_socket *s, void *value)
+{
+    int filter_mode;
+    struct pico_mcast_listen *listen;
+    struct pico_ip_mreq *mreq = (struct pico_ip_mreq *)value;
+    struct pico_ipv4_link *mcast_link = setopt_multicast_check(s, value, 1, 0);
+    if (!mcast_link)
+        return -1;
+
+    listen = listen_find(s, (union pico_address *)&mreq->mcast_link_addr, (union pico_address *)&mreq->mcast_group_addr);
+    if (listen) {
+        if (listen->filter_mode != PICO_IP_MULTICAST_EXCLUDE) {
+            so_mcast_dbg("pico_socket_setoption: ERROR any-source multicast (exclude) on source-specific multicast (include)\n");
+            pico_err = PICO_ERR_EINVAL;
+            return -1;
+        } else {
+            so_mcast_dbg("pico_socket_setoption: ERROR duplicate PICO_IP_ADD_MEMBERSHIP\n");
+            pico_err = PICO_ERR_EINVAL;
+            return -1;
+        }
+    } else {
+        listen = PICO_ZALLOC(sizeof(struct pico_mcast_listen));
+        if (!listen) {
+            pico_err = PICO_ERR_ENOMEM;
+            return -1;
+        }
+
+        listen->filter_mode = PICO_IP_MULTICAST_EXCLUDE;
+        listen->mcast_link.ip4 = mreq->mcast_link_addr;
+        listen->mcast_group.ip4 = mreq->mcast_group_addr;
+        listen->MCASTSources.root = &LEAF;
+        listen->MCASTSources.compare = mcast_sources_cmp;
+        listen->proto = s->net->proto_number;
+        pico_tree_insert(s->MCASTListen, listen);
+    }
+
+    pico_tree_insert(&MCASTSockets, s);
+    filter_mode = pico_socket_aggregate_mcastfilters((union pico_address *)&mcast_link->address, (union pico_address *)&mreq->mcast_group_addr);
+    if (filter_mode < 0)
+        return -1;
+
+    so_mcast_dbg("PICO_IP_ADD_MEMBERSHIP - success, added %p\n", s);
+    return pico_ipv4_mcast_join(&mreq->mcast_link_addr, &mreq->mcast_group_addr, 1, (uint8_t)filter_mode, &MCASTFilter);
+}
+
+static int mcast_so_dropm(struct pico_socket *s, void *value)
+{
+    int filter_mode = 0;
+    struct pico_mcast_listen *listen;
+    struct pico_ip_mreq *mreq = (struct pico_ip_mreq *)value;
+    union pico_address *source = NULL;
+    struct pico_tree_node *index, *_tmp;
+    struct pico_ipv4_link *mcast_link = setopt_multicast_check(s, value, 0, 0);
+    if (!mcast_link)
+        return -1;
+
+    listen = listen_find(s, (union pico_address *)&mreq->mcast_link_addr, (union pico_address *)&mreq->mcast_group_addr);
+    if (!listen) {
+        so_mcast_dbg("pico_socket_setoption: ERROR PICO_IP_DROP_MEMBERSHIP before PICO_IP_ADD_MEMBERSHIP/SOURCE_MEMBERSHIP\n");
+        pico_err = PICO_ERR_EADDRNOTAVAIL;
+        return -1;
+    } else {
+        pico_tree_foreach_safe(index, &listen->MCASTSources, _tmp)
+        {
+            source = index->keyValue;
+            pico_tree_delete(&listen->MCASTSources, source);
+        }
+        pico_tree_delete(s->MCASTListen, listen);
+        PICO_FREE(listen);
+        if (pico_tree_empty(s->MCASTListen)) {
+            PICO_FREE(s->MCASTListen);
+            s->MCASTListen = NULL;
+            pico_tree_delete(&MCASTSockets, s);
+        }
+    }
+
+    filter_mode = pico_socket_aggregate_mcastfilters((union pico_address *)&mcast_link->address, (union pico_address *)&mreq->mcast_group_addr);
+    if (filter_mode < 0)
+        return -1;
+
+    return pico_ipv4_mcast_leave(&mreq->mcast_link_addr, &mreq->mcast_group_addr, 1, (uint8_t)filter_mode, &MCASTFilter);
+}
+
+static int mcast_so_unblock_src(struct pico_socket *s, void *value)
+{
+    int filter_mode = 0;
+    struct pico_ip_mreq_source *mreq = (struct pico_ip_mreq_source *)value;
+    struct pico_mcast_listen *listen = NULL;
+    union pico_address *source = NULL, stest;
+    struct pico_ipv4_link *mcast_link = setopt_multicast_check(s, value, 0, 1);
+
+    memset(&stest, 0, sizeof(union pico_address));
+    if (!mcast_link)
+        return -1;
+
+
+    listen = listen_find(s, (union pico_address *) &mreq->mcast_link_addr, (union pico_address *) &mreq->mcast_group_addr);
+    if (!listen) {
+        so_mcast_dbg("pico_socket_setoption: ERROR PICO_IP_UNBLOCK_SOURCE before PICO_IP_ADD_MEMBERSHIP\n");
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    } else {
+        if (listen->filter_mode != PICO_IP_MULTICAST_EXCLUDE) {
+            so_mcast_dbg("pico_socket_setoption: ERROR any-source multicast (exclude) on source-specific multicast (include)\n");
+            pico_err = PICO_ERR_EINVAL;
+            return -1;
+        }
+
+        stest.ip4.addr = mreq->mcast_source_addr.addr;
+        source = pico_tree_findKey(&listen->MCASTSources, &stest);
+        if (!source) {
+            so_mcast_dbg("pico_socket_setoption: ERROR address to unblock not in source list\n");
+            pico_err = PICO_ERR_EADDRNOTAVAIL;
+            return -1;
+        } else {
+            pico_tree_delete(&listen->MCASTSources, source);
+        }
+    }
+
+    filter_mode = pico_socket_aggregate_mcastfilters((union pico_address *)&mcast_link->address, (union pico_address *)&mreq->mcast_group_addr);
+    if (filter_mode < 0)
+        return -1;
+
+    return pico_ipv4_mcast_leave(&mreq->mcast_link_addr, &mreq->mcast_group_addr, 0, (uint8_t)filter_mode, &MCASTFilter);
+}
+
+static int mcast_so_block_src(struct pico_socket *s, void *value)
+{
+    int filter_mode = 0;
+    struct pico_ip_mreq_source *mreq = (struct pico_ip_mreq_source *)value;
+    struct pico_mcast_listen *listen;
+    union pico_address *source, stest;
+    struct pico_ipv4_link *mcast_link = setopt_multicast_check(s, value, 0, 1);
+    if (!mcast_link)
+        return -1;
+
+    memset(&stest, 0, sizeof(union pico_address));
+
+    listen = listen_find(s, (union pico_address *)&mreq->mcast_link_addr, (union pico_address *)&mreq->mcast_group_addr);
+    if (!listen) {
+        dbg("pico_socket_setoption: ERROR PICO_IP_BLOCK_SOURCE before PICO_IP_ADD_MEMBERSHIP\n");
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    } else {
+        if (listen->filter_mode != PICO_IP_MULTICAST_EXCLUDE) {
+            so_mcast_dbg("pico_socket_setoption: ERROR any-source multicast (exclude) on source-specific multicast (include)\n");
+            pico_err = PICO_ERR_EINVAL;
+            return -1;
+        }
+
+        stest.ip4.addr = mreq->mcast_source_addr.addr;
+        source = pico_tree_findKey(&listen->MCASTSources, &stest);
+        if (source) {
+            so_mcast_dbg("pico_socket_setoption: ERROR address to block already in source list\n");
+            pico_err = PICO_ERR_EADDRNOTAVAIL;
+            return -1;
+        } else {
+            source = PICO_ZALLOC(sizeof(union pico_address));
+            if (!source) {
+                pico_err = PICO_ERR_ENOMEM;
+                return -1;
+            }
+
+            source->ip4.addr = mreq->mcast_source_addr.addr;
+            pico_tree_insert(&listen->MCASTSources, source);
+        }
+    }
+
+    filter_mode = pico_socket_aggregate_mcastfilters((union pico_address *)&mcast_link->address, (union pico_address *)&mreq->mcast_group_addr);
+    if (filter_mode < 0)
+        return -1;
+
+    return pico_ipv4_mcast_join(&mreq->mcast_link_addr, &mreq->mcast_group_addr, 0, (uint8_t)filter_mode, &MCASTFilter);
+}
+
+static int mcast_so_addsrcm(struct pico_socket *s, void *value)
+{
+    int filter_mode = 0, reference_count = 0;
+    struct pico_ip_mreq_source *mreq = (struct pico_ip_mreq_source *)value;
+    struct pico_mcast_listen *listen = NULL;
+    union pico_address *source = NULL, stest;
+    struct pico_ipv4_link *mcast_link = setopt_multicast_check(s, value, 1, 1);
+    if (!mcast_link)
+        return -1;
+
+    memset(&stest, 0, sizeof(union pico_address));
+
+    listen = listen_find(s, (union pico_address *)&mreq->mcast_link_addr, (union pico_address *) &mreq->mcast_group_addr);
+    if (listen) {
+        if (listen->filter_mode != PICO_IP_MULTICAST_INCLUDE) {
+            so_mcast_dbg("pico_socket_setoption: ERROR source-specific multicast (include) on any-source multicast (exclude)\n");
+            pico_err = PICO_ERR_EINVAL;
+            return -1;
+        }
+
+        stest.ip4.addr = mreq->mcast_source_addr.addr;
+        source = pico_tree_findKey(&listen->MCASTSources, &stest);
+        if (source) {
+            so_mcast_dbg("pico_socket_setoption: ERROR source address to allow already in source list\n");
+            pico_err = PICO_ERR_EADDRNOTAVAIL;
+            return -1;
+        } else {
+            source = PICO_ZALLOC(sizeof(union pico_address));
+            if (!source) {
+                pico_err = PICO_ERR_ENOMEM;
+                return -1;
+            }
+
+            source->ip4.addr = mreq->mcast_source_addr.addr;
+            pico_tree_insert(&listen->MCASTSources, source);
+        }
+    } else {
+        listen = PICO_ZALLOC(sizeof(struct pico_mcast_listen));
+        if (!listen) {
+            pico_err = PICO_ERR_ENOMEM;
+            return -1;
+        }
+
+        listen->filter_mode = PICO_IP_MULTICAST_INCLUDE;
+        listen->mcast_link.ip4 = mreq->mcast_link_addr;
+        listen->mcast_group.ip4 = mreq->mcast_group_addr;
+        listen->MCASTSources.root = &LEAF;
+        listen->MCASTSources.compare = mcast_sources_cmp;
+        source = PICO_ZALLOC(sizeof(union pico_address));
+        if (!source) {
+            PICO_FREE(listen);
+            pico_err = PICO_ERR_ENOMEM;
+            return -1;
+        }
+
+        source->ip4.addr = mreq->mcast_source_addr.addr;
+        pico_tree_insert(&listen->MCASTSources, source);
+        pico_tree_insert(s->MCASTListen, listen);
+        reference_count = 1;
+    }
+
+    pico_tree_insert(&MCASTSockets, s);
+    filter_mode = pico_socket_aggregate_mcastfilters((union pico_address *)&mcast_link->address, (union pico_address *)&mreq->mcast_group_addr);
+    if (filter_mode < 0)
+        return -1;
+
+    return pico_ipv4_mcast_join(&mreq->mcast_link_addr, &mreq->mcast_group_addr, (uint8_t)reference_count, (uint8_t)filter_mode, &MCASTFilter);
+}
+
+static int mcast_so_dropsrcm(struct pico_socket *s, void *value)
+{
+    int filter_mode = 0, reference_count = 0;
+    struct pico_ip_mreq_source *mreq = (struct pico_ip_mreq_source *)value;
+    struct pico_mcast_listen *listen;
+    union pico_address *source, stest;
+    struct pico_ipv4_link *mcast_link = setopt_multicast_check(s, value, 0, 1);
+    if (!mcast_link)
+        return -1;
+
+    memset(&stest, 0, sizeof(union pico_address));
+
+    listen = listen_find(s, (union pico_address *)&mreq->mcast_link_addr, (union pico_address *)&mreq->mcast_group_addr);
+    if (!listen) {
+        so_mcast_dbg("pico_socket_setoption: ERROR PICO_IP_DROP_SOURCE_MEMBERSHIP before PICO_IP_ADD_SOURCE_MEMBERSHIP\n");
+        pico_err = PICO_ERR_EADDRNOTAVAIL;
+        return -1;
+    } else {
+        if (listen->filter_mode != PICO_IP_MULTICAST_INCLUDE) {
+            so_mcast_dbg("pico_socket_setoption: ERROR source-specific multicast (include) on any-source multicast (exclude)\n");
+            pico_err = PICO_ERR_EINVAL;
+            return -1;
+        }
+
+        stest.ip4.addr = mreq->mcast_source_addr.addr;
+        source = pico_tree_findKey(&listen->MCASTSources, &stest);
+        if (!source) {
+            so_mcast_dbg("pico_socket_setoption: ERROR address to drop not in source list\n");
+            pico_err = PICO_ERR_EADDRNOTAVAIL;
+            return -1;
+        } else {
+            pico_tree_delete(&listen->MCASTSources, source);
+            if (pico_tree_empty(&listen->MCASTSources)) { /* 1 if empty, 0 otherwise */
+                reference_count = 1;
+                pico_tree_delete(s->MCASTListen, listen);
+                PICO_FREE(listen);
+                if (pico_tree_empty(s->MCASTListen)) {
+                    PICO_FREE(s->MCASTListen);
+                    s->MCASTListen = NULL;
+                    pico_tree_delete(&MCASTSockets, s);
+                }
+            }
+        }
+    }
+
+    filter_mode = pico_socket_aggregate_mcastfilters((union pico_address *)&mcast_link->address, (union pico_address *)&mreq->mcast_group_addr);
+    if (filter_mode < 0)
+        return -1;
+
+    return pico_ipv4_mcast_leave(&mreq->mcast_link_addr, &mreq->mcast_group_addr, (uint8_t)reference_count, (uint8_t)filter_mode, &MCASTFilter);
+}
+
+struct pico_setsockopt_mcast_call
+{
+    int option;
+    int (*call)(struct pico_socket *, void *);
+};
+
+static const struct pico_setsockopt_mcast_call mcast_so_calls[1 + PICO_IP_DROP_SOURCE_MEMBERSHIP - PICO_IP_MULTICAST_IF] =
+{
+    { PICO_IP_MULTICAST_IF,             NULL },
+    { PICO_IP_MULTICAST_TTL,            pico_udp_set_mc_ttl },
+    { PICO_IP_MULTICAST_LOOP,           mcast_so_loop },
+    { PICO_IP_ADD_MEMBERSHIP,           mcast_so_addm },
+    { PICO_IP_DROP_MEMBERSHIP,          mcast_so_dropm },
+    { PICO_IP_UNBLOCK_SOURCE,           mcast_so_unblock_src },
+    { PICO_IP_BLOCK_SOURCE,             mcast_so_block_src },
+    { PICO_IP_ADD_SOURCE_MEMBERSHIP,    mcast_so_addsrcm },
+    { PICO_IP_DROP_SOURCE_MEMBERSHIP,   mcast_so_dropsrcm }
+};
+
+
+static int mcast_so_check_socket(struct pico_socket *s)
+{
+    pico_err = PICO_ERR_EINVAL;
+    if (!s)
+        return -1;
+
+    if (!s->proto)
+        return -1;
+
+    if (s->proto->proto_number != PICO_PROTO_UDP)
+        return -1;
+
+    pico_err = PICO_ERR_NOERR;
+    return 0;
+}
+
+int pico_setsockopt_mcast(struct pico_socket *s, int option, void *value)
+{
+    int arrayn = option - PICO_IP_MULTICAST_IF;
+    if (option < PICO_IP_MULTICAST_IF || option > PICO_IP_DROP_SOURCE_MEMBERSHIP) {
+        pico_err = PICO_ERR_EOPNOTSUPP;
+        return -1;
+    }
+
+    if (mcast_so_check_socket(s) < 0)
+        return -1;
+
+    if (!mcast_so_calls[arrayn].call) {
+        pico_err = PICO_ERR_EOPNOTSUPP;
+        return -1;
+    }
+
+    return (mcast_so_calls[arrayn].call(s, value));
+}
+
+int pico_udp_set_mc_ttl(struct pico_socket *s, void  *_ttl)
+{
+    struct pico_socket_udp *u;
+    uint8_t ttl = *(uint8_t *)_ttl;
+    if(!s) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    u = (struct pico_socket_udp *) s;
+    u->mc_ttl = ttl;
+    return 0;
+}
+
+int pico_udp_get_mc_ttl(struct pico_socket *s, uint8_t *ttl)
+{
+    struct pico_socket_udp *u;
+    if(!s)
+        return -1;
+
+    u = (struct pico_socket_udp *) s;
+    *ttl = u->mc_ttl;
+    return 0;
+}
+#else
+int pico_udp_set_mc_ttl(struct pico_socket *s, void  *_ttl)
+{
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+    return -1;
+}
+
+int pico_udp_get_mc_ttl(struct pico_socket *s, uint8_t *ttl)
+{
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+    return -1;
+}
+
+int pico_socket_mcast_filter(struct pico_socket *s, union pico_address *mcast_group, union pico_address *src)
+{
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+    return -1;
+}
+
+void pico_multicast_delete(struct pico_socket *s)
+{
+    (void)s;
+}
+
+int pico_getsockopt_mcast(struct pico_socket *s, int option, void *value)
+{
+    (void)s;
+    (void)option;
+    (void)value;
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+    return -1;
+}
+
+int pico_setsockopt_mcast(struct pico_socket *s, int option, void *value)
+{
+    (void)s;
+    (void)option;
+    (void)value;
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+    return -1;
+
+}
+#endif /* PICO_SUPPORT_MCAST */
+
diff --git a/net/picotcp/stack/pico_stack.c b/net/picotcp/stack/pico_stack.c
new file mode 100644
index 0000000..b4fcdd6
--- /dev/null
+++ b/net/picotcp/stack/pico_stack.c
@@ -0,0 +1,1157 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   .
+
+   Authors: Daniele Lacamera
+ *********************************************************************/
+
+
+#include "pico_config.h"
+#include "pico_frame.h"
+#include "pico_device.h"
+#include "pico_protocol.h"
+#include "pico_stack.h"
+#include "pico_addressing.h"
+#include "pico_dns_client.h"
+
+#include "pico_olsr.h"
+#include "pico_aodv.h"
+#include "pico_eth.h"
+#include "pico_arp.h"
+#include "pico_ipv4.h"
+#include "pico_ipv6.h"
+#include "pico_icmp4.h"
+#include "pico_icmp6.h"
+#include "pico_igmp.h"
+#include "pico_udp.h"
+#include "pico_tcp.h"
+#include "pico_socket.h"
+#include "heap.h"
+
+#define IS_LIMITED_BCAST(f) (((struct pico_ipv4_hdr *) f->net_hdr)->dst.addr == PICO_IP4_BCAST)
+
+const uint8_t PICO_ETHADDR_ALL[6] = {
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+# define PICO_SIZE_MCAST 3
+const uint8_t PICO_ETHADDR_MCAST[6] = {
+    0x01, 0x00, 0x5e, 0x00, 0x00, 0x00
+};
+
+#ifdef PICO_SUPPORT_IPV6
+# define PICO_SIZE_MCAST6 2
+const uint8_t PICO_ETHADDR_MCAST6[6] = {
+    0x33, 0x33, 0x00, 0x00, 0x00, 0x00
+};
+#endif
+
+
+volatile pico_time pico_tick;
+volatile pico_err_t pico_err;
+
+static uint32_t _rand_seed;
+
+void WEAK pico_rand_feed(uint32_t feed)
+{
+    if (!feed)
+        return;
+
+    _rand_seed *= 1664525;
+    _rand_seed += 1013904223;
+    _rand_seed ^= ~(feed);
+}
+
+uint32_t WEAK pico_rand(void)
+{
+    pico_rand_feed((uint32_t)pico_tick);
+    return _rand_seed;
+}
+
+void pico_to_lowercase(char *str)
+{
+    int i = 0;
+    if (!str)
+        return;
+
+    while(str[i]) {
+        if ((str[i] <= 'Z') && (str[i] >= 'A'))
+            str[i] = (char) (str[i] - (char)('A' - 'a'));
+
+        i++;
+    }
+}
+
+/* NOTIFICATIONS: distributed notifications for stack internal errors.
+ */
+
+int pico_notify_socket_unreachable(struct pico_frame *f)
+{
+    if (0) {}
+
+#ifdef PICO_SUPPORT_ICMP4
+    else if (IS_IPV4(f)) {
+        pico_icmp4_port_unreachable(f);
+    }
+#endif
+#ifdef PICO_SUPPORT_ICMP6
+    else if (IS_IPV6(f)) {
+        pico_icmp6_port_unreachable(f);
+    }
+#endif
+
+    return 0;
+}
+
+int pico_notify_proto_unreachable(struct pico_frame *f)
+{
+    if (0) {}
+
+#ifdef PICO_SUPPORT_ICMP4
+    else if (IS_IPV4(f)) {
+        pico_icmp4_proto_unreachable(f);
+    }
+#endif
+#ifdef PICO_SUPPORT_ICMP6
+    else if (IS_IPV6(f)) {
+        pico_icmp6_proto_unreachable(f);
+    }
+#endif
+    return 0;
+}
+
+int pico_notify_dest_unreachable(struct pico_frame *f)
+{
+    if (0) {}
+
+#ifdef PICO_SUPPORT_ICMP4
+    else if (IS_IPV4(f)) {
+        pico_icmp4_dest_unreachable(f);
+    }
+#endif
+#ifdef PICO_SUPPORT_ICMP6
+    else if (IS_IPV6(f)) {
+        pico_icmp6_dest_unreachable(f);
+    }
+#endif
+    return 0;
+}
+
+int pico_notify_ttl_expired(struct pico_frame *f)
+{
+    if (0) {}
+
+#ifdef PICO_SUPPORT_ICMP4
+    else if (IS_IPV4(f)) {
+        pico_icmp4_ttl_expired(f);
+    }
+#endif
+#ifdef PICO_SUPPORT_ICMP6
+    else if (IS_IPV6(f)) {
+        pico_icmp6_ttl_expired(f);
+    }
+#endif
+    return 0;
+}
+
+int pico_notify_frag_expired(struct pico_frame *f)
+{
+    if (0) {}
+
+#ifdef PICO_SUPPORT_ICMP4
+    else if (IS_IPV4(f)) {
+        pico_icmp4_frag_expired(f);
+    }
+#endif
+#ifdef PICO_SUPPORT_ICMP6
+    else if (IS_IPV6(f)) {
+        pico_icmp6_frag_expired(f);
+    }
+#endif
+    return 0;
+}
+
+int pico_notify_pkt_too_big(struct pico_frame *f)
+{
+    if (0) {}
+
+#ifdef PICO_SUPPORT_ICMP4
+    else if (IS_IPV4(f)) {
+        pico_icmp4_mtu_exceeded(f);
+    }
+#endif
+#ifdef PICO_SUPPORT_ICMP6
+    else if (IS_IPV6(f)) {
+        pico_icmp6_pkt_too_big(f);
+    }
+#endif
+    return 0;
+}
+
+
+/* Transport layer */
+MOCKABLE int32_t pico_transport_receive(struct pico_frame *f, uint8_t proto)
+{
+    int32_t ret = -1;
+    switch (proto) {
+
+#ifdef PICO_SUPPORT_ICMP4
+    case PICO_PROTO_ICMP4:
+        ret = pico_enqueue(pico_proto_icmp4.q_in, f);
+        break;
+#endif
+
+#ifdef PICO_SUPPORT_ICMP6
+    case PICO_PROTO_ICMP6:
+        ret = pico_enqueue(pico_proto_icmp6.q_in, f);
+        break;
+#endif
+
+
+#ifdef PICO_SUPPORT_IGMP
+    case PICO_PROTO_IGMP:
+        ret = pico_enqueue(pico_proto_igmp.q_in, f);
+        break;
+#endif
+
+#ifdef PICO_SUPPORT_UDP
+    case PICO_PROTO_UDP:
+        ret = pico_enqueue(pico_proto_udp.q_in, f);
+        break;
+#endif
+
+#ifdef PICO_SUPPORT_TCP
+    case PICO_PROTO_TCP:
+        ret = pico_enqueue(pico_proto_tcp.q_in, f);
+        break;
+#endif
+
+    default:
+        /* Protocol not available */
+        dbg("pkt: no such protocol (%d)\n", proto);
+        pico_notify_proto_unreachable(f);
+        pico_frame_discard(f);
+        ret = -1;
+    }
+    return ret;
+}
+
+int32_t pico_network_receive(struct pico_frame *f)
+{
+    if (0) {}
+
+#ifdef PICO_SUPPORT_IPV4
+    else if (IS_IPV4(f)) {
+        pico_enqueue(pico_proto_ipv4.q_in, f);
+    }
+#endif
+#ifdef PICO_SUPPORT_IPV6
+    else if (IS_IPV6(f)) {
+        pico_enqueue(pico_proto_ipv6.q_in, f);
+    }
+#endif
+    else {
+        dbg("Network not found.\n");
+        pico_frame_discard(f);
+        return -1;
+    }
+    return (int32_t)f->buffer_len;
+}
+
+/* Network layer: interface towards socket for frame sending */
+int32_t pico_network_send(struct pico_frame *f)
+{
+    if (!f || !f->sock || !f->sock->net) {
+        pico_frame_discard(f);
+        return -1;
+    }
+
+    return f->sock->net->push(f->sock->net, f);
+}
+
+int pico_source_is_local(struct pico_frame *f)
+{
+    if (0) { }
+
+#ifdef PICO_SUPPORT_IPV4
+    else if (IS_IPV4(f)) {
+        struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *)f->net_hdr;
+        if (hdr->src.addr == PICO_IPV4_INADDR_ANY)
+            return 1;
+
+        if (pico_ipv4_link_find(&hdr->src))
+            return 1;
+    }
+#endif
+#ifdef PICO_SUPPORT_IPV6
+    else if (IS_IPV6(f)) {
+        struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)f->net_hdr;
+        if (pico_ipv6_is_unspecified(hdr->src.addr) || pico_ipv6_link_find(&hdr->src))
+            return 1;
+    }
+#endif
+    return 0;
+}
+
+#ifdef PICO_SUPPORT_ETH
+/* DATALINK LEVEL: interface from network to the device
+ * and vice versa.
+ */
+
+/* The pico_ethernet_receive() function is used by
+ * those devices supporting ETH in order to push packets up
+ * into the stack.
+ */
+
+static int destination_is_bcast(struct pico_frame *f)
+{
+    if (!f)
+        return 0;
+
+    if (IS_IPV6(f))
+        return 0;
+
+#ifdef PICO_SUPPORT_IPV4
+    else {
+        struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+        return pico_ipv4_is_broadcast(hdr->dst.addr);
+    }
+#else
+    return 0;
+#endif
+}
+
+static int destination_is_mcast(struct pico_frame *f)
+{
+    int ret = 0;
+    if (!f)
+        return 0;
+
+#ifdef PICO_SUPPORT_IPV6
+    if (IS_IPV6(f)) {
+        struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *) f->net_hdr;
+        ret = pico_ipv6_is_multicast(hdr->dst.addr);
+    }
+
+#endif
+#ifdef PICO_SUPPORT_IPV4
+    else {
+        struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+        ret = pico_ipv4_is_multicast(hdr->dst.addr);
+    }
+#endif
+
+    return ret;
+}
+
+#ifdef PICO_SUPPORT_IPV4
+static int32_t pico_ipv4_ethernet_receive(struct pico_frame *f)
+{
+    if (IS_IPV4(f)) {
+        pico_enqueue(pico_proto_ipv4.q_in, f);
+    } else {
+        (void)pico_icmp4_param_problem(f, 0);
+        pico_frame_discard(f);
+        return -1;
+    }
+
+    return (int32_t)f->buffer_len;
+}
+#endif
+
+#ifdef PICO_SUPPORT_IPV6
+static int32_t pico_ipv6_ethernet_receive(struct pico_frame *f)
+{
+    if (IS_IPV6(f)) {
+        pico_enqueue(pico_proto_ipv6.q_in, f);
+    } else {
+        /* Wrong version for link layer type */
+        pico_frame_discard(f);
+        return -1;
+    }
+
+    return (int32_t)f->buffer_len;
+}
+#endif
+
+static int32_t pico_ll_receive(struct pico_frame *f)
+{
+    struct pico_eth_hdr *hdr = (struct pico_eth_hdr *) f->datalink_hdr;
+    f->net_hdr = f->datalink_hdr + sizeof(struct pico_eth_hdr);
+
+#if (defined PICO_SUPPORT_IPV4) && (defined PICO_SUPPORT_ETH)
+    if (hdr->proto == PICO_IDETH_ARP)
+        return pico_arp_receive(f);
+
+#endif
+
+#if defined (PICO_SUPPORT_IPV4)
+    if (hdr->proto == PICO_IDETH_IPV4)
+        return pico_ipv4_ethernet_receive(f);
+
+#endif
+
+#if defined (PICO_SUPPORT_IPV6)
+    if (hdr->proto == PICO_IDETH_IPV6)
+        return pico_ipv6_ethernet_receive(f);
+
+#endif
+
+    pico_frame_discard(f);
+    return -1;
+}
+
+static void pico_ll_check_bcast(struct pico_frame *f)
+{
+    struct pico_eth_hdr *hdr = (struct pico_eth_hdr *) f->datalink_hdr;
+    /* Indicate a link layer broadcast packet */
+    if (memcmp(hdr->daddr, PICO_ETHADDR_ALL, PICO_SIZE_ETH) == 0)
+        f->flags |= PICO_FRAME_FLAG_BCAST;
+}
+
+int32_t pico_ethernet_receive(struct pico_frame *f)
+{
+    struct pico_eth_hdr *hdr;
+    if (!f || !f->dev || !f->datalink_hdr)
+    {
+        pico_frame_discard(f);
+        return -1;
+    }
+
+    hdr = (struct pico_eth_hdr *) f->datalink_hdr;
+    if ((memcmp(hdr->daddr, f->dev->eth->mac.addr, PICO_SIZE_ETH) != 0) &&
+        (memcmp(hdr->daddr, PICO_ETHADDR_MCAST, PICO_SIZE_MCAST) != 0) &&
+#ifdef PICO_SUPPORT_IPV6
+        (memcmp(hdr->daddr, PICO_ETHADDR_MCAST6, PICO_SIZE_MCAST6) != 0) &&
+#endif
+        (memcmp(hdr->daddr, PICO_ETHADDR_ALL, PICO_SIZE_ETH) != 0))
+    {
+        pico_frame_discard(f);
+        return -1;
+    }
+
+    pico_ll_check_bcast(f);
+    return pico_ll_receive(f);
+}
+
+static struct pico_eth *pico_ethernet_mcast_translate(struct pico_frame *f, uint8_t *pico_mcast_mac)
+{
+    struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+
+    /* place 23 lower bits of IP in lower 23 bits of MAC */
+    pico_mcast_mac[5] = (long_be(hdr->dst.addr) & 0x000000FFu);
+    pico_mcast_mac[4] = (uint8_t)((long_be(hdr->dst.addr) & 0x0000FF00u) >> 8u);
+    pico_mcast_mac[3] = (uint8_t)((long_be(hdr->dst.addr) & 0x007F0000u) >> 16u);
+
+    return (struct pico_eth *)pico_mcast_mac;
+}
+
+
+#ifdef PICO_SUPPORT_IPV6
+static struct pico_eth *pico_ethernet_mcast6_translate(struct pico_frame *f, uint8_t *pico_mcast6_mac)
+{
+    struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)f->net_hdr;
+
+    /* first 2 octets are 0x33, last four are the last four of dst */
+    pico_mcast6_mac[5] = hdr->dst.addr[PICO_SIZE_IP6 - 1];
+    pico_mcast6_mac[4] = hdr->dst.addr[PICO_SIZE_IP6 - 2];
+    pico_mcast6_mac[3] = hdr->dst.addr[PICO_SIZE_IP6 - 3];
+    pico_mcast6_mac[2] = hdr->dst.addr[PICO_SIZE_IP6 - 4];
+
+    return (struct pico_eth *)pico_mcast6_mac;
+}
+#endif
+
+static int pico_ethernet_ipv6_dst(struct pico_frame *f, struct pico_eth *const dstmac)
+{
+    int retval = -1;
+    if (!dstmac)
+        return -1;
+
+    #ifdef PICO_SUPPORT_IPV6
+    if (destination_is_mcast(f)) {
+        uint8_t pico_mcast6_mac[6] = {
+            0x33, 0x33, 0x00, 0x00, 0x00, 0x00
+        };
+        pico_ethernet_mcast6_translate(f, pico_mcast6_mac);
+        memcpy(dstmac, pico_mcast6_mac, PICO_SIZE_ETH);
+        retval = 0;
+    } else {
+        struct pico_eth *neighbor = pico_ipv6_get_neighbor(f);
+        if (neighbor)
+        {
+            memcpy(dstmac, neighbor, PICO_SIZE_ETH);
+            retval = 0;
+        }
+    }
+
+    #else
+    (void)f;
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+    #endif
+    return retval;
+}
+
+
+/* Ethernet send, first attempt: try our own address.
+ * Returns 0 if the packet is not for us.
+ * Returns 1 if the packet is cloned to our own receive queue, so the caller can discard the original frame.
+ * */
+static int32_t pico_ethsend_local(struct pico_frame *f, struct pico_eth_hdr *hdr)
+{
+    if (!hdr)
+        return 0;
+
+    /* Check own mac */
+    if(!memcmp(hdr->daddr, hdr->saddr, PICO_SIZE_ETH)) {
+        struct pico_frame *clone = pico_frame_copy(f);
+        dbg("sending out packet destined for our own mac\n");
+        (void)pico_ethernet_receive(clone);
+        return 1;
+    }
+
+    return 0;
+}
+
+/* Ethernet send, second attempt: try bcast.
+ * Returns 0 if the packet is not bcast, so it will be handled somewhere else.
+ * Returns 1 if the packet is handled by the pico_device_broadcast() function, so it can be discarded.
+ * */
+static int32_t pico_ethsend_bcast(struct pico_frame *f)
+{
+    if (IS_LIMITED_BCAST(f)) {
+        (void)pico_device_broadcast(f); /* We can discard broadcast even if it's not sent. */
+        return 1;
+    }
+
+    return 0;
+}
+
+/* Ethernet send, third attempt: try unicast.
+ * If the device driver is busy, we return 0, so the stack won't discard the frame.
+ * In case of success, we can safely return 1.
+ */
+static int32_t pico_ethsend_dispatch(struct pico_frame *f)
+{
+    int ret = f->dev->send(f->dev, f->start, (int) f->len);
+    if (ret <= 0)
+        return 0; /* Failure to deliver! */
+    else {
+        return 1; /* Frame is in flight by now. */
+    }
+}
+
+
+
+
+/* This function looks for the destination mac address
+ * in order to send the frame being processed.
+ */
+
+int32_t MOCKABLE pico_ethernet_send(struct pico_frame *f)
+{
+    struct pico_eth dstmac;
+    uint8_t dstmac_valid = 0;
+    uint16_t proto = PICO_IDETH_IPV4;
+
+#ifdef PICO_SUPPORT_IPV6
+    /* Step 1: If the frame has an IPv6 packet,
+     * destination address is taken from the ND tables
+     */
+    if (IS_IPV6(f)) {
+        if (pico_ethernet_ipv6_dst(f, &dstmac) < 0)
+        {
+            pico_ipv6_nd_postpone(f);
+            return 0; /* I don't care if frame was actually postponed. If there is no room in the ND table, discard safely. */
+        }
+
+        dstmac_valid = 1;
+        proto = PICO_IDETH_IPV6;
+    }
+    else
+#endif
+
+    /* In case of broadcast (IPV4 only), dst mac is FF:FF:... */
+    if (IS_BCAST(f) || destination_is_bcast(f))
+    {
+        memcpy(&dstmac, PICO_ETHADDR_ALL, PICO_SIZE_ETH);
+        dstmac_valid = 1;
+    }
+
+    /* In case of multicast, dst mac is translated from the group address */
+    else if (destination_is_mcast(f)) {
+        uint8_t pico_mcast_mac[6] = {
+            0x01, 0x00, 0x5e, 0x00, 0x00, 0x00
+        };
+        pico_ethernet_mcast_translate(f, pico_mcast_mac);
+        memcpy(&dstmac, pico_mcast_mac, PICO_SIZE_ETH);
+        dstmac_valid = 1;
+    }
+
+#if (defined PICO_SUPPORT_IPV4)
+    else {
+        struct pico_eth *arp_get;
+        arp_get = pico_arp_get(f);
+        if (arp_get) {
+            memcpy(&dstmac, arp_get, PICO_SIZE_ETH);
+            dstmac_valid = 1;
+        } else {
+            /* At this point, ARP will discard the frame in any case.
+             * It is safe to return without discarding.
+             */
+            pico_arp_postpone(f);
+            return 0;
+            /* Same case as for IPv6 ... */
+        }
+
+    }
+#endif
+
+    /* This sets destination and source address, then pushes the packet to the device. */
+    if (dstmac_valid) {
+        struct pico_eth_hdr *hdr;
+        hdr = (struct pico_eth_hdr *) f->datalink_hdr;
+        if ((f->start > f->buffer) && ((f->start - f->buffer) >= PICO_SIZE_ETHHDR))
+        {
+            f->start -= PICO_SIZE_ETHHDR;
+            f->len += PICO_SIZE_ETHHDR;
+            f->datalink_hdr = f->start;
+            hdr = (struct pico_eth_hdr *) f->datalink_hdr;
+            memcpy(hdr->saddr, f->dev->eth->mac.addr, PICO_SIZE_ETH);
+            memcpy(hdr->daddr, &dstmac, PICO_SIZE_ETH);
+            hdr->proto = proto;
+        }
+
+        if (pico_ethsend_local(f, hdr) || pico_ethsend_bcast(f) || pico_ethsend_dispatch(f)) {
+            /* one of the above functions has delivered the frame accordingly. (returned != 0)
+             * It is safe to directly return success.
+             * */
+            return 0;
+        }
+    }
+
+    /* Failure: do not dequeue the frame, keep it for later. */
+    return -1;
+}
+
+#endif /* PICO_SUPPORT_ETH */
+
+
+void pico_store_network_origin(void *src, struct pico_frame *f)
+{
+  #ifdef PICO_SUPPORT_IPV4
+    struct pico_ip4 *ip4;
+  #endif
+
+  #ifdef PICO_SUPPORT_IPV6
+    struct pico_ip6 *ip6;
+  #endif
+
+  #ifdef PICO_SUPPORT_IPV4
+    if (IS_IPV4(f)) {
+        struct pico_ipv4_hdr *hdr;
+        hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+        ip4 = (struct pico_ip4 *) src;
+        ip4->addr = hdr->src.addr;
+    }
+
+  #endif
+  #ifdef PICO_SUPPORT_IPV6
+    if (IS_IPV6(f)) {
+        struct pico_ipv6_hdr *hdr;
+        hdr = (struct pico_ipv6_hdr *) f->net_hdr;
+        ip6 = (struct pico_ip6 *) src;
+        memcpy(ip6->addr, hdr->src.addr, PICO_SIZE_IP6);
+    }
+
+  #endif
+}
+
+int pico_address_compare(union pico_address *a, union pico_address *b, uint16_t proto)
+{
+    #ifdef PICO_SUPPORT_IPV6
+    if (proto == PICO_PROTO_IPV6) {
+        return pico_ipv6_compare(&a->ip6, &b->ip6);
+    }
+
+    #endif
+    #ifdef PICO_SUPPORT_IPV4
+    if (proto == PICO_PROTO_IPV4) {
+        return pico_ipv4_compare(&a->ip4, &b->ip4);
+    }
+
+    #endif
+    return 0;
+
+}
+
+int pico_frame_dst_is_unicast(struct pico_frame *f)
+{
+    if (0) {
+        return 0;
+    }
+
+#ifdef PICO_SUPPORT_IPV4
+    if (IS_IPV4(f)) {
+        struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *)f->net_hdr;
+        if (pico_ipv4_is_multicast(hdr->dst.addr) || pico_ipv4_is_broadcast(hdr->dst.addr))
+            return 0;
+
+        return 1;
+    }
+
+#endif
+
+#ifdef PICO_SUPPORT_IPV6
+    if (IS_IPV6(f)) {
+        struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)f->net_hdr;
+        if (pico_ipv6_is_multicast(hdr->dst.addr) || pico_ipv6_is_unspecified(hdr->dst.addr))
+            return 0;
+
+        return 1;
+    }
+
+#endif
+    else return 0;
+}
+
+
+/* LOWEST LEVEL: interface towards devices. */
+/* Device driver will call this function which returns immediately.
+ * Incoming packet will be processed later on in the dev loop.
+ */
+int32_t pico_stack_recv(struct pico_device *dev, uint8_t *buffer, uint32_t len)
+{
+    struct pico_frame *f;
+    int32_t ret;
+    if (len <= 0)
+        return -1;
+
+    f = pico_frame_alloc(len);
+    if (!f)
+    {
+        dbg("Cannot alloc incoming frame!\n");
+        return -1;
+    }
+
+    /* Association to the device that just received the frame. */
+    f->dev = dev;
+
+    /* Setup the start pointer, length. */
+    f->start = f->buffer;
+    f->len = f->buffer_len;
+    if (f->len > 8) {
+        uint32_t rand, mid_frame = (f->buffer_len >> 2) << 1;
+        mid_frame -= (mid_frame % 4);
+        memcpy(&rand, f->buffer + mid_frame, sizeof(uint32_t));
+        pico_rand_feed(rand);
+    }
+
+    memcpy(f->buffer, buffer, len);
+    ret = pico_enqueue(dev->q_in, f);
+    if (ret <= 0) {
+        pico_frame_discard(f);
+    }
+
+    return ret;
+}
+
+static int32_t _pico_stack_recv_zerocopy(struct pico_device *dev, uint8_t *buffer, uint32_t len, int ext_buffer, void (*notify_free)(uint8_t *))
+{
+    struct pico_frame *f;
+    int ret;
+    if (len <= 0)
+        return -1;
+
+    f = pico_frame_alloc_skeleton(len, ext_buffer);
+    if (!f)
+    {
+        dbg("Cannot alloc incoming frame!\n");
+        return -1;
+    }
+
+    if (pico_frame_skeleton_set_buffer(f, buffer) < 0)
+    {
+        dbg("Invalid zero-copy buffer!\n");
+        PICO_FREE(f->usage_count);
+        PICO_FREE(f);
+        return -1;
+    }
+
+    if (notify_free) {
+        f->notify_free = notify_free;
+    }
+
+    f->dev = dev;
+    ret = pico_enqueue(dev->q_in, f);
+    if (ret <= 0) {
+        pico_frame_discard(f);
+    }
+
+    return ret;
+}
+
+int32_t pico_stack_recv_zerocopy(struct pico_device *dev, uint8_t *buffer, uint32_t len)
+{
+    return _pico_stack_recv_zerocopy(dev, buffer, len, 0, NULL);
+}
+
+int32_t pico_stack_recv_zerocopy_ext_buffer(struct pico_device *dev, uint8_t *buffer, uint32_t len)
+{
+    return _pico_stack_recv_zerocopy(dev, buffer, len, 1, NULL);
+}
+
+int32_t pico_stack_recv_zerocopy_ext_buffer_notify(struct pico_device *dev, uint8_t *buffer, uint32_t len, void (*notify_free)(uint8_t *buffer))
+{
+    return _pico_stack_recv_zerocopy(dev, buffer, len, 1, notify_free);
+}
+
+int32_t pico_sendto_dev(struct pico_frame *f)
+{
+    if (!f->dev) {
+        pico_frame_discard(f);
+        return -1;
+    } else {
+        if (f->len > 8) {
+            uint32_t rand, mid_frame = (f->buffer_len >> 2) << 1;
+            mid_frame -= (mid_frame % 4);
+            memcpy(&rand, f->buffer + mid_frame, sizeof(uint32_t));
+            pico_rand_feed(rand);
+        }
+
+        return pico_enqueue(f->dev->q_out, f);
+    }
+}
+
+struct pico_timer
+{
+    void *arg;
+    void (*timer)(pico_time timestamp, void *arg);
+};
+
+struct pico_timer_ref
+{
+    pico_time expire;
+    struct pico_timer *tmr;
+};
+
+typedef struct pico_timer_ref pico_timer_ref;
+
+DECLARE_HEAP(pico_timer_ref, expire);
+
+static heap_pico_timer_ref *Timers;
+
+int32_t pico_seq_compare(uint32_t a, uint32_t b)
+{
+    uint32_t thresh = ((uint32_t)(-1)) >> 1;
+
+    if (a > b) /* return positive number, if not wrapped */
+    {
+        if ((a - b) > thresh) /* b wrapped */
+            return -(int32_t)(b - a); /* b = very small,     a = very big      */
+        else
+            return (int32_t)(a - b); /* a = biggest,        b = a bit smaller */
+
+    }
+
+    if (a < b) /* return negative number, if not wrapped */
+    {
+        if ((b - a) > thresh) /* a wrapped */
+            return (int32_t)(a - b); /* a = very small,     b = very big      */
+        else
+            return -(int32_t)(b - a); /* b = biggest,        a = a bit smaller */
+
+    }
+
+    return 0;
+}
+
+static void pico_check_timers(void)
+{
+    struct pico_timer *t;
+    struct pico_timer_ref tref_unused, *tref = heap_first(Timers);
+    pico_tick = PICO_TIME_MS();
+    while((tref) && (tref->expire < pico_tick)) {
+        t = tref->tmr;
+        if (t && t->timer)
+            t->timer(pico_tick, t->arg);
+
+        if (t)
+        {
+            PICO_FREE(t);
+        }
+
+        t = NULL;
+        heap_peek(Timers, &tref_unused);
+        tref = heap_first(Timers);
+    }
+}
+
+void MOCKABLE pico_timer_cancel(struct pico_timer *t)
+{
+    uint32_t i;
+    struct pico_timer_ref *tref = Timers->top;
+    if (!t)
+        return;
+
+    for (i = 1; i <= Timers->n; i++) {
+        if (tref[i].tmr == t) {
+            Timers->top[i].tmr = NULL;
+            PICO_FREE(t);
+            break;
+        }
+    }
+}
+
+#define PROTO_DEF_NR      11
+#define PROTO_DEF_AVG_NR  4
+#define PROTO_DEF_SCORE   32
+#define PROTO_MIN_SCORE   32
+#define PROTO_MAX_SCORE   128
+#define PROTO_LAT_IND     3   /* latency indication 0-3 (lower is better latency performance), x1, x2, x4, x8 */
+#define PROTO_MAX_LOOP    (PROTO_MAX_SCORE << PROTO_LAT_IND) /* max global loop score, so per tick */
+
+static int calc_score(int *score, int *index, int avg[][PROTO_DEF_AVG_NR], int *ret)
+{
+    int temp, i, j, sum;
+    int max_total = PROTO_MAX_LOOP, total = 0;
+
+    /* dbg("USED SCORES> "); */
+
+    for (i = 0; i < PROTO_DEF_NR; i++) {
+
+        /* if used looped score */
+        if (ret[i] < score[i]) {
+            temp = score[i] - ret[i]; /* remaining loop score */
+
+            /* dbg("%3d - ",temp); */
+
+            if (index[i] >= PROTO_DEF_AVG_NR)
+                index[i] = 0;   /* reset index */
+
+            j = index[i];
+            avg[i][j] = temp;
+
+            index[i]++;
+
+            if (ret[i] == 0 && ((score[i] * 2) <= PROTO_MAX_SCORE) && ((total + (score[i] * 2)) < max_total)) { /* used all loop score -> increase next score directly */
+                score[i] *= 2;
+                total += score[i];
+                continue;
+            }
+
+            sum = 0;
+            for (j = 0; j < PROTO_DEF_AVG_NR; j++)
+                sum += avg[i][j]; /* calculate sum */
+
+            sum /= 4;           /* divide by 4 to get average used score */
+
+            /* criterion to increase next loop score */
+            if (sum > (score[i] - (score[i] / 4))  && ((score[i] * 2) <= PROTO_MAX_SCORE) && ((total + (score[i] / 2)) < max_total)) { /* > 3/4 */
+                score[i] *= 2; /* double loop score */
+                total += score[i];
+                continue;
+            }
+
+            /* criterion to decrease next loop score */
+            if ((sum < (score[i] / 4)) && ((score[i] / 2) >= PROTO_MIN_SCORE)) { /* < 1/4 */
+                score[i] /= 2; /* half loop score */
+                total += score[i];
+                continue;
+            }
+
+            /* also add non-changed scores */
+            total += score[i];
+        }
+        else if (ret[i] == score[i]) {
+            /* no used loop score - gradually decrease */
+
+            /*  dbg("%3d - ",0); */
+
+            if (index[i] >= PROTO_DEF_AVG_NR)
+                index[i] = 0;   /* reset index */
+
+            j = index[i];
+            avg[i][j] = 0;
+
+            index[i]++;
+
+            sum = 0;
+            for (j = 0; j < PROTO_DEF_AVG_NR; j++)
+                sum += avg[i][j]; /* calculate sum */
+
+            sum /= 2;          /* divide by 4 to get average used score */
+
+            if ((sum == 0) && ((score[i] / 2) >= PROTO_MIN_SCORE)) {
+                score[i] /= 2; /* half loop score */
+                total += score[i];
+                for (j = 0; j < PROTO_DEF_AVG_NR; j++)
+                    avg[i][j] = score[i];
+            }
+
+        }
+    }
+    /* dbg("\n"); */
+
+    return 0;
+}
+
+void pico_stack_tick(void)
+{
+    static int score[PROTO_DEF_NR] = {
+        PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE
+    };
+    static int index[PROTO_DEF_NR] = {
+        0, 0, 0, 0, 0, 0
+    };
+    static int avg[PROTO_DEF_NR][PROTO_DEF_AVG_NR];
+    static int ret[PROTO_DEF_NR] = {
+        0
+    };
+
+    pico_check_timers();
+
+    /* dbg("LOOP_SCORES> %3d - %3d - %3d - %3d - %3d - %3d - %3d - %3d - %3d - %3d - %3d\n",score[0],score[1],score[2],score[3],score[4],score[5],score[6],score[7],score[8],score[9],score[10]); */
+
+    /* score = pico_protocols_loop(100); */
+
+    ret[0] = pico_devices_loop(score[0], PICO_LOOP_DIR_IN);
+    pico_rand_feed((uint32_t)ret[0]);
+
+    ret[1] = pico_protocol_datalink_loop(score[1], PICO_LOOP_DIR_IN);
+    pico_rand_feed((uint32_t)ret[1]);
+
+    ret[2] = pico_protocol_network_loop(score[2], PICO_LOOP_DIR_IN);
+    pico_rand_feed((uint32_t)ret[2]);
+
+    ret[3] = pico_protocol_transport_loop(score[3], PICO_LOOP_DIR_IN);
+    pico_rand_feed((uint32_t)ret[3]);
+
+
+    ret[5] = score[5];
+#if defined (PICO_SUPPORT_IPV4) || defined (PICO_SUPPORT_IPV6)
+#if defined (PICO_SUPPORT_TCP) || defined (PICO_SUPPORT_UDP)
+    ret[5] = pico_sockets_loop(score[5]); /* swapped */
+    pico_rand_feed((uint32_t)ret[5]);
+#endif
+#endif
+
+    ret[4] = pico_protocol_socket_loop(score[4], PICO_LOOP_DIR_IN);
+    pico_rand_feed((uint32_t)ret[4]);
+
+
+    ret[6] = pico_protocol_socket_loop(score[6], PICO_LOOP_DIR_OUT);
+    pico_rand_feed((uint32_t)ret[6]);
+
+    ret[7] = pico_protocol_transport_loop(score[7], PICO_LOOP_DIR_OUT);
+    pico_rand_feed((uint32_t)ret[7]);
+
+    ret[8] = pico_protocol_network_loop(score[8], PICO_LOOP_DIR_OUT);
+    pico_rand_feed((uint32_t)ret[8]);
+
+    ret[9] = pico_protocol_datalink_loop(score[9], PICO_LOOP_DIR_OUT);
+    pico_rand_feed((uint32_t)ret[9]);
+
+    ret[10] = pico_devices_loop(score[10], PICO_LOOP_DIR_OUT);
+    pico_rand_feed((uint32_t)ret[10]);
+
+    /* calculate new loop scores for next iteration */
+    calc_score(score, index, (int (*)[])avg, ret);
+}
+
+void pico_stack_loop(void)
+{
+    while(1) {
+        pico_stack_tick();
+        PICO_IDLE();
+    }
+}
+
+MOCKABLE struct pico_timer *pico_timer_add(pico_time expire, void (*timer)(pico_time, void *), void *arg)
+{
+    struct pico_timer *t = PICO_ZALLOC(sizeof(struct pico_timer));
+    struct pico_timer_ref tref;
+    if (!t) {
+        pico_err = PICO_ERR_ENOMEM;
+        return NULL;
+    }
+
+    tref.expire = PICO_TIME_MS() + expire;
+    t->arg = arg;
+    t->timer = timer;
+    tref.tmr = t;
+    heap_insert(Timers, &tref);
+    if (Timers->n > PICO_MAX_TIMERS) {
+        dbg("Warning: I have %d timers\n", (int)Timers->n);
+    }
+
+    return t;
+}
+
+int pico_stack_init(void)
+{
+
+#ifdef PICO_SUPPORT_IPV4
+    pico_protocol_init(&pico_proto_ipv4);
+#endif
+
+#ifdef PICO_SUPPORT_IPV6
+    pico_protocol_init(&pico_proto_ipv6);
+#endif
+
+#ifdef PICO_SUPPORT_ICMP4
+    pico_protocol_init(&pico_proto_icmp4);
+#endif
+
+#ifdef PICO_SUPPORT_ICMP6
+    pico_protocol_init(&pico_proto_icmp6);
+#endif
+
+#ifdef PICO_SUPPORT_IGMP
+    pico_protocol_init(&pico_proto_igmp);
+#endif
+
+#ifdef PICO_SUPPORT_UDP
+    pico_protocol_init(&pico_proto_udp);
+#endif
+
+#ifdef PICO_SUPPORT_TCP
+    pico_protocol_init(&pico_proto_tcp);
+#endif
+
+#ifdef PICO_SUPPORT_DNS_CLIENT
+    pico_dns_client_init();
+#endif
+
+    pico_rand_feed(123456);
+
+    /* Initialize timer heap */
+    Timers = heap_init();
+    if (!Timers)
+        return -1;
+
+#if ((defined PICO_SUPPORT_IPV4) && (defined PICO_SUPPORT_ETH))
+    /* Initialize ARP module */
+    pico_arp_init();
+#endif
+
+#ifdef PICO_SUPPORT_IPV6
+    /* Initialize Neighbor discovery module */
+    pico_ipv6_nd_init();
+#endif
+
+#ifdef PICO_SUPPORT_OLSR
+    pico_olsr_init();
+#endif
+#ifdef PICO_SUPPORT_AODV
+    pico_aodv_init();
+#endif
+
+    pico_stack_tick();
+    pico_stack_tick();
+    pico_stack_tick();
+    return 0;
+}
+
diff --git a/net/picotcp/stack/pico_tree.c b/net/picotcp/stack/pico_tree.c
new file mode 100644
index 0000000..76eaf2f
--- /dev/null
+++ b/net/picotcp/stack/pico_tree.c
@@ -0,0 +1,575 @@
+/*********************************************************************
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
+   See LICENSE and COPYING for usage.
+
+   Author: Andrei Carp <andrei.carp at tass.be>
+ *********************************************************************/
+
+#include "pico_tree.h"
+#include "pico_config.h"
+#include "pico_protocol.h"
+#include "pico_mm.h"
+
+#define RED     0
+#define BLACK 1
+
+/* By default the null leafs are black */
+struct pico_tree_node LEAF = {
+    NULL, /* key */
+    &LEAF, &LEAF, &LEAF, /* parent, left,right */
+    BLACK, /* color */
+};
+
+#define IS_LEAF(x) (x == &LEAF)
+#define IS_NOT_LEAF(x) (x != &LEAF)
+#define INIT_LEAF (&LEAF)
+
+#define AM_I_LEFT_CHILD(x) (x == x->parent->leftChild)
+#define AM_I_RIGHT_CHILD(x) (x == x->parent->rightChild)
+
+#define PARENT(x) (x->parent)
+#define GRANPA(x) (x->parent->parent)
+
+/*
+ * Local Functions
+ */
+static struct pico_tree_node *create_node(struct pico_tree *tree, void *key, uint8_t allocator);
+static void rotateToLeft(struct pico_tree*tree, struct pico_tree_node*node);
+static void rotateToRight(struct pico_tree*root, struct pico_tree_node*node);
+static void fix_insert_collisions(struct pico_tree*tree, struct pico_tree_node*node);
+static void fix_delete_collisions(struct pico_tree*tree, struct pico_tree_node *node);
+static void switchNodes(struct pico_tree*tree, struct pico_tree_node*nodeA, struct pico_tree_node*nodeB);
+void *pico_tree_insert_implementation(struct pico_tree *tree, void *key, uint8_t allocator);
+void *pico_tree_delete_implementation(struct pico_tree *tree, void *key, uint8_t allocator);
+
+#ifdef PICO_SUPPORT_MM
+/* The memory manager also uses the pico_tree to keep track of all the different slab sizes it has.
+ * These nodes should be placed in the manager page which is in a different memory region then the nodes
+ * which are used for the pico stack in general.
+ * Therefore the following 2 functions are created so that pico_tree can use them to to put these nodes
+ * into the correct memory regions.
+ * If pico_tree_insert is called from the memory manager module, then create_node should use
+ * pico_mem_page0_zalloc to create a node. The same for pico_tree_delete.
+ */
+extern void*pico_mem_page0_zalloc(size_t len);
+extern void pico_mem_page0_free(void*ptr);
+#endif  /* PICO_SUPPORT_MM */
+
+/*
+ * Exported functions
+ */
+
+struct pico_tree_node *pico_tree_firstNode(struct pico_tree_node *node)
+{
+    while(IS_NOT_LEAF(node->leftChild))
+        node = node->leftChild;
+    return node;
+}
+
+struct pico_tree_node *pico_tree_lastNode(struct pico_tree_node *node)
+{
+    while(IS_NOT_LEAF(node->rightChild))
+        node = node->rightChild;
+    return node;
+}
+
+struct pico_tree_node *pico_tree_next(struct pico_tree_node *node)
+{
+    if(IS_NOT_LEAF(node->rightChild))
+    {
+        node = node->rightChild;
+        while(IS_NOT_LEAF(node->leftChild))
+            node = node->leftChild;
+    }
+    else
+    {
+        if (IS_NOT_LEAF(node->parent) &&  AM_I_LEFT_CHILD(node))
+            node = node->parent;
+        else {
+            while (IS_NOT_LEAF(node->parent) && AM_I_RIGHT_CHILD(node))
+                node = node->parent;
+            node = node->parent;
+        }
+    }
+
+    return node;
+}
+
+struct pico_tree_node *pico_tree_prev(struct pico_tree_node *node)
+{
+    if (IS_NOT_LEAF(node->leftChild)) {
+        node = node->leftChild;
+        while (IS_NOT_LEAF(node->rightChild))
+            node = node->rightChild;
+    } else {
+        if (IS_NOT_LEAF(node->parent) && AM_I_RIGHT_CHILD(node))
+            node = node->parent;
+        else {
+            while (IS_NOT_LEAF(node) && AM_I_LEFT_CHILD(node))
+                node = node->parent;
+            node = node->parent;
+        }
+    }
+
+    return node;
+}
+
+/* The memory manager also uses the pico_tree to keep track of all the different slab sizes it has.
+ * These nodes should be placed in the manager page which is in a different memory region then the nodes
+ * which are used for the pico stack in general.
+ * Therefore the following wrapper for pico_tree_insert is created.
+ * The actual implementation can be found in pico_tree_insert_implementation.
+ */
+void *pico_tree_insert(struct pico_tree *tree, void *key)
+{
+    return pico_tree_insert_implementation(tree, key, USE_PICO_ZALLOC);
+}
+
+void *pico_tree_insert_implementation(struct pico_tree*tree, void *key, uint8_t allocator)
+{
+    struct pico_tree_node *last_node = INIT_LEAF;
+    struct pico_tree_node *temp = tree->root;
+    struct pico_tree_node *insert;
+    void *LocalKey;
+    int result = 0;
+
+    LocalKey = (IS_NOT_LEAF(tree->root) ? pico_tree_findKey(tree, key) : NULL);
+    /* if node already in, bail out */
+    if(LocalKey)
+        return LocalKey;
+    else
+    {
+        if(allocator == USE_PICO_PAGE0_ZALLOC)
+            insert = create_node(tree, key, USE_PICO_PAGE0_ZALLOC);
+        else
+            insert = create_node(tree, key, USE_PICO_ZALLOC);
+
+        if(!insert)
+        {
+            pico_err = PICO_ERR_ENOMEM;
+            /* to let the user know that it couldn't insert */
+            return (void *)&LEAF;
+        }
+    }
+
+    /* search for the place to insert the new node */
+    while(IS_NOT_LEAF(temp))
+    {
+        last_node = temp;
+        result = tree->compare(insert->keyValue, temp->keyValue);
+
+        temp = (result < 0) ? (temp->leftChild) : (temp->rightChild);
+    }
+    /* make the needed connections */
+    insert->parent = last_node;
+
+    if(IS_LEAF(last_node))
+        tree->root = insert;
+    else{
+        result = tree->compare(insert->keyValue, last_node->keyValue);
+        if(result < 0)
+            last_node->leftChild = insert;
+        else
+            last_node->rightChild = insert;
+    }
+
+    /* fix colour issues */
+    fix_insert_collisions(tree, insert);
+
+    return NULL;
+}
+
+struct pico_tree_node *pico_tree_findNode(struct pico_tree *tree, void *key)
+{
+    struct pico_tree_node *found;
+
+    found = tree->root;
+
+    while(IS_NOT_LEAF(found))
+    {
+        int result;
+        result = tree->compare(found->keyValue, key);
+        if(result == 0)
+        {
+            return found;
+        }
+        else if(result < 0)
+            found = found->rightChild;
+        else
+            found = found->leftChild;
+    }
+    return NULL;
+}
+
+void *pico_tree_findKey(struct pico_tree *tree, void *key)
+{
+    struct pico_tree_node *found;
+
+
+    found = tree->root;
+
+    while(IS_NOT_LEAF(found))
+    {
+        int result;
+
+        result = tree->compare(found->keyValue, key);
+        if(result == 0)
+            return found->keyValue;
+        else if(result < 0)
+            found = found->rightChild;
+        else
+            found = found->leftChild;
+
+    }
+    return NULL;
+}
+
+void *pico_tree_first(struct pico_tree *tree)
+{
+    return pico_tree_firstNode(tree->root)->keyValue;
+}
+
+void *pico_tree_last(struct pico_tree *tree)
+{
+    return pico_tree_lastNode(tree->root)->keyValue;
+}
+
+static uint8_t pico_tree_delete_node(struct pico_tree *tree, struct pico_tree_node *d, struct pico_tree_node **temp)
+{
+    struct pico_tree_node *min;
+    struct pico_tree_node *ltemp = d;
+    uint8_t nodeColor;
+    min = pico_tree_firstNode(d->rightChild);
+    nodeColor = min->color;
+
+    *temp = min->rightChild;
+    if(min->parent == ltemp && IS_NOT_LEAF(*temp))
+        (*temp)->parent = min;
+    else{
+        switchNodes(tree, min, min->rightChild);
+        min->rightChild = ltemp->rightChild;
+        if(IS_NOT_LEAF(min->rightChild)) min->rightChild->parent = min;
+    }
+
+    switchNodes(tree, ltemp, min);
+    min->leftChild = ltemp->leftChild;
+
+    if(IS_NOT_LEAF(min->leftChild))
+        min->leftChild->parent = min;
+
+    min->color = ltemp->color;
+    return nodeColor;
+}
+
+static uint8_t pico_tree_delete_check_switch(struct pico_tree *tree, struct pico_tree_node *delete, struct pico_tree_node **temp)
+{
+    struct pico_tree_node *ltemp = delete;
+    uint8_t nodeColor = delete->color;
+    if(IS_LEAF(delete->leftChild))
+    {
+        *temp = ltemp->rightChild;
+        switchNodes(tree, ltemp, ltemp->rightChild);
+    }
+    else
+    if(IS_LEAF(delete->rightChild))
+    {
+        struct pico_tree_node *_ltemp = delete;
+        *temp = _ltemp->leftChild;
+        switchNodes(tree, _ltemp, _ltemp->leftChild);
+    }
+    else{
+        nodeColor = pico_tree_delete_node(tree, delete, temp);
+    }
+
+    return nodeColor;
+
+}
+
+/* The memory manager also uses the pico_tree to keep track of all the different slab sizes it has.
+ * These nodes should be placed in the manager page which is in a different memory region then the nodes
+ * which are used for the pico stack in general.
+ * Therefore the following wrapper for pico_tree_delete is created.
+ * The actual implementation can be found in pico_tree_delete_implementation.
+ */
+void *pico_tree_delete(struct pico_tree *tree, void *key)
+{
+    return pico_tree_delete_implementation(tree, key, USE_PICO_ZALLOC);
+}
+
+static inline void if_nodecolor_black_fix_collisions(struct pico_tree *tree, struct pico_tree_node *temp, uint8_t nodeColor)
+{
+    /* deleted node is black, this will mess up the black path property */
+    if(nodeColor == BLACK)
+        fix_delete_collisions(tree, temp);
+
+}
+
+void *pico_tree_delete_implementation(struct pico_tree *tree, void *key, uint8_t allocator)
+{
+    struct pico_tree_node *temp;
+    uint8_t nodeColor; /* keeps the color of the node to be deleted */
+    void *lkey; /* keeps a copy of the key which will be removed */
+    struct pico_tree_node *delete;  /* keeps a copy of the node to be extracted */
+
+    if (!key)
+        return NULL;
+
+    delete = pico_tree_findNode(tree, key);
+
+    /* this key isn't in the tree, bail out */
+    if(!delete)
+        return NULL;
+
+    lkey = delete->keyValue;
+    nodeColor = pico_tree_delete_check_switch(tree, delete, &temp);
+
+    if_nodecolor_black_fix_collisions(tree, temp, nodeColor);
+
+    if(allocator == USE_PICO_ZALLOC)
+        PICO_FREE(delete);
+
+#ifdef PICO_SUPPORT_MM
+    else
+        pico_mem_page0_free(delete);
+#endif
+    return lkey;
+}
+
+int pico_tree_empty(struct pico_tree *tree)
+{
+    return (!tree->root || IS_LEAF(tree->root));
+}
+
+/*
+ * Private functions
+ */
+static void rotateToLeft(struct pico_tree*tree, struct pico_tree_node*node)
+{
+    struct pico_tree_node*temp;
+
+    temp = node->rightChild;
+
+    if(temp == &LEAF) return;
+
+    node->rightChild = temp->leftChild;
+
+    if(IS_NOT_LEAF(temp->leftChild))
+        temp->leftChild->parent = node;
+
+    temp->parent = node->parent;
+
+    if(IS_LEAF(node->parent))
+        tree->root = temp;
+    else
+    if(node == node->parent->leftChild)
+        node->parent->leftChild = temp;
+    else
+        node->parent->rightChild = temp;
+
+    temp->leftChild = node;
+    node->parent = temp;
+}
+
+
+static void rotateToRight(struct pico_tree *tree, struct pico_tree_node *node)
+{
+    struct pico_tree_node*temp;
+
+    temp = node->leftChild;
+    node->leftChild = temp->rightChild;
+
+    if(temp == &LEAF) return;
+
+    if(IS_NOT_LEAF(temp->rightChild))
+        temp->rightChild->parent = node;
+
+    temp->parent = node->parent;
+
+    if(IS_LEAF(node->parent))
+        tree->root = temp;
+    else
+    if(node == node->parent->rightChild)
+        node->parent->rightChild = temp;
+    else
+        node->parent->leftChild = temp;
+
+    temp->rightChild = node;
+    node->parent = temp;
+    return;
+}
+
+static struct pico_tree_node *create_node(struct pico_tree *tree, void*key, uint8_t allocator)
+{
+    struct pico_tree_node *temp = NULL;
+    IGNORE_PARAMETER(tree);
+    if(allocator == USE_PICO_ZALLOC)
+        temp = (struct pico_tree_node *)PICO_ZALLOC(sizeof(struct pico_tree_node));
+
+#ifdef PICO_SUPPORT_MM
+    else
+        temp = (struct pico_tree_node *)pico_mem_page0_zalloc(sizeof(struct pico_tree_node));
+#endif
+
+    if(!temp)
+        return NULL;
+
+    temp->keyValue = key;
+    temp->parent = &LEAF;
+    temp->leftChild = &LEAF;
+    temp->rightChild = &LEAF;
+    /* by default every new node is red */
+    temp->color = RED;
+    return temp;
+}
+
+/*
+ * This function fixes the possible collisions in the tree.
+ * Eg. if a node is red his children must be black !
+ */
+static void fix_insert_collisions(struct pico_tree*tree, struct pico_tree_node*node)
+{
+    struct pico_tree_node*temp;
+
+    while(node->parent->color == RED && IS_NOT_LEAF(GRANPA(node)))
+    {
+        if(AM_I_RIGHT_CHILD(node->parent))
+        {
+            temp = GRANPA(node)->leftChild;
+            if(temp->color == RED) {
+                node->parent->color = BLACK;
+                temp->color = BLACK;
+                GRANPA(node)->color = RED;
+                node = GRANPA(node);
+            }
+            else if(temp->color == BLACK) {
+                if(node == node->parent->leftChild) {
+                    node = node->parent;
+                    rotateToRight(tree, node);
+                }
+
+                node->parent->color = BLACK;
+                GRANPA(node)->color = RED;
+                rotateToLeft(tree, GRANPA(node));
+            }
+        }
+        else if(AM_I_LEFT_CHILD(node->parent))
+        {
+            temp = GRANPA(node)->rightChild;
+            if(temp->color == RED) {
+                node->parent->color = BLACK;
+                temp->color = BLACK;
+                GRANPA(node)->color = RED;
+                node = GRANPA(node);
+            }
+            else if(temp->color == BLACK) {
+                if(AM_I_RIGHT_CHILD(node)) {
+                    node = node->parent;
+                    rotateToLeft(tree, node);
+                }
+
+                node->parent->color = BLACK;
+                GRANPA(node)->color = RED;
+                rotateToRight(tree, GRANPA(node));
+            }
+        }
+    }
+    /* make sure that the root of the tree stays black */
+    tree->root->color = BLACK;
+}
+
+static void switchNodes(struct pico_tree*tree, struct pico_tree_node*nodeA, struct pico_tree_node*nodeB)
+{
+
+    if(IS_LEAF(nodeA->parent))
+        tree->root = nodeB;
+    else
+    if(IS_NOT_LEAF(nodeA))
+    {
+        if(AM_I_LEFT_CHILD(nodeA))
+            nodeA->parent->leftChild = nodeB;
+        else
+            nodeA->parent->rightChild = nodeB;
+    }
+
+    if(IS_NOT_LEAF(nodeB)) nodeB->parent = nodeA->parent;
+
+}
+
+/*
+ * This function fixes the possible collisions in the tree.
+ * Eg. if a node is red his children must be black !
+ * In this case the function fixes the constant black path property.
+ */
+static void fix_delete_collisions(struct pico_tree*tree, struct pico_tree_node *node)
+{
+    struct pico_tree_node*temp;
+
+    while( node != tree->root && node->color == BLACK && IS_NOT_LEAF(node))
+    {
+        if(AM_I_LEFT_CHILD(node)) {
+
+            temp = node->parent->rightChild;
+            if(temp->color == RED)
+            {
+                temp->color = BLACK;
+                node->parent->color = RED;
+                rotateToLeft(tree, node->parent);
+                temp = node->parent->rightChild;
+            }
+
+            if(temp->leftChild->color == BLACK && temp->rightChild->color == BLACK)
+            {
+                temp->color = RED;
+                node = node->parent;
+            }
+            else
+            {
+                if(temp->rightChild->color == BLACK)
+                {
+                    temp->leftChild->color = BLACK;
+                    temp->color = RED;
+                    rotateToRight(tree, temp);
+                    temp = temp->parent->rightChild;
+                }
+
+                temp->color = node->parent->color;
+                node->parent->color = BLACK;
+                temp->rightChild->color = BLACK;
+                rotateToLeft(tree, node->parent);
+                node = tree->root;
+            }
+        }
+        else{
+            temp = node->parent->leftChild;
+            if(temp->color == RED)
+            {
+                temp->color = BLACK;
+                node->parent->color = RED;
+                rotateToRight(tree, node->parent);
+                temp = node->parent->leftChild;
+            }
+
+            if(temp->rightChild->color == BLACK && temp->leftChild->color == BLACK)
+            {
+                temp->color = RED;
+                node = node->parent;
+            }
+            else{
+                if(temp->leftChild->color == BLACK)
+                {
+                    temp->rightChild->color = BLACK;
+                    temp->color = RED;
+                    rotateToLeft(tree, temp);
+                    temp = temp->parent->leftChild;
+                }
+
+                temp->color = node->parent->color;
+                node->parent->color = BLACK;
+                temp->leftChild->color = BLACK;
+                rotateToRight(tree, node->parent);
+                node = tree->root;
+            }
+        }
+    }
+    node->color = BLACK;
+}
-- 
2.1.4




More information about the barebox mailing list