Free (GPLv2) TCP/IP stack developed by TASS Belgium

Dependents:   lpc1768-picotcp-demo ZeroMQ_PicoTCP_Publisher_demo TCPSocket_HelloWorld_PicoTCP Pico_TCP_UDP_Test ... more

PicoTCP. Copyright (c) 2013 TASS Belgium NV.

Released under the GNU General Public License, version 2.

Different licensing models may exist, at the sole discretion of the Copyright holders.

Official homepage: http://www.picotcp.com

Bug tracker: https://github.com/tass-belgium/picotcp/issues

Development steps:

  • initial integration with mbed RTOS
  • generic mbed Ethernet driver
  • high performance NXP LPC1768 specific Ethernet driver
  • Multi-threading support for mbed RTOS
  • Berkeley sockets and integration with the New Socket API
  • Fork of the apps running on top of the New Socket API
  • Scheduling optimizations
  • Debugging/benchmarking/testing

Demo application (measuring TCP sender performance):

Import programlpc1768-picotcp-demo

A PicoTCP demo app testing the ethernet throughput on the lpc1768 mbed board.

Files at this revision

API Documentation at this revision

Comitter:
tass
Date:
Mon Sep 28 13:16:18 2015 +0200
Parent:
151:dbbb7e41f583
Child:
153:5e8a725dbd6e
Commit message:
Mercurial: latest development version of PicoTCP

Changed in this revision

include/heap.h Show annotated file Show diff for this revision Revisions of this file
include/pico_addressing.h Show annotated file Show diff for this revision Revisions of this file
include/pico_config.h Show annotated file Show diff for this revision Revisions of this file
include/pico_constants.h Show annotated file Show diff for this revision Revisions of this file
include/pico_device.h Show annotated file Show diff for this revision Revisions of this file
include/pico_eth.h Show annotated file Show diff for this revision Revisions of this file
include/pico_frame.h Show annotated file Show diff for this revision Revisions of this file
include/pico_module_eth.h Show annotated file Show diff for this revision Revisions of this file
include/pico_protocol.h Show annotated file Show diff for this revision Revisions of this file
include/pico_queue.h Show annotated file Show diff for this revision Revisions of this file
include/pico_socket.h Show annotated file Show diff for this revision Revisions of this file
include/pico_stack.h Show annotated file Show diff for this revision Revisions of this file
include/pico_tree.h Show annotated file Show diff for this revision Revisions of this file
modules/pico_arp.c Show annotated file Show diff for this revision Revisions of this file
modules/pico_arp.h Show annotated file Show diff for this revision Revisions of this file
modules/pico_dev_loop.c Show annotated file Show diff for this revision Revisions of this file
modules/pico_dev_loop.h Show annotated file Show diff for this revision Revisions of this file
modules/pico_dhcp_client.c Show annotated file Show diff for this revision Revisions of this file
modules/pico_dhcp_client.h Show annotated file Show diff for this revision Revisions of this file
modules/pico_dhcp_common.c Show annotated file Show diff for this revision Revisions of this file
modules/pico_dhcp_common.h Show annotated file Show diff for this revision Revisions of this file
modules/pico_dhcp_server.c Show annotated file Show diff for this revision Revisions of this file
modules/pico_dhcp_server.h Show annotated file Show diff for this revision Revisions of this file
modules/pico_dns_client.c Show annotated file Show diff for this revision Revisions of this file
modules/pico_dns_client.h Show annotated file Show diff for this revision Revisions of this file
modules/pico_icmp4.c Show annotated file Show diff for this revision Revisions of this file
modules/pico_icmp4.h Show annotated file Show diff for this revision Revisions of this file
modules/pico_icmp6.h Show annotated file Show diff for this revision Revisions of this file
modules/pico_igmp.c Show annotated file Show diff for this revision Revisions of this file
modules/pico_igmp.h Show annotated file Show diff for this revision Revisions of this file
modules/pico_ipfilter.c Show annotated file Show diff for this revision Revisions of this file
modules/pico_ipfilter.h Show annotated file Show diff for this revision Revisions of this file
modules/pico_ipv4.c Show annotated file Show diff for this revision Revisions of this file
modules/pico_ipv4.h Show annotated file Show diff for this revision Revisions of this file
modules/pico_ipv6.h Show annotated file Show diff for this revision Revisions of this file
modules/pico_ipv6_nd.h Show annotated file Show diff for this revision Revisions of this file
modules/pico_mm.h Show annotated file Show diff for this revision Revisions of this file
modules/pico_nat.c Show annotated file Show diff for this revision Revisions of this file
modules/pico_nat.h Show annotated file Show diff for this revision Revisions of this file
modules/pico_slaacv4.c Show annotated file Show diff for this revision Revisions of this file
modules/pico_slaacv4.h Show annotated file Show diff for this revision Revisions of this file
modules/pico_socket_tcp.c Show annotated file Show diff for this revision Revisions of this file
modules/pico_socket_udp.c Show annotated file Show diff for this revision Revisions of this file
modules/pico_socket_udp.h Show annotated file Show diff for this revision Revisions of this file
modules/pico_tcp.c Show annotated file Show diff for this revision Revisions of this file
modules/pico_tcp.h Show annotated file Show diff for this revision Revisions of this file
modules/pico_udp.c Show annotated file Show diff for this revision Revisions of this file
modules/pico_udp.h Show annotated file Show diff for this revision Revisions of this file
stack/pico_device.c Show annotated file Show diff for this revision Revisions of this file
stack/pico_frame.c Show annotated file Show diff for this revision Revisions of this file
stack/pico_protocol.c Show annotated file Show diff for this revision Revisions of this file
stack/pico_socket.c Show annotated file Show diff for this revision Revisions of this file
stack/pico_socket_multicast.c Show annotated file Show diff for this revision Revisions of this file
stack/pico_stack.c Show annotated file Show diff for this revision Revisions of this file
stack/pico_tree.c Show annotated file Show diff for this revision Revisions of this file
--- a/include/heap.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/include/heap.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
  *********************************************************************/
@@ -17,8 +17,10 @@
         type *newTop; \
         if (++heap->n >= heap->size) {                                                \
             newTop = PICO_ZALLOC((heap->n + 1) * sizeof(type)); \
-            if(!newTop) \
+            if(!newTop) { \
+                heap->n--; \
                 return -1; \
+            } \
             if (heap->top)  { \
                 memcpy(newTop, heap->top, heap->n * sizeof(type)); \
                 PICO_FREE(heap->top); \
--- a/include/pico_addressing.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/include/pico_addressing.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,12 +1,11 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   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 <stdint.h>
 #include "pico_config.h"
 
 PACKED_STRUCT_DEF pico_ip4
@@ -25,7 +24,7 @@
     struct pico_ip6 ip6;
 };
 
-struct pico_eth
+PACKED_STRUCT_DEF pico_eth
 {
     uint8_t addr[6];
     uint8_t padding[2];
@@ -34,7 +33,7 @@
 extern const uint8_t PICO_ETHADDR_ALL[];
 
 
-struct pico_trans
+PACKED_STRUCT_DEF pico_trans
 {
     uint16_t sport;
     uint16_t dport;
--- a/include/pico_config.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/include/pico_config.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,44 +1,222 @@
 /*********************************************************************
-PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
-See LICENSE and COPYING for usage.
+   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
 
-#ifdef __IAR_SYSTEMS_ICC__
+#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 *)&le;
+    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 *)&le;
+    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 MBED
+#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
+
 
-#define PICO_SUPPORT_ETH
-#define PICO_SUPPORT_CRC
-#define PICO_SUPPORT_DEVLOOP
-#define PICO_SUPPORT_DHCPC
-#define PICO_SUPPORT_DHCPD
-#define PICO_SUPPORT_DNS_CLIENT
-#define PICO_SUPPORT_ICMP4
-#define PICO_SUPPORT_PING
-#define PICO_SUPPORT_IGMP
-#define PICO_SUPPORT_IPFILTER
-#define PICO_SUPPORT_IPFRAG
-#define PICO_SUPPORT_IPV4
-#define PICO_SUPPORT_MCAST
-#define PICO_SUPPORT_NAT
-#define PICO_SUPPORT_TCP
-#define PICO_SUPPORT_UDP
-#define PICO_SUPPORT_SLAACV4
-
+/*** *** *** *** *** *** ***
+ *** 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)
@@ -48,4 +226,4 @@
 #define PICO_FREE(x) pico_free(x)
 #endif  /* PICO_SUPPORT_MM */
 
-#endif  /* INCLUDE_PICO_CONFIG */
+#endif
--- a/include/pico_constants.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/include/pico_constants.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
  *********************************************************************/
@@ -17,107 +17,6 @@
 typedef uint64_t pico_time;
 extern volatile uint64_t pico_tick;
 
-#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
-
-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 *)&le;
-    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 *)&le;
-    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;
-}
-#endif
 
 /*** *** *** *** *** *** ***
  ***     ARP CONFIG      ***
--- a/include/pico_device.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/include/pico_device.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
  *********************************************************************/
@@ -9,6 +9,7 @@
 #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
 
@@ -21,9 +22,11 @@
     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);
@@ -36,10 +39,16 @@
   #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
--- a/include/pico_eth.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/include/pico_eth.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
  *********************************************************************/
--- a/include/pico_frame.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/include/pico_frame.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
  *********************************************************************/
@@ -8,8 +8,10 @@
 #include "pico_config.h"
 
 
-#define PICO_FRAME_FLAG_BCAST   (0x01)
-#define PICO_FRAME_FLAG_SACKED  (0x80)
+#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)
 
 
@@ -62,8 +64,8 @@
     unsigned char *payload;
     uint16_t payload_len;
 
-#ifdef PICO_SUPPORT_IPFRAG
-    /* Payload fragmentation info (big endian)*/
+#if defined(PICO_SUPPORT_IPV4FRAG) || defined(PICO_SUPPORT_IPV6FRAG)
+    /* Payload fragmentation info */
     uint16_t frag;
 #endif
 
@@ -76,6 +78,12 @@
     /*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 **/
@@ -83,7 +91,8 @@
 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);
-struct pico_frame *pico_frame_alloc_skeleton(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);
--- a/include/pico_module_eth.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/include/pico_module_eth.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
  *********************************************************************/
--- a/include/pico_protocol.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/include/pico_protocol.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,11 +1,11 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   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 <stdint.h>
+#include "pico_config.h"
 #include "pico_queue.h"
 
 #define PICO_LOOP_DIR_IN   1
--- a/include/pico_queue.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/include/pico_queue.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,11 +1,10 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   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 <stdint.h>
 #include "pico_config.h"
 #include "pico_frame.h"
 
@@ -15,6 +14,13 @@
 #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;
@@ -36,10 +42,12 @@
         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
@@ -64,9 +72,12 @@
     if ((q->max_frames) && (q->max_frames <= q->frames))
         return -1;
 
-    if ((Q_LIMIT) && (Q_LIMIT < p->buffer_len + q->size))
+#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;
 
@@ -131,11 +142,18 @@
     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_FREE(p);
+        pico_frame_discard(p);
         p = pico_dequeue(q);
     }
 }
--- a/include/pico_socket.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/include/pico_socket.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
  *********************************************************************/
@@ -14,13 +14,26 @@
 #ifdef __linux__
     #define PICO_DEFAULT_SOCKETQ (16 * 1024) /* Linux host, so we want full throttle */
 #else
-    #define PICO_DEFAULT_SOCKETQ (4 * 1024) /* seems like an acceptable default for small embedded systems */
+    #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 ? */
@@ -55,6 +68,9 @@
 #endif
 #ifdef PICO_SUPPORT_MCAST
     struct pico_tree *MCASTListen;
+#ifdef PICO_SUPPORT_IPV6
+    struct pico_tree *MCASTListen_ipv6;
+#endif 
 #endif
     uint16_t ev_pending;
 
@@ -75,6 +91,7 @@
 
 
 /* request struct for multicast socket opt */
+// Deprecated!
 struct pico_ip_mreq {
     struct pico_ip4 mcast_group_addr;
     struct pico_ip4 mcast_link_addr;
@@ -85,6 +102,18 @@
     struct pico_ip4 mcast_source_addr;
     struct pico_ip4 mcast_link_addr;
 };
+// [end] Deprecated
+// Use this instead:
+struct pico_mreq {
+    union pico_address mcast_group_addr;
+    union pico_address mcast_link_addr;
+};
+struct pico_mreq_source {
+    union pico_address mcast_group_addr;
+    union pico_address mcast_source_addr;
+    union pico_address mcast_link_addr;
+};
+
 
 #define PICO_SOCKET_STATE_UNDEFINED       0x0000u
 #define PICO_SOCKET_STATE_SHUT_LOCAL      0x0001u
@@ -127,15 +156,22 @@
 # define PICO_IP_DROP_SOURCE_MEMBERSHIP       40
 
 # define PICO_SOCKET_OPT_MULTICAST_LOOP       1
+# define PICO_SOCKET_OPT_KEEPIDLE              4
+# define PICO_SOCKET_OPT_KEEPINTVL             5
+# define PICO_SOCKET_OPT_KEEPCNT               6
+
+#define PICO_SOCKET_OPT_LINGER                13
 
 # 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_TIMEOUT                   5000u /* 5 seconds */
+#define PICO_SOCKET_LINGER_TIMEOUT            3000u /* 3 seconds */
 #define PICO_SOCKET_BOUND_TIMEOUT             30000u /* 30 seconds */
 
 #define PICO_SOCKET_SHUTDOWN_WRITE 0x01u
@@ -149,6 +185,11 @@
 #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));
 
@@ -156,13 +197,19 @@
 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);
@@ -190,13 +237,13 @@
 #endif
 
 #ifdef PICO_SUPPORT_UDP
-# define is_sock_udp(x) (x->net == &pico_proto_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->net == &pico_proto_tcp)
+# define is_sock_tcp(x) (x->proto == &pico_proto_tcp)
 #else
 # define is_sock_tcp(x) (0)
 #endif
@@ -215,9 +262,11 @@
 
 struct pico_sockport *pico_get_sockport(uint16_t proto, uint16_t port);
 
-uint16_t pico_socket_get_mtu(struct pico_socket *s);
+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)
--- a/include/pico_stack.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/include/pico_stack.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
  *********************************************************************/
@@ -10,8 +10,8 @@
 
 #define PICO_MAX_TIMERS 20
 
-#define PICO_ETH_MTU 1514
-#define PICO_IP_MTU 1500u
+#define PICO_ETH_MRU (1514u)
+#define PICO_IP_MRU (1500u)
 
 /* ===== RECEIVING FUNCTIONS (from dev up to socket) ===== */
 
@@ -23,12 +23,6 @@
 /* interface towards ethernet */
 int32_t pico_network_receive(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);
 
 /* LOWEST LEVEL: interface towards devices. */
 /* Device driver will call this function which returns immediately.
@@ -38,13 +32,28 @@
  */
 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));
 
-/* ===== SENDIING FUNCTIONS (from socket down to dev) ===== */
+/* ===== 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);
-int32_t pico_sendto_dev(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);
@@ -58,15 +67,21 @@
 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
--- a/include/pico_tree.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/include/pico_tree.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    Author: Andrei Carp <andrei.carp@tass.be>
@@ -8,7 +8,6 @@
 #ifndef PICO_RBTREE_H
 #define PICO_RBTREE_H
 
-#include <stdint.h>
 #include "pico_config.h"
 
 /* This is used to declare a new tree, leaf root by default */
--- a/modules/pico_arp.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_arp.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    .
@@ -18,6 +18,7 @@
 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
@@ -25,24 +26,24 @@
     #define arp_dbg(...) do {} while(0)
 #endif
 
-static struct pico_queue pending;
-static int pending_timer_on = 0;
 static int max_arp_reqs = PICO_ARP_MAX_RATE;
-
+static struct pico_frame *frames_queued[PICO_ARP_MAX_PENDING] = { };
 
-static void check_pending(pico_time now, void *_unused)
+static void pico_arp_queued_trigger(void)
 {
-    struct pico_frame *f = pico_dequeue(&pending);
-    IGNORE_PARAMETER(now);
-    IGNORE_PARAMETER(_unused);
-    if (!f) {
-        pending_timer_on = 0;
-        return;
+    int i;
+    struct pico_frame *f;
+    for (i = 0; i < PICO_ARP_MAX_PENDING; i++)
+    {
+        f = frames_queued[i];
+        if (f) {
+            if (!pico_ethernet_send(f))
+            {
+                pico_frame_discard(f);
+                frames_queued[i] = NULL;
+            }
+        }
     }
-
-    pico_ethernet_send(f);
-
-    pico_timer_add(PICO_ARP_RETRY, &check_pending, NULL);
 }
 
 static void update_max_arp_reqs(pico_time now, void *unused)
@@ -116,12 +117,7 @@
 static int arp_compare(void *ka, void *kb)
 {
     struct pico_arp *a = ka, *b = kb;
-    if (a->ipv4.addr < b->ipv4.addr)
-        return -1;
-    else if (a->ipv4.addr > b->ipv4.addr)
-        return 1;
-
-    return 0;
+    return pico_ipv4_compare(&a->ipv4, &b->ipv4);
 }
 
 PICO_TREE_DECLARE(arp_tree, arp_compare);
@@ -153,21 +149,41 @@
     return NULL;
 }
 
-void pico_arp_retry(struct pico_frame *f, struct pico_ip4 *where)
+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);
-        pico_enqueue(&pending, f);
-        if (!pending_timer_on) {
-            pending_timer_on++;
-            pico_timer_add(PICO_ARP_RETRY, &check_pending, NULL);
-        }
     } else {
-        dbg("ARP: Destination Unreachable\n");
-        pico_notify_dest_unreachable(f);
-        pico_frame_discard(f);
+        pico_arp_unreachable(where);
     }
 }
 
@@ -202,6 +218,21 @@
     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)
 {
@@ -236,6 +267,7 @@
 
     pico_tree_insert(&arp_tree, entry);
     arp_dbg("ARP ## reachable.\n");
+    pico_arp_queued_trigger();
     pico_timer_add(PICO_ARP_TIMEOUT, arp_expire, entry);
 }
 
@@ -410,7 +442,7 @@
 
 }
 
-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)
+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;
--- a/modules/pico_arp.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_arp.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
  *********************************************************************/
@@ -27,5 +27,6 @@
 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
--- a/modules/pico_dev_loop.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_dev_loop.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    Authors: Daniele Lacamera
--- a/modules/pico_dev_loop.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_dev_loop.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
  *********************************************************************/
--- a/modules/pico_dhcp_client.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_dhcp_client.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    Authors: Kristof Roelants, Frederik Van Slycken
@@ -14,32 +14,20 @@
 #include "pico_socket.h"
 #include "pico_eth.h"
 
-#ifdef PICO_SUPPORT_DHCPC
+#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             3000 /* msec */
+#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
 
-/* custom statuses */
-#define DHCP_CLIENT_STATUS_INIT        0
-#define DHCP_CLIENT_STATUS_SOCKET      1
-#define DHCP_CLIENT_STATUS_BOUND       2
-#define DHCP_CLIENT_STATUS_LINKED      3
-#define DHCP_CLIENT_STATUS_TRANSMITTED 4
-#define DHCP_CLIENT_STATUS_INITIALIZED 5
-
 /* maximum size of a DHCP message */
-#define DHCP_CLIENT_MAXMSGZISE         PICO_IP_MTU
-
-/* serialize client negotiations if multiple devices */
-/* NOTE: ONLY initialization is serialized! */
-static uint8_t pico_dhcp_client_mutex = 1;
+#define DHCP_CLIENT_MAXMSGZISE         (PICO_IP_MRU - PICO_SIZE_IP4HDR)
 
 enum dhcp_client_state {
     DHCP_CLIENT_STATE_INIT_REBOOT = 0,
@@ -52,15 +40,25 @@
     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;
-    uint32_t time;
+    unsigned int type;
+    uint32_t xid;
 };
 
 struct pico_dhcp_client_cookie
 {
-    uint8_t status;
     uint8_t event;
     uint8_t retry;
     uint32_t xid;
@@ -72,16 +70,15 @@
     struct pico_ip4 address;
     struct pico_ip4 netmask;
     struct pico_ip4 gateway;
-    struct pico_ip4 nameserver;
+    struct pico_ip4 nameserver[2];
     struct pico_ip4 server_id;
     struct pico_device *dev;
-    struct dhcp_client_timer init_timer;
-    struct dhcp_client_timer requesting_timer;
-    struct dhcp_client_timer renewing_timer;
-    struct dhcp_client_timer rebinding_timer;
-    struct dhcp_client_timer T1_timer;
-    struct dhcp_client_timer T2_timer;
-    struct dhcp_client_timer lease_timer;
+    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);
@@ -90,6 +87,15 @@
 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_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;
@@ -120,7 +126,6 @@
     }
 
     dhcpc->state = DHCP_CLIENT_STATE_INIT;
-    dhcpc->status = DHCP_CLIENT_STATUS_INIT;
     dhcpc->xid = xid;
     dhcpc->uid = uid;
     *(dhcpc->uid) = 0;
@@ -131,6 +136,7 @@
     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 = {
@@ -142,7 +148,9 @@
     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);
@@ -163,129 +171,101 @@
         return NULL;
 }
 
-static void pico_dhcp_client_init_timer(pico_time __attribute__((unused)) now, void *arg)
+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 pico_dhcp_client_cookie *dhcpc = (struct pico_dhcp_client_cookie *)arg;
-
-    if (dhcpc->init_timer.state == DHCP_CLIENT_TIMER_STOPPED)
-        return;
+    struct dhcp_client_timer *t;
 
-    if (++dhcpc->retry >= DHCP_CLIENT_RETRIES) {
-        pico_err = PICO_ERR_EAGAIN;
-        dhcpc->cb(dhcpc, PICO_DHCP_ERROR);
-        pico_dhcp_client_del_cookie(dhcpc->xid);
-        pico_dhcp_client_mutex++;
-        return;
+    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;
     }
 
-    /* init_timer is restarted in retransmit function,
-     * otherwise an old init_timer would go on indefinitely */
-    dhcpc->event = PICO_DHCP_EVENT_RETRANSMIT;
-    pico_dhcp_state_machine(dhcpc->event, dhcpc, NULL);
-    return;
+    ck->timer[type] = t;
+    return t;
 }
 
-static void pico_dhcp_client_requesting_timer(pico_time __attribute__((unused)) now, void *arg)
+static int dhcp_get_timer_event(struct pico_dhcp_client_cookie *dhcpc, unsigned int type)
 {
-    struct pico_dhcp_client_cookie *dhcpc = (struct pico_dhcp_client_cookie *)arg;
+    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 (dhcpc->requesting_timer.state == DHCP_CLIENT_TIMER_STOPPED)
+    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;
 
-    if (++dhcpc->retry > DHCP_CLIENT_RETRIES) {
-        pico_dhcp_client_mutex++;
-        reset(dhcpc, NULL);
-        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);
+            }
+        }
     }
-
-    /* requesting_timer is restarted in retransmit function,
-     * otherwise an old requesting_timer would go on indefinitely */
-    dhcpc->event = PICO_DHCP_EVENT_RETRANSMIT;
-    pico_dhcp_state_machine(dhcpc->event, dhcpc, NULL);
-    return;
 }
 
-static void pico_dhcp_client_renewing_timer(pico_time __attribute__((unused)) now, void *arg)
+static void pico_dhcp_client_timer_stop(struct pico_dhcp_client_cookie *dhcpc, int type)
 {
-    struct pico_dhcp_client_cookie *dhcpc = (struct pico_dhcp_client_cookie *)arg;
-
-    if (dhcpc->renewing_timer.state == DHCP_CLIENT_TIMER_STOPPED)
-        return;
+    if (dhcpc->timer[type]) {
+        dhcpc->timer[type]->state = DHCP_CLIENT_TIMER_STOPPED;
+    }
 
-    /* renewing_timer is restarted in retransmit function,
-     * otherwise an old renewing_timer would go on indefinitely */
-    dhcpc->retry++;
-    dhcpc->event = PICO_DHCP_EVENT_RETRANSMIT;
-    pico_dhcp_state_machine(dhcpc->event, dhcpc, NULL);
-    return;
 }
 
-static void pico_dhcp_client_rebinding_timer(pico_time __attribute__((unused)) now, void *arg)
+
+static void pico_dhcp_client_reinit(pico_time now, void *arg)
 {
     struct pico_dhcp_client_cookie *dhcpc = (struct pico_dhcp_client_cookie *)arg;
-
-    if (dhcpc->rebinding_timer.state == DHCP_CLIENT_TIMER_STOPPED)
-        return;
-
-    /* rebinding_timer is restarted in retransmit function,
-     * otherwise an old rebinding_timer would go on indefinitely */
-    dhcpc->retry++;
-    dhcpc->event = PICO_DHCP_EVENT_RETRANSMIT;
-    pico_dhcp_state_machine(dhcpc->event, dhcpc, NULL);
-    return;
-}
-
-static void pico_dhcp_client_T1_timer(pico_time __attribute__((unused)) now, void *arg)
-{
-    struct pico_dhcp_client_cookie *dhcpc = (struct pico_dhcp_client_cookie *)arg;
-
-    if (dhcpc->T1_timer.state == DHCP_CLIENT_TIMER_STOPPED)
-        return;
-
-    /* T1 state is set to stopped in renew function,
-     * otherwise an old T1 could stop a valid T1 */
-    dhcpc->event = PICO_DHCP_EVENT_T1;
-    pico_dhcp_state_machine(dhcpc->event, dhcpc, NULL);
-    return;
-}
-
-static void pico_dhcp_client_T2_timer(pico_time __attribute__((unused)) now, void *arg)
-{
-    struct pico_dhcp_client_cookie *dhcpc = (struct pico_dhcp_client_cookie *)arg;
+    (void) now;
 
-    if (dhcpc->T2_timer.state == DHCP_CLIENT_TIMER_STOPPED)
-        return;
-
-    /* T2 state is set to stopped in rebind function,
-     * otherwise an old T2 could stop a valid T2.
-     * Likewise for renewing_timer */
-    dhcpc->event = PICO_DHCP_EVENT_T2;
-    pico_dhcp_state_machine(dhcpc->event, dhcpc, NULL);
-    return;
-}
-
-static void pico_dhcp_client_lease_timer(pico_time __attribute__((unused)) now, void *arg)
-{
-    struct pico_dhcp_client_cookie *dhcpc = (struct pico_dhcp_client_cookie *)arg;
-
-    if (dhcpc->lease_timer.state == DHCP_CLIENT_TIMER_STOPPED)
-        return;
-
-    /* lease state is set to stopped in reset function,
-     * otherwise an old lease could stop a valid lease.
-     * Likewise for rebinding_timer */
-    dhcpc->event = PICO_DHCP_EVENT_LEASE;
-    pico_dhcp_state_machine(dhcpc->event, dhcpc, NULL);
-    return;
-}
-
-static void pico_dhcp_client_reinit(pico_time __attribute__((unused)) now, void *arg)
-{
-    struct pico_dhcp_client_cookie *dhcpc = (struct pico_dhcp_client_cookie *)arg;
+    if (dhcpc->s) {
+        pico_socket_close(dhcpc->s);
+        dhcpc->s = NULL;
+    }
 
     if (++dhcpc->retry > DHCP_CLIENT_RETRIES) {
         pico_err = PICO_ERR_EAGAIN;
-        dhcpc->cb(dhcpc, PICO_DHCP_ERROR);
+        if (dhcpc->cb)
+            dhcpc->cb(dhcpc, PICO_DHCP_ERROR);
+
         pico_dhcp_client_del_cookie(dhcpc->xid);
         return;
     }
@@ -294,31 +274,21 @@
     return;
 }
 
+
 static void pico_dhcp_client_stop_timers(struct pico_dhcp_client_cookie *dhcpc)
 {
+    int i;
     dhcpc->retry = 0;
-    dhcpc->init_timer.state = DHCP_CLIENT_TIMER_STOPPED;
-    dhcpc->requesting_timer.state = DHCP_CLIENT_TIMER_STOPPED;
-    dhcpc->renewing_timer.state = DHCP_CLIENT_TIMER_STOPPED;
-    dhcpc->rebinding_timer.state = DHCP_CLIENT_TIMER_STOPPED;
-    dhcpc->T1_timer.state = DHCP_CLIENT_TIMER_STOPPED;
-    dhcpc->T2_timer.state = DHCP_CLIENT_TIMER_STOPPED;
-    dhcpc->lease_timer.state = DHCP_CLIENT_TIMER_STOPPED;
-
-    return;
+    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) */
-    dhcpc->init_timer.state = DHCP_CLIENT_TIMER_STARTED;
-    dhcpc->init_timer.time = DHCP_CLIENT_RETRANS;
-    time = dhcpc->init_timer.time << dhcpc->retry;
-    pico_timer_add(time * 1000, pico_dhcp_client_init_timer, dhcpc);
-
-    return;
+    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)
@@ -326,13 +296,8 @@
     uint32_t time = 0;
 
     /* timer value is doubled with every retry (exponential backoff) */
-    dhcpc->init_timer.state = DHCP_CLIENT_TIMER_STOPPED;
-    dhcpc->requesting_timer.state = DHCP_CLIENT_TIMER_STARTED;
-    dhcpc->requesting_timer.time = DHCP_CLIENT_RETRANS;
-    time = dhcpc->requesting_timer.time << dhcpc->retry;
-    pico_timer_add(time * 1000, pico_dhcp_client_requesting_timer, dhcpc);
-
-    return;
+    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)
@@ -341,13 +306,12 @@
 
     /* 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 */
-    dhcpc->T1_timer.state = DHCP_CLIENT_TIMER_STOPPED;
-    dhcpc->renewing_timer.state = DHCP_CLIENT_TIMER_STARTED;
-    halftime = dhcpc->renewing_timer.time >> (dhcpc->retry + 1);
+    pico_dhcp_client_stop_timers(dhcpc);
+    halftime = dhcpc->renew_time >> (dhcpc->retry + 1);
     if (halftime < 60)
         halftime = 60;
 
-    pico_timer_add(halftime * 1000, pico_dhcp_client_renewing_timer, dhcpc);
+    pico_dhcp_timer_add(PICO_DHCPC_TIMER_RENEW, halftime * 1000, dhcpc);
 
     return;
 }
@@ -356,106 +320,60 @@
 {
     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 */
-    dhcpc->T2_timer.state = DHCP_CLIENT_TIMER_STOPPED;
-    dhcpc->renewing_timer.state = DHCP_CLIENT_TIMER_STOPPED;
-    dhcpc->rebinding_timer.state = DHCP_CLIENT_TIMER_STARTED;
-    halftime = dhcpc->rebinding_timer.time >> (dhcpc->retry + 1);
+    pico_dhcp_client_stop_timers(dhcpc);
+    halftime = dhcpc->rebind_time >> (dhcpc->retry + 1);
     if (halftime < 60)
         halftime = 60;
 
-    pico_timer_add(halftime * 1000, pico_dhcp_client_rebinding_timer, dhcpc);
+    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)
 {
-    dhcpc->requesting_timer.state = DHCP_CLIENT_TIMER_STOPPED;
-    dhcpc->T1_timer.state = DHCP_CLIENT_TIMER_STARTED;
-    dhcpc->T2_timer.state = DHCP_CLIENT_TIMER_STARTED;
-    dhcpc->lease_timer.state = DHCP_CLIENT_TIMER_STARTED;
-    pico_timer_add(dhcpc->T1_timer.time * 1000, pico_dhcp_client_T1_timer, dhcpc);
-    pico_timer_add(dhcpc->T2_timer.time * 1000, pico_dhcp_client_T2_timer, dhcpc);
-    pico_timer_add(dhcpc->lease_timer.time * 1000, pico_dhcp_client_lease_timer, dhcpc);
 
-    return;
+    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;
-    struct pico_ip4 inaddr_any = {
-        0
-    }, netmask = {
-        0
-    };
+    if (!dhcpc)
+        return -1;
 
-    /* serialize client negotations if multiple devices */
-    /* NOTE: ONLY initialization is serialized! */
-    if (!pico_dhcp_client_mutex) {
-        pico_timer_add(DHCP_CLIENT_REINIT, pico_dhcp_client_reinit, dhcpc);
+    /* 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;
     }
 
-    pico_dhcp_client_mutex--;
-
-    switch (dhcpc->status)
-    {
-    case DHCP_CLIENT_STATUS_INIT:
-        dhcpc->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcp_client_wakeup);
-        if (!dhcpc->s) {
-            pico_dhcp_client_mutex++;
-            pico_timer_add(DHCP_CLIENT_REINIT, pico_dhcp_client_reinit, dhcpc);
-            break;
-        }
-
-        dhcpc->s->dev = dhcpc->dev;
-        dhcpc->status = DHCP_CLIENT_STATUS_SOCKET;
-    /* fallthrough */
-
-    case DHCP_CLIENT_STATUS_SOCKET:
-        if (pico_socket_bind(dhcpc->s, &inaddr_any, &port) < 0) {
-            pico_dhcp_client_mutex++;
-            pico_timer_add(DHCP_CLIENT_REINIT, pico_dhcp_client_reinit, dhcpc);
-            break;
-        }
-
-        dhcpc->status = DHCP_CLIENT_STATUS_BOUND;
-    /* fallthrough */
+    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;
+    }
 
-    case DHCP_CLIENT_STATUS_BOUND:
-        /* adding a link with address 0.0.0.0 and netmask 0.0.0.0,
-         * automatically adds a route for a global broadcast */
-        if (pico_ipv4_link_add(dhcpc->dev, inaddr_any, netmask) < 0) {
-            pico_dhcp_client_mutex++;
-            pico_timer_add(DHCP_CLIENT_REINIT, pico_dhcp_client_reinit, dhcpc);
-            break;
-        }
-
-        dhcpc->status = DHCP_CLIENT_STATUS_LINKED;
-    /* fallthrough */
+    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;
+    }
 
-    case DHCP_CLIENT_STATUS_LINKED:
-        if (pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_DISCOVER) < 0) {
-            pico_dhcp_client_mutex++;
-            pico_timer_add(DHCP_CLIENT_REINIT, pico_dhcp_client_reinit, dhcpc);
-            break;
-        }
-
-        dhcpc->status = DHCP_CLIENT_STATUS_TRANSMITTED;
-    /* fallthrough */
-
-    case DHCP_CLIENT_STATUS_TRANSMITTED:
-        dhcpc->retry = 0;
-        dhcpc->init_timestamp = PICO_TIME_MS();
-        pico_dhcp_client_start_init_timer(dhcpc);
-        break;
-
-    default:
-        return -1;
-    }
+    dhcpc->retry = 0;
+    dhcpc->init_timestamp = PICO_TIME_MS();
+    pico_dhcp_client_start_init_timer(dhcpc);
     return 0;
 }
 
@@ -490,6 +408,7 @@
         return -1;
 
     dhcpc_dbg("DHCP client: cookie with xid %u\n", dhcpc->xid);
+    *uid = xid;
     return pico_dhcp_client_init(dhcpc);
 }
 
@@ -510,18 +429,18 @@
             break;
 
         case PICO_DHCP_OPT_LEASETIME:
-            dhcpc->lease_timer.time = long_be(opt->ext.lease_time.time);
-            dhcpc_dbg("DHCP client: lease time %u\n", dhcpc->lease_timer.time);
+            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_timer.time = long_be(opt->ext.renewal_time.time);
-            dhcpc_dbg("DHCP client: renewal time %u\n", dhcpc->T1_timer.time);
+            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_timer.time = long_be(opt->ext.rebinding_time.time);
-            dhcpc_dbg("DHCP client: rebinding time %u\n", dhcpc->T2_timer.time);
+            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:
@@ -530,8 +449,13 @@
             break;
 
         case PICO_DHCP_OPT_DNS:
-            dhcpc->nameserver = opt->ext.dns.ip;
-            dhcpc_dbg("DHCP client: dns %08X\n", dhcpc->nameserver.addr);
+            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:
@@ -555,11 +479,11 @@
     } while (pico_dhcp_next_option(&opt));
 
     /* default values for T1 and T2 when not provided */
-    if (!dhcpc->T1_timer.time)
-        dhcpc->T1_timer.time = dhcpc->lease_timer.time >> 1;
+    if (!dhcpc->t1_time)
+        dhcpc->t1_time = dhcpc->lease_time >> 1;
 
-    if (!dhcpc->T2_timer.time)
-        dhcpc->T2_timer.time = (dhcpc->lease_timer.time * 875) / 1000;
+    if (!dhcpc->t2_time)
+        dhcpc->t2_time = (dhcpc->lease_time * 875) / 1000;
 
     return;
 }
@@ -570,7 +494,7 @@
     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_timer.time)
+    if ((dhcpc->event != PICO_DHCP_MSG_OFFER) || !dhcpc->server_id.addr || !dhcpc->netmask.addr || !dhcpc->lease_time)
         return -1;
 
     dhcpc->address.addr = hdr->yiaddr;
@@ -589,14 +513,15 @@
     struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)hdr->options;
     struct pico_ip4 address = {
         0
-    }, netmask = {
+    };
+    struct pico_ip4 any_address = {
         0
-    }, bcast = {
-        .addr = 0xFFFFFFFF
     };
 
+    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_timer.time)
+    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 */
@@ -607,49 +532,60 @@
 
     /* close the socket used for address (re)acquisition */
     pico_socket_close(dhcpc->s);
-    /* delete the link with address 0.0.0.0, add new link with acquired address */
-    if (dhcpc->status == DHCP_CLIENT_STATUS_TRANSMITTED) {
-        pico_ipv4_link_del(dhcpc->dev, address);
-        pico_ipv4_link_add(dhcpc->dev, dhcpc->address, dhcpc->netmask);
-        dhcpc->status = DHCP_CLIENT_STATUS_INITIALIZED;
+    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);
     }
 
-    /* delete the default route for our global broadcast messages, otherwise another interface can not rebind */
-    if (dhcpc->state == DHCP_CLIENT_STATE_REBINDING)
-        pico_ipv4_route_del(bcast, netmask, 1);
-
-    dbg("DHCP client: renewal time (T1) %u\n", dhcpc->T1_timer.time);
-    dbg("DHCP client: rebinding time (T2) %u\n", dhcpc->T2_timer.time);
-    dbg("DHCP client: lease time %u\n", dhcpc->lease_timer.time);
-
     dhcpc->retry = 0;
-    dhcpc->renewing_timer.time = dhcpc->T2_timer.time - dhcpc->T1_timer.time;
-    dhcpc->rebinding_timer.time = dhcpc->lease_timer.time - dhcpc->T2_timer.time;
+    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);
 
-    pico_dhcp_client_mutex++;
     *(dhcpc->uid) = dhcpc->xid;
-    dhcpc->cb(dhcpc, PICO_DHCP_SUCCESS);
+    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 __attribute__((unused)) *buf)
+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));
-        dhcpc->cb(dhcpc, PICO_DHCP_ERROR);
+        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->cb(dhcpc, PICO_DHCP_ERROR);
+        dhcpc->s = NULL;
+        if (dhcpc->cb)
+            dhcpc->cb(dhcpc, PICO_DHCP_ERROR);
+
         return -1;
     }
 
@@ -660,31 +596,24 @@
     return 0;
 }
 
-static int rebind(struct pico_dhcp_client_cookie *dhcpc, uint8_t __attribute__((unused)) *buf)
+static int rebind(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
 {
-    struct pico_ip4 bcast = {
-        .addr = 0xFFFFFFFF
-    }, netmask = {
-        0
-    }, inaddr_any = {
-        0
-    };
+    (void) buf;
 
     dhcpc->state = DHCP_CLIENT_STATE_REBINDING;
     dhcpc->retry = 0;
-    /* we need a default route for our global broadcast messages, otherwise they get dropped. */
-    pico_ipv4_route_add(bcast, netmask, inaddr_any, 1, pico_ipv4_link_get(&dhcpc->address));
     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 __attribute__((unused)) *buf)
+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;
@@ -693,24 +622,28 @@
 
     /* 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);
 
-    dhcpc->cb(dhcpc, PICO_DHCP_RESET);
+    if (dhcpc->cb)
+        dhcpc->cb(dhcpc, PICO_DHCP_RESET);
+
     if (dhcpc->state < DHCP_CLIENT_STATE_BOUND)
     {
-        pico_dhcp_client_mutex++;
+        /* pico_dhcp_client_timer_stop(dhcpc, PICO_DHCPC_TIMER_INIT); */
     }
 
+
     dhcpc->state = DHCP_CLIENT_STATE_INIT;
-    dhcpc->status = DHCP_CLIENT_STATUS_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 __attribute__((unused)) *buf)
+static int retransmit(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf)
 {
+    (void) buf;
     switch (dhcpc->state)
     {
     case DHCP_CLIENT_STATE_INIT:
@@ -860,6 +793,12 @@
     };
     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:
@@ -876,7 +815,6 @@
         break;
 
     case PICO_DHCP_MSG_REQUEST:
-        dhcpc_dbg("DHCP client: sent DHCPREQUEST\n");
         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);
@@ -934,6 +872,9 @@
     /* 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)
@@ -944,19 +885,23 @@
 
 static void pico_dhcp_client_wakeup(uint16_t ev, struct pico_socket *s)
 {
-    uint8_t buf[DHCP_CLIENT_MAXMSGZISE] = {
-        0
-    };
+
+    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)
+    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)
-        return;
+        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
@@ -964,10 +909,13 @@
     hdr = (struct pico_dhcp_hdr *)buf;
     dhcpc = pico_dhcp_client_find_cookie(hdr->xid);
     if (!dhcpc)
-        return;
+        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)
@@ -990,9 +938,19 @@
     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;
 
-struct pico_ip4 pico_dhcp_get_nameserver(void*dhcpc)
+    return ((struct pico_dhcp_client_cookie*)dhcpc)->nameserver[index];
+}
+
+int pico_dhcp_client_abort(uint32_t xid)
 {
-    return ((struct pico_dhcp_client_cookie*)dhcpc)->nameserver;
+    return pico_dhcp_client_del_cookie(xid);
 }
 #endif
--- a/modules/pico_dhcp_client.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_dhcp_client.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    .
@@ -7,17 +7,19 @@
  *********************************************************************/
 #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_process_incoming_message(uint8_t *data, int len);
 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);
+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
@@ -25,3 +27,4 @@
 #define PICO_DHCP_RESET   2
 
 #endif
+#endif
--- a/modules/pico_dhcp_common.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_dhcp_common.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    .
@@ -90,7 +90,7 @@
     /* option: dns */
     opt->code = PICO_DHCP_OPT_DNS;
     opt->len = PICO_DHCP_OPTLEN_DNS - PICO_DHCP_OPTLEN_HDR;
-    opt->ext.dns.ip = *ip;
+    opt->ext.dns1.ip = *ip;
     return PICO_DHCP_OPTLEN_DNS;
 }
 
@@ -162,6 +162,7 @@
     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;
 }
 
--- a/modules/pico_dhcp_common.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_dhcp_common.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    .
@@ -57,7 +57,7 @@
 #define PICO_DHCP_OPTLEN_OPTOVERLOAD    3
 #define PICO_DHCP_OPTLEN_MSGTYPE        3
 #define PICO_DHCP_OPTLEN_SERVERID       6
-#define PICO_DHCP_OPTLEN_PARAMLIST      8 /* PicoTCP specific */
+#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
@@ -82,6 +82,7 @@
 #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
 {
@@ -108,59 +109,60 @@
 {
     uint8_t code;
     uint8_t len;
-    union dhcp_opt_ext_u {
-        struct {
+    PACKED_UNION_DEF dhcp_opt_ext_u {
+        PEDANTIC_STRUCT_DEF netmask_s {
             struct pico_ip4 ip;
         } netmask;
-        struct {
+        PEDANTIC_STRUCT_DEF router_s {
             struct pico_ip4 ip;
         } router;
-        struct {
+        PEDANTIC_STRUCT_DEF dns_s {
             struct pico_ip4 ip;
-        } dns;
-        struct {
+        } dns1;
+        struct dns_s dns2;
+        PEDANTIC_STRUCT_DEF broadcast_s {
             struct pico_ip4 ip;
         } broadcast;
-        struct {
+        PEDANTIC_STRUCT_DEF req_ip_s {
             struct pico_ip4 ip;
         } req_ip;
-        struct {
+        PEDANTIC_STRUCT_DEF lease_time_s {
             uint32_t time;
         } lease_time;
-        struct {
+        PEDANTIC_STRUCT_DEF opt_overload_s {
             uint8_t value;
         } opt_overload;
-        struct {
+        PEDANTIC_STRUCT_DEF tftp_server_s {
             char name[1];
         } tftp_server;
-        struct {
+        PEDANTIC_STRUCT_DEF bootfile_s {
             char name[1];
         } bootfile;
-        struct {
+        PEDANTIC_STRUCT_DEF msg_type_s {
             uint8_t type;
         } msg_type;
-        struct {
+        PEDANTIC_STRUCT_DEF server_id_s {
             struct pico_ip4 ip;
         } server_id;
-        struct {
+        PEDANTIC_STRUCT_DEF param_list_s {
             uint8_t code[1];
         } param_list;
-        struct {
+        PEDANTIC_STRUCT_DEF message_s {
             char error[1];
         } message;
-        struct {
+        PEDANTIC_STRUCT_DEF max_msg_size_s {
             uint16_t size;
         } max_msg_size;
-        struct {
+        PEDANTIC_STRUCT_DEF renewal_time_s {
             uint32_t time;
         } renewal_time;
-        struct {
+        PEDANTIC_STRUCT_DEF rebinding_time_s {
             uint32_t time;
         } rebinding_time;
-        struct {
+        PEDANTIC_STRUCT_DEF vendor_id_s {
             uint8_t id[1];
         } vendor_id;
-        struct {
+        PEDANTIC_STRUCT_DEF client_id_s {
             uint8_t id[1];
         } client_id;
     } ext;
--- a/modules/pico_dhcp_server.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_dhcp_server.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
 
@@ -14,7 +14,7 @@
 #include "pico_stack.h"
 #include "pico_arp.h"
 
-#ifdef PICO_SUPPORT_DHCPD
+#if (defined PICO_SUPPORT_DHCPD && defined PICO_SUPPORT_UDP)
 
 #define dhcps_dbg(...) do {} while(0)
 /* #define dhcps_dbg dbg */
@@ -26,7 +26,7 @@
 #define DHCP_SERVER_LEASE_TIME long_be(0x00000078)
 
 /* maximum size of a DHCP message */
-#define DHCP_SERVER_MAXMSGSIZE (PICO_IP_MTU - sizeof(struct pico_ipv4_hdr) - sizeof(struct pico_udp_hdr))
+#define DHCP_SERVER_MAXMSGSIZE (PICO_IP_MRU - sizeof(struct pico_ipv4_hdr) - sizeof(struct pico_udp_hdr))
 
 enum dhcp_server_state {
     PICO_DHCP_STATE_DISCOVER = 0,
@@ -80,9 +80,46 @@
 }
 PICO_TREE_DECLARE(DHCPNegotiations, dhcp_negotiations_cmp);
 
+
+static inline void dhcps_set_default_pool_start_if_not_provided(struct pico_dhcp_server_setting *dhcps)
+{
+    if (!dhcps->pool_start)
+        dhcps->pool_start = (dhcps->server_ip.addr & dhcps->netmask.addr) | DHCP_SERVER_POOL_START;
+}
+
+static inline void dhcps_set_default_pool_end_if_not_provided(struct pico_dhcp_server_setting *dhcps)
+{
+    if (!dhcps->pool_end)
+        dhcps->pool_end = (dhcps->server_ip.addr & dhcps->netmask.addr) | DHCP_SERVER_POOL_END;
+}
+static inline void dhcps_set_default_lease_time_if_not_provided(struct pico_dhcp_server_setting *dhcps)
+{
+    if (!dhcps->lease_time)
+        dhcps->lease_time = DHCP_SERVER_LEASE_TIME;
+}
+
+static inline struct pico_dhcp_server_setting *dhcps_try_open_socket(struct pico_dhcp_server_setting *dhcps)
+{
+    uint16_t port = PICO_DHCPD_PORT;
+    dhcps->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcpd_wakeup);
+    if (!dhcps->s) {
+        dhcps_dbg("DHCP server ERROR: failure opening socket (%s)\n", strerror(pico_err));
+        PICO_FREE(dhcps);
+        return NULL;
+    }
+
+    if (pico_socket_bind(dhcps->s, &dhcps->server_ip, &port) < 0) {
+        dhcps_dbg("DHCP server ERROR: failure binding socket (%s)\n", strerror(pico_err));
+        PICO_FREE(dhcps);
+        return NULL;
+    }
+
+    pico_tree_insert(&DHCPSettings, dhcps);
+    return dhcps;
+}
+
 static struct pico_dhcp_server_setting *pico_dhcp_server_add_setting(struct pico_dhcp_server_setting *setting)
 {
-    uint16_t port = PICO_DHCPD_PORT;
     struct pico_dhcp_server_setting *dhcps = NULL, *found = NULL, test = {
         0
     };
@@ -116,32 +153,14 @@
     dhcps->netmask = link->netmask;
 
     /* default values if not provided */
-    if (!dhcps->pool_start)
-        dhcps->pool_start = (dhcps->server_ip.addr & dhcps->netmask.addr) | DHCP_SERVER_POOL_START;
-
-    if (!dhcps->pool_end)
-        dhcps->pool_end = (dhcps->server_ip.addr & dhcps->netmask.addr) | DHCP_SERVER_POOL_END;
-
-    if (!dhcps->lease_time)
-        dhcps->lease_time = DHCP_SERVER_LEASE_TIME;
+    dhcps_set_default_lease_time_if_not_provided(dhcps);
+    dhcps_set_default_pool_end_if_not_provided(dhcps);
+    dhcps_set_default_pool_start_if_not_provided(dhcps);
 
     dhcps->pool_next = dhcps->pool_start;
 
-    dhcps->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcpd_wakeup);
-    if (!dhcps->s) {
-        dhcps_dbg("DHCP server ERROR: failure opening socket (%s)\n", strerror(pico_err));
-        PICO_FREE(dhcps);
-        return NULL;
-    }
+    return dhcps_try_open_socket(dhcps);
 
-    if (pico_socket_bind(dhcps->s, &dhcps->server_ip, &port) < 0) {
-        dhcps_dbg("DHCP server ERROR: failure binding socket (%s)\n", strerror(pico_err));
-        PICO_FREE(dhcps);
-        return NULL;
-    }
-
-    pico_tree_insert(&DHCPSettings, dhcps);
-    return dhcps;
 }
 
 static struct pico_dhcp_server_negotiation *pico_dhcp_server_find_negotiation(uint32_t xid)
@@ -158,13 +177,25 @@
         return NULL;
 }
 
+static inline void dhcp_negotiation_set_ciaddr(struct pico_dhcp_server_negotiation *dhcpn)
+{
+    struct pico_ip4 *ciaddr = NULL;
+    ciaddr = pico_arp_reverse_lookup(&dhcpn->hwaddr);
+    if (!ciaddr) {
+        dhcpn->ciaddr.addr = dhcpn->dhcps->pool_next;
+        dhcpn->dhcps->pool_next = long_be(long_be(dhcpn->dhcps->pool_next) + 1);
+        pico_arp_create_entry(dhcpn->hwaddr.addr, dhcpn->ciaddr, dhcpn->dhcps->dev);
+    } else {
+        dhcpn->ciaddr = *ciaddr;
+    }
+}
+
 static struct pico_dhcp_server_negotiation *pico_dhcp_server_add_negotiation(struct pico_device *dev, struct pico_dhcp_hdr *hdr)
 {
     struct pico_dhcp_server_negotiation *dhcpn = NULL;
     struct pico_dhcp_server_setting test = {
         0
     };
-    struct pico_ip4 *ciaddr = NULL;
 
     if (pico_dhcp_server_find_negotiation(hdr->xid))
         return NULL;
@@ -188,15 +219,7 @@
         return NULL;
     }
 
-    ciaddr = pico_arp_reverse_lookup(&dhcpn->hwaddr);
-    if (!ciaddr) {
-        dhcpn->ciaddr.addr = dhcpn->dhcps->pool_next;
-        dhcpn->dhcps->pool_next = long_be(long_be(dhcpn->dhcps->pool_next) + 1);
-        pico_arp_create_entry(dhcpn->hwaddr.addr, dhcpn->ciaddr, dhcpn->dhcps->dev);
-    } else {
-        dhcpn->ciaddr = *ciaddr;
-    }
-
+    dhcp_negotiation_set_ciaddr(dhcpn);
     pico_tree_insert(&DHCPNegotiations, dhcpn);
     return dhcpn;
 }
@@ -253,21 +276,75 @@
     if (r < 0)
         dhcps_dbg("DHCP server WARNING: failure sending: %s!\n", strerror(pico_err));
 
+    PICO_FREE(hdr);
+
     return;
 }
 
-static void pico_dhcp_server_recv(struct pico_socket *s, uint8_t *buf, uint32_t len)
+static inline void parse_opt_msgtype(struct pico_dhcp_opt *opt, uint8_t *msgtype)
+{
+    if (opt->code == PICO_DHCP_OPT_MSGTYPE) {
+        *msgtype = opt->ext.msg_type.type;
+        dhcps_dbg("DHCP server: message type %u\n", msgtype);
+    }
+}
+
+static inline void parse_opt_reqip(struct pico_dhcp_opt *opt, struct pico_ip4 *reqip)
+{
+    if (opt->code == PICO_DHCP_OPT_REQIP)
+        reqip->addr = opt->ext.req_ip.ip.addr;
+}
+
+static inline void parse_opt_serverid(struct pico_dhcp_opt *opt,  struct pico_ip4 *serverid)
+{
+    if (opt->code == PICO_DHCP_OPT_SERVERID)
+        *serverid = opt->ext.server_id.ip;
+}
+
+static inline void dhcps_make_reply_to_request_msg(struct pico_dhcp_server_negotiation *dhcpn, int bound_valid_flag)
 {
+    if ((dhcpn->state == PICO_DHCP_STATE_BOUND) && bound_valid_flag)
+        dhcpd_make_reply(dhcpn, PICO_DHCP_MSG_ACK);
+
+    if (dhcpn->state == PICO_DHCP_STATE_OFFER) {
+        dhcpn->state = PICO_DHCP_STATE_BOUND;
+        dhcpd_make_reply(dhcpn, PICO_DHCP_MSG_ACK);
+    }
+}
+
+static inline void dhcps_make_reply_to_discover_or_request(struct pico_dhcp_server_negotiation *dhcpn,  uint8_t msgtype, int bound_valid_flag)
+{
+    if (PICO_DHCP_MSG_DISCOVER == msgtype) {
+        dhcpd_make_reply(dhcpn, PICO_DHCP_MSG_OFFER);
+        dhcpn->state = PICO_DHCP_STATE_OFFER;
+    } else if (PICO_DHCP_MSG_REQUEST == msgtype) {
+        dhcps_make_reply_to_request_msg(dhcpn, bound_valid_flag);
+    }
+}
+
+static inline void dhcps_parse_options_loop(struct pico_dhcp_server_negotiation *dhcpn, struct pico_dhcp_hdr *hdr)
+{
+    struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)hdr->options;
     uint8_t msgtype = 0;
-    int32_t optlen = (int32_t)(len - sizeof(struct pico_dhcp_hdr));
-    struct pico_dhcp_hdr *hdr = (struct pico_dhcp_hdr *)buf;
-    struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)hdr->options;
-    struct pico_dhcp_server_negotiation *dhcpn = NULL;
     struct pico_ip4 reqip = {
         0
     }, server_id = {
         0
     };
+
+    do {
+        parse_opt_msgtype(opt, &msgtype);
+        parse_opt_reqip(opt, &reqip);
+        parse_opt_serverid(opt, &server_id);
+    } while (pico_dhcp_next_option(&opt));
+    dhcps_make_reply_to_discover_or_request(dhcpn, msgtype, (!reqip.addr) && (!server_id.addr) && (hdr->ciaddr == dhcpn->ciaddr.addr));
+}
+
+static void pico_dhcp_server_recv(struct pico_socket *s, uint8_t *buf, uint32_t len)
+{
+    int32_t optlen = (int32_t)(len - sizeof(struct pico_dhcp_hdr));
+    struct pico_dhcp_hdr *hdr = (struct pico_dhcp_hdr *)buf;
+    struct pico_dhcp_server_negotiation *dhcpn = NULL;
     struct pico_device *dev = NULL;
 
     if (!pico_dhcp_are_options_valid(hdr->options, optlen))
@@ -281,59 +358,8 @@
     if (!ip_address_is_in_dhcp_range(dhcpn, dhcpn->ciaddr.addr))
         return;
 
-    do {
-        switch (opt->code)
-        {
-        case PICO_DHCP_OPT_PAD:
-            break;
-
-        case PICO_DHCP_OPT_END:
-            break;
-
-        case PICO_DHCP_OPT_MSGTYPE:
-            msgtype = opt->ext.msg_type.type;
-            dhcps_dbg("DHCP server: message type %u\n", msgtype);
-            break;
-
-        case PICO_DHCP_OPT_REQIP:
-            reqip = opt->ext.req_ip.ip;
-            dhcps_dbg("DHCP server: requested IP %08X\n", reqip.addr);
-            break;
-
-        case PICO_DHCP_OPT_SERVERID:
-            server_id = opt->ext.server_id.ip;
-            dhcps_dbg("DHCP server: server ID %08X\n", server_id.addr);
-            break;
+    dhcps_parse_options_loop(dhcpn, hdr);
 
-        default:
-            dhcps_dbg("DHCP server WARNING: unsupported option %u\n", opt->code);
-            break;
-        }
-    } while (pico_dhcp_next_option(&opt));
-
-    switch (msgtype)
-    {
-    case PICO_DHCP_MSG_DISCOVER:
-        dhcpd_make_reply(dhcpn, PICO_DHCP_MSG_OFFER);
-        dhcpn->state = PICO_DHCP_STATE_OFFER;
-        break;
-
-    case PICO_DHCP_MSG_REQUEST:
-        if ((dhcpn->state == PICO_DHCP_STATE_BOUND) && (!reqip.addr) && (!server_id.addr) && (hdr->ciaddr == dhcpn->ciaddr.addr))
-            dhcpd_make_reply(dhcpn, PICO_DHCP_MSG_ACK);
-
-        if (dhcpn->state == PICO_DHCP_STATE_OFFER) {
-            dhcpn->state = PICO_DHCP_STATE_BOUND;
-            dhcpd_make_reply(dhcpn, PICO_DHCP_MSG_ACK);
-        }
-
-        break;
-
-    default:
-        dhcps_dbg("DHCP server WARNING: unsupported message type %u\n", msgtype);
-        break;
-    }
-    return;
 }
 
 static void pico_dhcpd_wakeup(uint16_t ev, struct pico_socket *s)
--- a/modules/pico_dhcp_server.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_dhcp_server.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,10 +1,12 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
  *********************************************************************/
 #ifndef INCLUDE_PICO_DHCP_SERVER
 #define INCLUDE_PICO_DHCP_SERVER
+#include "pico_defines.h"
+#ifdef PICO_SUPPORT_UDP
 
 #include "pico_dhcp_common.h"
 #include "pico_addressing.h"
@@ -29,3 +31,4 @@
 int pico_dhcp_server_destroy(struct pico_device *dev);
 
 #endif /* _INCLUDE_PICO_DHCP_SERVER */
+#endif
--- a/modules/pico_dns_client.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_dns_client.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,9 +1,7 @@
 /*********************************************************************
    PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
    See LICENSE and COPYING for usage.
-
    .
-
    Authors: Kristof Roelants
  *********************************************************************/
 #include "pico_config.h"
@@ -13,111 +11,27 @@
 #include "pico_ipv4.h"
 #include "pico_ipv6.h"
 #include "pico_dns_client.h"
+#include "pico_dns_common.h"
 #include "pico_tree.h"
 
 #ifdef PICO_SUPPORT_DNS_CLIENT
 
+#ifdef PICO_SUPPORT_IPV4
+
 #define dns_dbg(...) do {} while(0)
 /* #define dns_dbg dbg */
 
 /* DNS response length */
-#define PICO_DNS_MAX_RESPONSE_LEN 256
+#define PICO_DNS_MAX_QUERY_LEN 255
+#define PICO_DNS_MAX_QUERY_LABEL_LEN 63
 
 /* DNS client retransmission time (msec) + frequency */
 #define PICO_DNS_CLIENT_RETRANS 4000
 #define PICO_DNS_CLIENT_MAX_RETRANS 3
 
-/* Default nameservers + port */
-#define PICO_DNS_NS_DEFAULT "208.67.222.222"
-#define PICO_DNS_NS_PORT 53
-
-/* 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
-
-/* QTYPE values */
-#define PICO_DNS_TYPE_A 1
-#define PICO_DNS_TYPE_AAAA 28
-#define PICO_DNS_TYPE_PTR 12
-
-/* QCLASS values */
-#define PICO_DNS_CLASS_IN 1
-
-/* 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
-
 static void pico_dns_client_callback(uint16_t ev, struct pico_socket *s);
 static void pico_dns_client_retransmission(pico_time now, void *arg);
-
-/* RFC 1035 section 4. MESSAGES */
-PACKED_STRUCT_DEF pico_dns_name
-{
-    char name[1];
-};
-
-/* prefix = header + name pointer
- * flags splitted in 2x uint8 due to endianness */
-PACKED_STRUCT_DEF pico_dns_prefix
-{
-    uint16_t 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  */
-    uint8_t rcode : 4; /* response code */
-    uint8_t z : 3; /* zero */
-    uint8_t ra : 1; /* recursion available  */
-    uint16_t qdcount;
-    uint16_t ancount;
-    uint16_t nscount;
-    uint16_t arcount;
-    struct pico_dns_name domain;
-};
-
-PACKED_STRUCT_DEF pico_dns_query_suffix
-{
-    uint16_t qtype;
-    uint16_t qclass;
-};
-
-PACKED_STRUCT_DEF pico_dns_answer_suffix
-{
-    uint16_t qtype;
-    uint16_t qclass;
-    uint32_t ttl;
-    uint16_t rdlength;
-    uint8_t rdata[];
-};
+static int pico_dns_client_getaddr_init(const char *url, uint16_t proto, void (*callback)(char *, void *), void *arg);
 
 struct pico_dns_ns
 {
@@ -127,10 +41,7 @@
 static int dns_ns_cmp(void *ka, void *kb)
 {
     struct pico_dns_ns *a = ka, *b = kb;
-    if (a->ns.addr == b->ns.addr)
-        return 0;
-
-    return (a->ns.addr < b->ns.addr) ? (-1) : (1);
+    return pico_ipv4_compare(&a->ns, &b->ns);
 }
 PICO_TREE_DECLARE(NSTable, dns_ns_cmp);
 
@@ -222,7 +133,7 @@
     return *nxtdns;
 }
 
-static struct pico_dns_query *pico_dns_client_add_query(struct pico_dns_prefix *hdr, uint16_t len, struct pico_dns_query_suffix *suffix,
+static struct pico_dns_query *pico_dns_client_add_query(struct pico_dns_header *hdr, uint16_t len, struct pico_dns_question_suffix *suffix,
                                                         void (*callback)(char *, void *), void *arg)
 {
     struct pico_dns_query *q = NULL, *found = NULL;
@@ -289,21 +200,6 @@
         return NULL;
 }
 
-/* determine len of string */
-static uint16_t pico_dns_client_strlen(const char *url)
-{
-    uint16_t len;
-
-    if (!url)
-        return 0;
-
-    for (len = 0; len < 0xFFFF; len++) {
-        if (url[len] == 0)
-            break;
-    }
-    return len;
-}
-
 /* seek end of string */
 static char *pico_dns_client_seek(char *ptr)
 {
@@ -315,42 +211,6 @@
     return ptr + 1;
 }
 
-/* mirror ip6 */
-
-/* mirror ip address numbers
- * f.e. 192.168.0.1 => 1.0.168.192 */
-static int8_t pico_dns_client_mirror(char *ptr)
-{
-    const unsigned char *addr = NULL;
-    char *m = ptr;
-    uint32_t ip = 0;
-    int8_t i = 0;
-
-    if (pico_string_to_ipv4(ptr, &ip) < 0)
-        return -1;
-
-    ptr = m;
-    addr = (unsigned char *)&ip;
-    for (i = 3; i >= 0; i--) {
-        if (addr[i] > 99) {
-            *ptr++ = (char)('0' + (addr[i] / 100));
-            *ptr++ = (char)('0' + ((addr[i] % 100) / 10));
-            *ptr++ = (char)('0' + ((addr[i] % 100) % 10));
-        } else if(addr[i] > 9) {
-            *ptr++ = (char)('0' + (addr[i] / 10));
-            *ptr++ = (char)('0' + (addr[i] % 10));
-        } else {
-            *ptr++ = (char)('0' + addr[i]);
-        }
-
-        if(i > 0)
-            *ptr++ = '.';
-    }
-    *ptr = '\0';
-
-    return 0;
-}
-
 static struct pico_dns_query *pico_dns_client_idcheck(uint16_t id)
 {
     struct pico_dns_query test = {
@@ -361,7 +221,7 @@
     return pico_tree_findKey(&DNSTable, &test);
 }
 
-static int pico_dns_client_query_prefix(struct pico_dns_prefix *pre)
+static int pico_dns_client_query_header(struct pico_dns_header *hdr)
 {
     uint16_t id = 0;
     uint8_t retry = 32;
@@ -373,73 +233,13 @@
     if (!retry)
         return -1;
 
-    pre->id = short_be(id);
-    pre->qr = PICO_DNS_QR_QUERY;
-    pre->opcode = PICO_DNS_OPCODE_QUERY;
-    pre->aa = PICO_DNS_AA_NO_AUTHORITY;
-    pre->tc = PICO_DNS_TC_NO_TRUNCATION;
-    pre->rd = PICO_DNS_RD_IS_DESIRED;
-    pre->ra = PICO_DNS_RA_NO_SUPPORT;
-    pre->z = 0;
-    pre->rcode = PICO_DNS_RCODE_NO_ERROR;
-    pre->qdcount = short_be(1);
-    pre->ancount = short_be(0);
-    pre->nscount = short_be(0);
-    pre->arcount = short_be(0);
+    hdr->id = short_be(id);
+    pico_dns_fill_packet_header(hdr, 1, 0, 0, 0); /* 1 question, 0 answers */
 
     return 0;
 }
 
-static int pico_dns_client_query_suffix(struct pico_dns_query_suffix *suf, uint16_t type, uint16_t class)
-{
-    suf->qtype = short_be(type);
-    suf->qclass = short_be(class);
-    return 0;
-}
-
-/* replace '.' in the domain name by the label length
- * f.e. www.google.be => 3www6google2be0 */
-static int pico_dns_client_query_domain(char *ptr)
-{
-    char p = 0, *label = NULL;
-    uint8_t len = 0;
-
-    if (!ptr)
-        return -1;
-
-    label = ptr++;
-    while ((p = *ptr++) != 0) {
-        if (p == '.') {
-            *label = (char)len;
-            label = ptr - 1;
-            len = 0;
-        } else {
-            len++;
-        }
-    }
-    *label = (char)len;
-    return 0;
-}
-
-/* replace the label length in the domain name by '.'
- * f.e. 3www6google2be0 => .www.google.be */
-static int pico_dns_client_answer_domain(char *ptr)
-{
-    char p = 0, *label = NULL;
-
-    if (!ptr)
-        return -1;
-
-    label = ptr;
-    while ((p = *ptr++) != 0) {
-        ptr += p;
-        *label = '.';
-        label = ptr;
-    }
-    return 0;
-}
-
-static int pico_dns_client_check_prefix(struct pico_dns_prefix *pre)
+static int pico_dns_client_check_header(struct pico_dns_header *pre)
 {
     if (pre->qr != PICO_DNS_QR_RESPONSE || pre->opcode != PICO_DNS_OPCODE_QUERY || pre->rcode != PICO_DNS_RCODE_NO_ERROR) {
         dns_dbg("DNS ERROR: OPCODE %d | TC %d | RCODE %d\n", pre->opcode, pre->tc, pre->rcode);
@@ -454,8 +254,11 @@
     return 0;
 }
 
-static int pico_dns_client_check_qsuffix(struct pico_dns_query_suffix *suf, struct pico_dns_query *q)
+static int pico_dns_client_check_qsuffix(struct pico_dns_question_suffix *suf, struct pico_dns_query *q)
 {
+    if (!suf)
+        return -1;
+
     if (short_be(suf->qtype) != q->qtype || short_be(suf->qclass) != q->qclass) {
         dns_dbg("DNS ERROR: received qtype (%u) or qclass (%u) incorrect\n", short_be(suf->qtype), short_be(suf->qclass));
         return -1;
@@ -464,52 +267,62 @@
     return 0;
 }
 
-static int pico_dns_client_check_asuffix(struct pico_dns_answer_suffix *suf, struct pico_dns_query *q)
+static int pico_dns_client_check_url(struct pico_dns_header *resp, struct pico_dns_query *q)
+{
+    char *recv_name = (char*)(resp) + sizeof(struct pico_dns_header) + PICO_DNS_LABEL_INITIAL;
+    char *exp_name = (char *)(q->query) + sizeof(struct pico_dns_header) + PICO_DNS_LABEL_INITIAL;
+    if (strcasecmp(recv_name,  exp_name) != 0)
+        return -1;
+
+    return 0;
+}
+
+static int pico_dns_client_check_asuffix(struct pico_dns_record_suffix *suf, struct pico_dns_query *q)
 {
     if (!suf) {
         pico_err = PICO_ERR_EINVAL;
         return -1;
     }
 
-    if (short_be(suf->qtype) != q->qtype || short_be(suf->qclass) != q->qclass) {
-        dns_dbg("DNS WARNING: received qtype (%u) or qclass (%u) incorrect\n", short_be(suf->qtype), short_be(suf->qclass));
+    if (short_be(suf->rtype) != q->qtype || short_be(suf->rclass) != q->qclass) {
+        dns_dbg("DNS WARNING: received qtype (%u) or qclass (%u) incorrect\n", short_be(suf->rtype), short_be(suf->rclass));
         return -1;
     }
 
-    if (long_be(suf->ttl) > PICO_DNS_MAX_TTL) {
-        dns_dbg("DNS WARNING: received TTL (%u) > MAX (%u)\n", short_be(suf->ttl), PICO_DNS_MAX_TTL);
+    if (long_be(suf->rttl) > PICO_DNS_MAX_TTL) {
+        dns_dbg("DNS WARNING: received TTL (%u) > MAX (%u)\n", short_be(suf->rttl), PICO_DNS_MAX_TTL);
         return -1;
     }
 
     return 0;
 }
 
-static char *pico_dns_client_seek_suffix(char *suf, struct pico_dns_prefix *pre, struct pico_dns_query *q)
+static char *pico_dns_client_seek_suffix(char *suf, struct pico_dns_header *pre, struct pico_dns_query *q)
 {
-    struct pico_dns_answer_suffix *asuffix = NULL;
+    struct pico_dns_record_suffix *asuffix = NULL;
     uint16_t comp = 0, compression = 0;
     uint16_t i = 0;
-    char *psuffix = suf;
+
     if (!suf)
         return NULL;
 
     while (i++ < short_be(pre->ancount)) {
-        comp = short_from(psuffix);
+        comp = short_from(suf);
         compression = short_be(comp);
         switch (compression >> 14)
         {
         case PICO_DNS_POINTER:
             while (compression >> 14 == PICO_DNS_POINTER) {
                 dns_dbg("DNS: pointer\n");
-                psuffix += sizeof(uint16_t);
-                comp = short_from(psuffix);
+                suf += sizeof(uint16_t);
+                comp = short_from(suf);
                 compression = short_be(comp);
             }
             break;
 
         case PICO_DNS_LABEL:
             dns_dbg("DNS: label\n");
-            psuffix = pico_dns_client_seek(psuffix);
+            suf = pico_dns_client_seek(suf);
             break;
 
         default:
@@ -517,16 +330,16 @@
             return NULL;
         }
 
-        asuffix = (struct pico_dns_answer_suffix *)psuffix;
+        asuffix = (struct pico_dns_record_suffix *)suf;
         if (!asuffix)
             break;
 
         if (pico_dns_client_check_asuffix(asuffix, q) < 0) {
-            psuffix += (sizeof(struct pico_dns_answer_suffix) + short_be(asuffix->rdlength));
+            suf += (sizeof(struct pico_dns_record_suffix) + short_be(asuffix->rdlength));
             continue;
         }
 
-        return psuffix;
+        return suf;
     }
     return NULL;
 }
@@ -576,7 +389,8 @@
         return;
     }
 
-    if (q->retrans++ <= PICO_DNS_CLIENT_MAX_RETRANS) {
+    q->retrans++;
+    if (q->retrans <= PICO_DNS_CLIENT_MAX_RETRANS) {
         q->q_ns = pico_dns_client_next_ns(&q->q_ns.ns);
         pico_dns_client_send(q);
     } else {
@@ -586,15 +400,16 @@
     }
 }
 
-static int pico_dns_client_user_callback(struct pico_dns_answer_suffix *asuffix, struct pico_dns_query *q)
+static int pico_dns_client_user_callback(struct pico_dns_record_suffix *asuffix, struct pico_dns_query *q)
 {
     uint32_t ip = 0;
     char *str = NULL;
+    char *rdata = (char *) asuffix + sizeof(struct pico_dns_record_suffix);
 
     switch (q->qtype)
     {
     case PICO_DNS_TYPE_A:
-        ip = long_from(asuffix->rdata);
+        ip = long_from(rdata);
         str = PICO_ZALLOC(PICO_DNS_IPV4_ADDR_LEN);
         pico_ipv4_to_string(str, ip);
         break;
@@ -602,21 +417,23 @@
     case PICO_DNS_TYPE_AAAA:
     {
         struct pico_ip6 ip6;
-        memcpy(&ip6.addr, asuffix->rdata, sizeof(struct pico_ip6));
+        memcpy(&ip6.addr, rdata, sizeof(struct pico_ip6));
         str = PICO_ZALLOC(PICO_DNS_IPV6_ADDR_LEN);
         pico_ipv6_to_string(str, ip6.addr);
         break;
     }
 #endif
     case PICO_DNS_TYPE_PTR:
-        pico_dns_client_answer_domain((char *)asuffix->rdata);
-        str = PICO_ZALLOC((size_t)(asuffix->rdlength - PICO_DNS_LABEL_INITIAL));
+        /* TODO: check for decompression / rdlength vs. decompressed length */
+        pico_dns_notation_to_name(rdata, short_be(asuffix->rdlength));
+        str = PICO_ZALLOC((size_t)(short_be(asuffix->rdlength) -
+                                   PICO_DNS_LABEL_INITIAL));
         if (!str) {
             pico_err = PICO_ERR_ENOMEM;
             return -1;
         }
 
-        memcpy(str, asuffix->rdata + PICO_DNS_LABEL_INITIAL, short_be(asuffix->rdlength) - PICO_DNS_LABEL_INITIAL);
+        memcpy(str, rdata + PICO_DNS_LABEL_INITIAL, short_be(asuffix->rdlength) - PICO_DNS_LABEL_INITIAL);
         break;
 
     default:
@@ -627,24 +444,71 @@
     if (q->retrans) {
         q->callback(str, q->arg);
         q->retrans = 0;
-        PICO_FREE(str);
         pico_dns_client_del_query(q->id);
     }
 
+    if (str)
+        PICO_FREE(str);
+
     return 0;
 }
 
+static char dns_response[PICO_IP_MRU] = {
+    0
+};
+
+static void pico_dns_try_fallback_cname(struct pico_dns_query *q, struct pico_dns_header *h, struct pico_dns_question_suffix *qsuffix)
+{
+    uint16_t type = q->qtype;
+    uint16_t proto = PICO_PROTO_IPV4;
+    struct pico_dns_record_suffix *asuffix = NULL;
+    char *p_asuffix = NULL;
+    char *cname_orig = NULL;
+    char *cname = NULL;
+    uint16_t cname_len;
+
+    /* Try to use CNAME only if A or AAAA query is ongoing */
+    if (type != PICO_DNS_TYPE_A && type != PICO_DNS_TYPE_AAAA)
+        return;
+
+    if (type == PICO_DNS_TYPE_AAAA)
+        proto = PICO_PROTO_IPV6;
+
+    q->qtype = PICO_DNS_TYPE_CNAME;
+    p_asuffix = (char *)qsuffix + sizeof(struct pico_dns_question_suffix);
+    p_asuffix = pico_dns_client_seek_suffix(p_asuffix, h, q);
+    if (!p_asuffix) {
+        return;
+    }
+
+    /* Found CNAME response. Re-initiating query. */
+    asuffix = (struct pico_dns_record_suffix *)p_asuffix;
+    cname = pico_dns_decompress_name((char *)asuffix + sizeof(struct pico_dns_record_suffix), (pico_dns_packet *)h); /* allocates memory! */
+    cname_orig = cname; /* to free later */
+
+    if (cname == NULL)
+        return;
+
+    cname_len = (uint16_t)(pico_dns_strlen(cname) + 1);
+
+    pico_dns_notation_to_name(cname, cname_len);
+    if (cname[0] == '.')
+        cname++;
+
+    dns_dbg("Restarting query for name '%s'\n", cname);
+    pico_dns_client_getaddr_init(cname, proto, q->callback, q->arg);
+    PICO_FREE(cname_orig);
+    pico_dns_client_del_query(q->id);
+}
+
 static void pico_dns_client_callback(uint16_t ev, struct pico_socket *s)
 {
-    struct pico_dns_prefix *prefix = NULL;
-    struct pico_dns_name *domain = NULL;
-    struct pico_dns_query_suffix *qsuffix = NULL;
-    struct pico_dns_answer_suffix *asuffix = NULL;
+    struct pico_dns_header *header = NULL;
+    char *domain;
+    struct pico_dns_question_suffix *qsuffix = NULL;
+    struct pico_dns_record_suffix *asuffix = NULL;
     struct pico_dns_query *q = NULL;
     char *p_asuffix = NULL;
-    char msg[PICO_DNS_MAX_RESPONSE_LEN] = {
-        0
-    };
 
     if (ev == PICO_SOCK_EV_ERR) {
         dns_dbg("DNS: socket error received\n");
@@ -652,79 +516,172 @@
     }
 
     if (ev & PICO_SOCK_EV_RD) {
-        if (pico_socket_read(s, msg, PICO_DNS_MAX_RESPONSE_LEN) <= 0)
+        if (pico_socket_read(s, dns_response, PICO_IP_MRU) < 0)
             return;
     }
 
-    prefix = (struct pico_dns_prefix *)msg;
-    domain = &prefix->domain;
-    qsuffix = (struct pico_dns_query_suffix *)pico_dns_client_seek(domain->name);
+    header = (struct pico_dns_header *)dns_response;
+    domain = (char *)header + sizeof(struct pico_dns_header);
+    qsuffix = (struct pico_dns_question_suffix *)pico_dns_client_seek(domain);
     /* valid asuffix is determined dynamically later on */
 
-    if (pico_dns_client_check_prefix(prefix) < 0)
+    if (pico_dns_client_check_header(header) < 0)
         return;
 
-    q = pico_dns_client_find_query(short_be(prefix->id));
+    q = pico_dns_client_find_query(short_be(header->id));
     if (!q)
         return;
 
     if (pico_dns_client_check_qsuffix(qsuffix, q) < 0)
         return;
 
-    p_asuffix = (char *)qsuffix + sizeof(struct pico_dns_query_suffix);
-    p_asuffix = pico_dns_client_seek_suffix(p_asuffix, prefix, q);
-    if (!p_asuffix)
+    if (pico_dns_client_check_url(header, q) < 0)
         return;
 
-    asuffix = (struct pico_dns_answer_suffix *)p_asuffix;
+    p_asuffix = (char *)qsuffix + sizeof(struct pico_dns_question_suffix);
+    p_asuffix = pico_dns_client_seek_suffix(p_asuffix, header, q);
+    if (!p_asuffix) {
+        pico_dns_try_fallback_cname(q, header, qsuffix);
+        return;
+    }
+
+    asuffix = (struct pico_dns_record_suffix *)p_asuffix;
     pico_dns_client_user_callback(asuffix, q);
 
     return;
 }
 
-static int pico_dns_client_getaddr_init(const char *url, uint16_t proto, void (*callback)(char *, void *), void *arg)
+static int pico_dns_create_message(struct pico_dns_header **header, struct pico_dns_question_suffix **qsuffix, enum pico_dns_arpa arpa, const char *url, uint16_t *urlen, uint16_t *hdrlen)
 {
-    char *msg = NULL;
-    struct pico_dns_prefix *prefix = NULL;
-    struct pico_dns_name *domain = NULL;
-    struct pico_dns_query_suffix *qsuffix = NULL;
-    struct pico_dns_query *q = NULL;
-    uint16_t len = 0, lblen = 0, strlen = 0;
-    (void)proto;
+    char *domain;
+    char inaddr_arpa[14];
+    uint16_t strlen = 0, arpalen = 0;
+
+    if (!url) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    if(arpa == PICO_DNS_ARPA4) {
+        strcpy(inaddr_arpa, ".in-addr.arpa");
+        strlen = pico_dns_strlen(url);
+    }
+
+#ifdef PICO_SUPPORT_IPV6
+    else if (arpa == PICO_DNS_ARPA6) {
+        strcpy(inaddr_arpa, ".IP6.ARPA");
+        strlen = STRLEN_PTR_IP6;
+    }
+#endif
+    else {
+        strcpy(inaddr_arpa, "");
+        strlen = pico_dns_strlen(url);
+    }
+
+    arpalen = pico_dns_strlen(inaddr_arpa);
+    *urlen = (uint16_t)(PICO_DNS_LABEL_INITIAL + strlen + arpalen + PICO_DNS_LABEL_ROOT);
+    *hdrlen = (uint16_t)(sizeof(struct pico_dns_header) + *urlen + sizeof(struct pico_dns_question_suffix));
+    *header = PICO_ZALLOC(*hdrlen);
+    if (!*header) {
+        pico_err = PICO_ERR_ENOMEM;
+        return -1;
+    }
+
+    *header = (struct pico_dns_header *)*header;
+    domain = (char *) *header + sizeof(struct pico_dns_header);
+    *qsuffix = (struct pico_dns_question_suffix *)(domain + *urlen);
+
+    if(arpa == PICO_DNS_ARPA4) {
+        memcpy(domain + PICO_DNS_LABEL_INITIAL, url, strlen);
+        pico_dns_mirror_addr(domain + PICO_DNS_LABEL_INITIAL);
+        memcpy(domain + PICO_DNS_LABEL_INITIAL + strlen, inaddr_arpa, arpalen);
+    }
 
+#ifdef PICO_SUPPORT_IPV6
+    else if (arpa == PICO_DNS_ARPA6) {
+        pico_dns_ipv6_set_ptr(url, domain + PICO_DNS_LABEL_INITIAL);
+        memcpy(domain + PICO_DNS_LABEL_INITIAL + STRLEN_PTR_IP6, inaddr_arpa, arpalen);
+    }
+#endif
+    else {
+        memcpy(domain + PICO_DNS_LABEL_INITIAL, url, strlen);
+    }
+
+    /* assemble dns message */
+    pico_dns_client_query_header(*header);
+    pico_dns_name_to_dns_notation(domain, strlen);
+
+    return 0;
+}
+
+static int pico_dns_client_addr_label_check_len(const char *url)
+{
+    const char *p, *label;
+    int count;
+    label = url;
+    p = label;
+
+    while(*p != (char) 0) {
+        count = 0;
+        while((*p != (char)0)) {
+            if (*p == '.') {
+                label = ++p;
+                break;
+            }
+
+            count++;
+            p++;
+            if (count > PICO_DNS_MAX_QUERY_LABEL_LEN)
+                return -1;
+        }
+    }
+    return 0;
+}
+
+static int pico_dns_client_getaddr_check(const char *url, void (*callback)(char *, void *))
+{
     if (!url || !callback) {
         pico_err = PICO_ERR_EINVAL;
         return -1;
     }
 
-    strlen = pico_dns_client_strlen(url);
-    lblen = (uint16_t)(PICO_DNS_LABEL_INITIAL + strlen + PICO_DNS_LABEL_ROOT);
-    len = (uint16_t)(sizeof(struct pico_dns_prefix) + lblen + sizeof(struct pico_dns_query_suffix));
-    msg = PICO_ZALLOC(len);
-    if (!msg) {
-        pico_err = PICO_ERR_ENOMEM;
+    if (strlen(url) > PICO_DNS_MAX_QUERY_LEN) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    }
+
+    if (pico_dns_client_addr_label_check_len(url) < 0) {
+        pico_err = PICO_ERR_EINVAL;
         return -1;
     }
 
-    prefix = (struct pico_dns_prefix *)msg;
-    domain = &prefix->domain;
-    qsuffix = (struct pico_dns_query_suffix *)(domain->name + lblen);
-    memcpy(domain->name + PICO_DNS_LABEL_INITIAL, url, strlen);
+    return 0;
+}
 
-    /* assemble dns message */
-    pico_dns_client_query_prefix(prefix);
-    pico_dns_client_query_domain(domain->name);
+static int pico_dns_client_getaddr_init(const char *url, uint16_t proto, void (*callback)(char *, void *), void *arg)
+{
+    struct pico_dns_header *header = NULL;
+    struct pico_dns_question_suffix *qsuffix = NULL;
+    struct pico_dns_query *q = NULL;
+    uint16_t len = 0, lblen = 0;
+    (void)proto;
+
+    if (pico_dns_client_getaddr_check(url, callback) < 0)
+        return -1;
+
+    if(pico_dns_create_message(&header, &qsuffix, PICO_DNS_NO_ARPA, url, &lblen, &len) != 0)
+        return -1;
 
 #ifdef PICO_SUPPORT_IPV6
     if (proto == PICO_PROTO_IPV6) {
-        pico_dns_client_query_suffix(qsuffix, PICO_DNS_TYPE_AAAA, PICO_DNS_CLASS_IN);
+        pico_dns_question_fill_suffix(qsuffix, PICO_DNS_TYPE_AAAA, PICO_DNS_CLASS_IN);
     } else
 #endif
-    pico_dns_client_query_suffix(qsuffix, PICO_DNS_TYPE_A, PICO_DNS_CLASS_IN);
+    pico_dns_question_fill_suffix(qsuffix, PICO_DNS_TYPE_A, PICO_DNS_CLASS_IN);
 
-    q = pico_dns_client_add_query(prefix, len, qsuffix, callback, arg);
+    q = pico_dns_client_add_query(header, len, qsuffix, callback, arg);
     if (!q) {
-        PICO_FREE(msg);
+        PICO_FREE(header);
         return -1;
     }
 
@@ -746,137 +703,46 @@
     return pico_dns_client_getaddr_init(url, PICO_PROTO_IPV6, callback, arg);
 }
 
-int pico_dns_client_getname(const char *ip, void (*callback)(char *, void *), void *arg)
+static int pico_dns_getname_univ(const char *ip, void (*callback)(char *, void *), void *arg, enum pico_dns_arpa arpa)
 {
-    const char *inaddr_arpa = ".in-addr.arpa";
-    char *msg = NULL;
-    struct pico_dns_prefix *prefix = NULL;
-    struct pico_dns_name *domain = NULL;
-    struct pico_dns_query_suffix *qsuffix = NULL;
+    struct pico_dns_header *header = NULL;
+    struct pico_dns_question_suffix *qsuffix = NULL;
     struct pico_dns_query *q = NULL;
-    uint16_t len = 0, lblen = 0, strlen = 0, arpalen = 0;
+    uint16_t len = 0, lblen = 0;
 
     if (!ip || !callback) {
         pico_err = PICO_ERR_EINVAL;
         return -1;
     }
 
-    strlen = pico_dns_client_strlen(ip);
-    arpalen = pico_dns_client_strlen(inaddr_arpa);
-    lblen = (uint16_t)(PICO_DNS_LABEL_INITIAL + strlen + arpalen + PICO_DNS_LABEL_ROOT);
-    len = (uint16_t)(sizeof(struct pico_dns_prefix) + lblen + sizeof(struct pico_dns_query_suffix));
-    msg = PICO_ZALLOC(len);
-    if (!msg) {
-        pico_err = PICO_ERR_ENOMEM;
+    if(pico_dns_create_message(&header, &qsuffix, arpa, ip, &lblen, &len) != 0)
         return -1;
-    }
 
-    prefix = (struct pico_dns_prefix *)msg;
-    domain = &prefix->domain;
-    qsuffix = (struct pico_dns_query_suffix *)(prefix->domain.name + lblen);
-    memcpy(domain->name + PICO_DNS_LABEL_INITIAL, ip, strlen);
-    pico_dns_client_mirror(domain->name + PICO_DNS_LABEL_INITIAL);
-    memcpy(domain->name + PICO_DNS_LABEL_INITIAL + strlen, inaddr_arpa, arpalen);
-    /* assemble dns message */
-    pico_dns_client_query_prefix(prefix);
-    pico_dns_client_query_domain(domain->name);
-    pico_dns_client_query_suffix(qsuffix, PICO_DNS_TYPE_PTR, PICO_DNS_CLASS_IN);
-    q = pico_dns_client_add_query(prefix, len, qsuffix, callback, arg);
+    pico_dns_question_fill_suffix(qsuffix, PICO_DNS_TYPE_PTR, PICO_DNS_CLASS_IN);
+    q = pico_dns_client_add_query(header, len, qsuffix, callback, arg);
     if (!q) {
-        PICO_FREE(msg);
+        PICO_FREE(header);
         return -1;
     }
 
     if (pico_dns_client_send(q) < 0) {
-        pico_dns_client_del_query(q->id); /* frees msg */
+        pico_dns_client_del_query(q->id); /* frees header */
         return -1;
     }
 
     return 0;
 }
 
-
-#ifdef PICO_SUPPORT_IPV6
-#define STRLEN_PTR_IP6 63
-
-static inline char dns_ptr_ip6_nibble_lo(uint8_t byte)
+int pico_dns_client_getname(const char *ip, void (*callback)(char *, void *), void *arg)
 {
-    uint8_t nibble = byte & 0x0f;
-    if (nibble < 10)
-        return (char)(nibble + '0');
-    else
-        return (char)(nibble - 0xa + 'a');
+    return pico_dns_getname_univ(ip, callback, arg, PICO_DNS_ARPA4);
 }
 
-static inline char dns_ptr_ip6_nibble_hi(uint8_t byte)
-{
-    uint8_t nibble = (byte & 0xf0u) >> 4u;
-    if (nibble < 10u)
-        return (char)(nibble + '0');
-    else
-        return (char)(nibble - 0xa + 'a');
-}
-
-static void pico_dns_ipv6_set_ptr(const char *ip, char *dst)
-{
-    struct pico_ip6 ip6;
-    int i, j = 0;
-    pico_string_to_ipv6(ip, ip6.addr);
-    for (i = 15; i >= 0; i--) {
-        dst[j++] = dns_ptr_ip6_nibble_lo(ip6.addr[i]);
-        dst[j++] = '.';
-        dst[j++] = dns_ptr_ip6_nibble_hi(ip6.addr[i]);
-        dst[j++] = '.';
-    }
-}
 
 int pico_dns_client_getname6(const char *ip, void (*callback)(char *, void *), void *arg)
 {
-    const char *inaddr6_arpa = ".IP6.ARPA";
-    char *msg = NULL;
-    struct pico_dns_prefix *prefix = NULL;
-    struct pico_dns_name *domain = NULL;
-    struct pico_dns_query_suffix *qsuffix = NULL;
-    struct pico_dns_query *q = NULL;
-    uint16_t len = 0, lblen = 0, arpalen = 0;
-
-    if (!ip || !callback) {
-        pico_err = PICO_ERR_EINVAL;
-        return -1;
-    }
-
-    arpalen = pico_dns_client_strlen(inaddr6_arpa);
-    lblen = (uint16_t)(PICO_DNS_LABEL_INITIAL + STRLEN_PTR_IP6 + arpalen + PICO_DNS_LABEL_ROOT);
-    len = (uint16_t)(sizeof(struct pico_dns_prefix) + lblen + sizeof(struct pico_dns_query_suffix));
-    msg = PICO_ZALLOC(len);
-    if (!msg) {
-        pico_err = PICO_ERR_ENOMEM;
-        return -1;
-    }
-
-    prefix = (struct pico_dns_prefix *)msg;
-    domain = &prefix->domain;
-    qsuffix = (struct pico_dns_query_suffix *)(prefix->domain.name + lblen);
-    pico_dns_ipv6_set_ptr(ip, domain->name + PICO_DNS_LABEL_INITIAL);
-    memcpy(domain->name + PICO_DNS_LABEL_INITIAL + STRLEN_PTR_IP6, inaddr6_arpa, arpalen);
-    /* assemble dns message */
-    pico_dns_client_query_prefix(prefix);
-    pico_dns_client_query_domain(domain->name);
-    pico_dns_client_query_suffix(qsuffix, PICO_DNS_TYPE_PTR, PICO_DNS_CLASS_IN);
-    q = pico_dns_client_add_query(prefix, len, qsuffix, callback, arg);
-    if (!q) {
-        PICO_FREE(msg);
-        return -1;
-    }
-
-    if (pico_dns_client_send(q) < 0) {
-        pico_dns_client_del_query(q->id); /* frees msg */
-        return -1;
-    }
-
-    return 0;
+    return pico_dns_getname_univ(ip, callback, arg, PICO_DNS_ARPA6);
 }
-#endif
 
 int pico_dns_client_nameserver(struct pico_ip4 *ns, uint8_t flag)
 {
@@ -920,4 +786,15 @@
     return pico_dns_client_nameserver(&default_ns, PICO_DNS_NS_ADD);
 }
 
+#else
+
+int pico_dns_client_init(void)
+{
+    dbg("ERROR Trying to initialize DNS module: IPv4 not supported in this build.\n");
+    return -1;
+}
+#endif /* PICO_SUPPORT_IPV4 */
+
+
 #endif /* PICO_SUPPORT_DNS_CLIENT */
+
--- a/modules/pico_dns_client.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_dns_client.h	Mon Sep 28 13:16:18 2015 +0200
@@ -12,7 +12,26 @@
 
 #define PICO_DNS_NS_DEL 0
 #define PICO_DNS_NS_ADD 1
-#include <stdint.h>
+#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 */
--- a/modules/pico_icmp4.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_icmp4.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    .
@@ -47,6 +47,9 @@
 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) {
@@ -55,6 +58,15 @@
         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) {
@@ -96,21 +108,33 @@
     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, 8 + sizeof(struct pico_ipv4_hdr) + PICO_ICMPHDR_UN_SIZE);
+    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 = 8 + sizeof(struct pico_ipv4_hdr) +  PICO_ICMPHDR_UN_SIZE;
+    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, 8 + sizeof(struct pico_ipv4_hdr));
+    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;
@@ -140,6 +164,12 @@
     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*/
@@ -153,6 +183,11 @@
     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 */
 /***********************/
@@ -176,7 +211,6 @@
     int interval;
     int timeout;
     void (*cb)(struct pico_icmp4_stats*);
-
 };
 
 static int cookie_compare(void *ka, void *kb)
@@ -255,6 +289,9 @@
     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)
@@ -280,6 +317,9 @@
     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;
@@ -328,8 +368,28 @@
     pico_tree_insert(&Pings, cookie);
     send_ping(cookie);
 
-    return 0;
+    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
--- a/modules/pico_icmp4.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_icmp4.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    .
@@ -7,9 +7,11 @@
  *********************************************************************/
 #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 {
@@ -18,19 +20,19 @@
     uint16_t crc;
 
     /* hun */
-    union hun_u {
+    PACKED_UNION_DEF hun_u {
         uint8_t ih_pptr;
         struct pico_ip4 ih_gwaddr;
-        struct {
+        PEDANTIC_STRUCT_DEF ih_idseq_s {
             uint16_t idseq_id;
             uint16_t idseq_seq;
         } ih_idseq;
         uint32_t ih_void;
-        struct {
+        PEDANTIC_STRUCT_DEF ih_pmtu_s {
             uint16_t ipm_void;
             uint16_t ipm_nmtu;
         } ih_pmtu;
-        struct {
+        PEDANTIC_STRUCT_DEF ih_rta_s {
             uint8_t rta_numgw;
             uint8_t rta_wpa;
             uint16_t rta_lifetime;
@@ -38,18 +40,18 @@
     } hun;
 
     /* dun */
-    union dun_u {
-        struct {
+    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;
-        struct {
+        PEDANTIC_STRUCT_DEF id_ip_s {
             uint32_t ip_options;
             uint32_t ip_data_hi;
             uint32_t ip_data_lo;
         } id_ip;
-        struct {
+        PEDANTIC_STRUCT_DEF id_ra_s {
             uint32_t ira_addr;
             uint32_t ira_pref;
         } id_ra;
@@ -137,13 +139,24 @@
 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 */
 
-int pico_icmp4_ping(char *dst, int count, int interval, int timeout, int size, void (*cb)(struct pico_icmp4_stats *));
 #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
--- a/modules/pico_icmp6.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_icmp6.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    .
@@ -9,12 +9,13 @@
 #define _INCLUDE_PICO_ICMP6
 #include "pico_addressing.h"
 #include "pico_protocol.h"
-
+#include "pico_mld.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
@@ -56,8 +57,37 @@
 #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
@@ -67,6 +97,7 @@
 #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;
 
@@ -75,44 +106,44 @@
     uint8_t code;
     uint16_t crc;
 
-    union icmp6_msg_u {
+    PACKED_UNION_DEF icmp6_msg_u {
         /* error messages */
-        union icmp6_err_u {
-            struct {
+        PACKED_UNION_DEF icmp6_err_u {
+            PEDANTIC_STRUCT_DEF dest_unreach_s {
                 uint32_t unused;
                 uint8_t data[0];
             } dest_unreach;
-            struct {
+            PEDANTIC_STRUCT_DEF pkt_too_big_s {
                 uint32_t mtu;
                 uint8_t data[0];
             } pkt_too_big;
-            struct {
+            PEDANTIC_STRUCT_DEF time_exceeded_s {
                 uint32_t unused;
                 uint8_t data[0];
             } time_exceeded;
-            struct {
+            PEDANTIC_STRUCT_DEF param_problem_s {
                 uint32_t ptr;
                 uint8_t data[0];
             } param_problem;
         } err;
 
         /* informational messages */
-        union icmp6_info_u {
-            struct {
+        PACKED_UNION_DEF icmp6_info_u {
+            PEDANTIC_STRUCT_DEF echo_request_s {
                 uint16_t id;
                 uint16_t seq;
                 uint8_t data[0];
             } echo_request;
-            struct {
+            PEDANTIC_STRUCT_DEF echo_reply_s {
                 uint16_t id;
                 uint16_t seq;
                 uint8_t data[0];
             } echo_reply;
-            struct {
+            PEDANTIC_STRUCT_DEF router_sol_s {
                 uint32_t unused;
                 uint8_t options[0];
             } router_sol;
-            struct {
+            PEDANTIC_STRUCT_DEF router_adv_s {
                 uint8_t hop;
                 uint8_t mor;
                 uint16_t life_time;
@@ -120,22 +151,32 @@
                 uint32_t retrans_time;
                 uint8_t options[0];
             } router_adv;
-            struct {
+            PEDANTIC_STRUCT_DEF neigh_sol_s {
                 uint32_t unused;
                 struct pico_ip6 target;
                 uint8_t options[0];
             } neigh_sol;
-            struct {
+            PEDANTIC_STRUCT_DEF neigh_adv_s {
                 uint32_t rsor;
                 struct pico_ip6 target;
                 uint8_t options[0];
             } neigh_adv;
-            struct {
+            PEDANTIC_STRUCT_DEF redirect_s {
                 uint32_t reserved;
                 struct pico_ip6 target;
                 struct pico_ip6 dest;
                 uint8_t options[0];
             } redirect;
+            PEDANTIC_STRUCT_DEF mld_s { 
+                uint16_t max_resp_time;
+                uint16_t reserved;
+                struct pico_ip6 mmcast_group;
+                /*MLDv2*/
+                uint8_t reserverd; // With S and QRV
+                uint8_t QQIC;
+                uint16_t nbr_src;
+                struct pico_ip6 src[0];
+            } mld;          
         } info;
     } msg;
 };
@@ -144,7 +185,7 @@
 {
     uint8_t type;
     uint8_t len;
-    union icmp6_opt_hw_addr_u {
+    PACKED_UNION_DEF icmp6_opt_hw_addr_u {
         struct pico_eth mac;
     } addr;
 };
@@ -180,6 +221,15 @@
     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;
@@ -197,7 +247,8 @@
     struct pico_ip6 dst;
 };
 
-int pico_icmp6_ping(char *dst, int count, int interval, int timeout, int size, void (*cb)(struct pico_icmp6_stats *));
+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);
@@ -208,7 +259,11 @@
 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
--- a/modules/pico_igmp.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_igmp.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    RFC 1112, 2236, 3376, 3569, 3678, 4607
@@ -18,6 +18,8 @@
 #include "pico_device.h"
 #include "pico_socket.h"
 
+#if defined(PICO_SUPPORT_IGMP) && defined(PICO_SUPPORT_MCAST) 
+
 #define igmp_dbg(...) do {} while(0)
 /* #define igmp_dbg dbg */
 
@@ -94,7 +96,7 @@
     uint8_t rsq;
     uint8_t qqic;
     uint16_t sources;
-    uint32_t source_addr[];
+    uint32_t source_addr[0];
 };
 
 PACKED_STRUCT_DEF igmpv3_group_record {
@@ -102,7 +104,7 @@
     uint8_t aux;
     uint16_t sources;
     uint32_t mcast_group;
-    uint32_t source_addr[];
+    uint32_t source_addr[0];
 };
 
 PACKED_STRUCT_DEF igmpv3_report {
@@ -111,7 +113,7 @@
     uint16_t crc;
     uint16_t res1;
     uint16_t groups;
-    struct igmpv3_group_record record[];
+    struct igmpv3_group_record record[0];
 };
 
 struct igmp_parameters {
@@ -151,61 +153,70 @@
 /* state callback prototype */
 typedef int (*callback)(struct igmp_parameters *);
 
-/* redblack trees */
-static int igmp_timer_cmp(void *ka, void *kb)
+static inline int igmpt_type_compare(struct igmp_timer *a,  struct igmp_timer *b)
 {
-    struct igmp_timer *a = ka, *b = kb;
     if (a->type < b->type)
         return -1;
 
     if (a->type > b->type)
         return 1;
 
-    if (a->mcast_group.addr < b->mcast_group.addr)
-        return -1;
-
-    if (a->mcast_group.addr > b->mcast_group.addr)
-        return 1;
-
-    if (a->mcast_link.addr < b->mcast_link.addr)
-        return -1;
-
-    if (a->mcast_link.addr > b->mcast_link.addr)
-        return 1;
-
     return 0;
 }
+
+
+static inline int igmpt_group_compare(struct igmp_timer *a,  struct igmp_timer *b)
+{
+    return pico_ipv4_compare(&a->mcast_group, &b->mcast_group);
+}
+
+static inline int igmpt_link_compare(struct igmp_timer *a,  struct igmp_timer *b)
+{
+    return pico_ipv4_compare(&a->mcast_link, &b->mcast_link);
+}
+
+/* redblack trees */
+static int igmp_timer_cmp(void *ka, void *kb)
+{
+    struct igmp_timer *a = ka, *b = kb;
+    int cmp = igmpt_type_compare(a, b);
+    if (cmp)
+        return cmp;
+
+    cmp = igmpt_group_compare(a, b);
+    if (cmp)
+        return cmp;
+
+    return igmpt_link_compare(a, b);
+
+}
 PICO_TREE_DECLARE(IGMPTimers, igmp_timer_cmp);
 
+static inline int igmpparm_group_compare(struct igmp_parameters *a,  struct igmp_parameters *b)
+{
+    return pico_ipv4_compare(&a->mcast_group, &b->mcast_group);
+}
+
+static inline int igmpparm_link_compare(struct igmp_parameters *a,  struct igmp_parameters *b)
+{
+    return pico_ipv4_compare(&a->mcast_link, &b->mcast_link);
+}
+
 static int igmp_parameters_cmp(void *ka, void *kb)
 {
     struct igmp_parameters *a = ka, *b = kb;
-    if (a->mcast_group.addr < b->mcast_group.addr)
-        return -1;
-
-    if (a->mcast_group.addr > b->mcast_group.addr)
-        return 1;
+    int cmp = igmpparm_group_compare(a, b);
+    if (cmp)
+        return cmp;
 
-    if (a->mcast_link.addr < b->mcast_link.addr)
-        return -1;
-
-    if (a->mcast_link.addr > b->mcast_link.addr)
-        return 1;
-
-    return 0;
+    return igmpparm_link_compare(a, b);
 }
 PICO_TREE_DECLARE(IGMPParameters, igmp_parameters_cmp);
 
 static int igmp_sources_cmp(void *ka, void *kb)
 {
     struct pico_ip4 *a = ka, *b = kb;
-    if (a->addr < b->addr)
-        return -1;
-
-    if (a->addr > b->addr)
-        return 1;
-
-    return 0;
+    return pico_ipv4_compare(a, b);
 }
 PICO_TREE_DECLARE(IGMPAllow, igmp_sources_cmp);
 PICO_TREE_DECLARE(IGMPBlock, igmp_sources_cmp);
@@ -432,13 +443,15 @@
     datalen = (uint8_t)(short_be(hdr->len) - ihl);
     igmp_dbg("IGMP: IHL = %u, LEN = %u, OCTETS = %u\n", ihl, short_be(hdr->len), datalen);
 
-    if (datalen > 12) {
+    if (datalen >= 12) {
         /* IGMPv3 query */
         t.type = IGMP_TIMER_V2_QUERIER;
         if (pico_igmp_timer_is_running(&t)) { /* IGMPv2 querier present timer still running */
+            igmp_dbg("Timer is already running\n");
             return -1;
         } else {
             link->mcast_compatibility = PICO_IGMPV3;
+            igmp_dbg("IGMP Compatibility: v3\n");
             return 0;
         }
     } else if (datalen == 8) {
@@ -566,7 +579,7 @@
     .q_out = &igmp_out,
 };
 
-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)
+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)
 {
     struct igmp_parameters *p = NULL;
 
@@ -612,7 +625,7 @@
         return -1;
     }
     p->filter_mode = filter_mode;
-    p->MCASTFilter = MCASTFilter;
+    p->MCASTFilter = _MCASTFilter;
 
     return pico_igmp_process_event(p);
 }
@@ -720,6 +733,11 @@
             p->MCASTFilter = NULL;
         }
 
+        if (p->event == IGMP_EVENT_QUERY_RECV) {
+            goto igmp3_report;
+        }
+
+
         /* cleanup filters */
         pico_tree_foreach_safe(index, &IGMPAllow, _tmp)
         {
@@ -884,6 +902,7 @@
             return -1;
         }
 
+igmp3_report:
         len = (uint16_t)(sizeof(struct igmpv3_report) + sizeof(struct igmpv3_group_record) + (sources * sizeof(struct pico_ip4)));
         p->f = pico_proto_ipv4.alloc(&pico_proto_ipv4, (uint16_t)(IP_OPTION_ROUTER_ALERT_LEN + len));
         p->f->net_len = (uint16_t)(p->f->net_len + IP_OPTION_ROUTER_ALERT_LEN);
@@ -904,7 +923,7 @@
         record->aux = 0;
         record->sources = short_be(sources);
         record->mcast_group = p->mcast_group.addr;
-        if (!pico_tree_empty(IGMPFilter)) {
+        if (IGMPFilter && !pico_tree_empty(IGMPFilter)) {
             i = 0;
             pico_tree_foreach(index, IGMPFilter)
             {
@@ -1096,16 +1115,20 @@
 
     igmp_dbg("IGMP: event = query received | action = start timer\n");
 
-    if (pico_igmp_generate_report(p) < 0)
+    if (pico_igmp_generate_report(p) < 0) {
+        igmp_dbg("Failed to generate report\n");
         return -1;
+    }
 
-    if (!p->f)
+    if (!p->f) {
+        igmp_dbg("No pending frame\n");
         return -1;
+    }
 
     t.type = IGMP_TIMER_GROUP_REPORT;
     t.mcast_link = p->mcast_link;
     t.mcast_group = p->mcast_group;
-    t.delay = (pico_rand() % (p->max_resp_time * 100u));
+    t.delay = (pico_rand() % ((1u + p->max_resp_time) * 100u));
     t.f = p->f;
     t.callback = pico_igmp_report_expired;
     pico_igmp_timer_start(&t);
@@ -1163,7 +1186,7 @@
 
     time_to_run = (uint32_t)(t->start + t->delay - PICO_TIME_MS());
     if ((p->max_resp_time * 100u) < time_to_run) { /* max_resp_time in units of 1/10 seconds */
-        t->delay = pico_rand() % (p->max_resp_time * 100u);
+        t->delay = pico_rand() % ((1u + p->max_resp_time) * 100u);
         pico_igmp_timer_reset(t);
     }
 
@@ -1210,3 +1233,45 @@
     return 0;
 }
 
+#else
+static struct pico_queue igmp_in = {
+    0
+};
+static struct pico_queue igmp_out = {
+    0
+};
+
+static int pico_igmp_process_in(struct pico_protocol *self, struct pico_frame *f) {
+    IGNORE_PARAMETER(self);
+    IGNORE_PARAMETER(f);
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+    return -1;
+}
+
+static int pico_igmp_process_out(struct pico_protocol *self, struct pico_frame *f) {
+    IGNORE_PARAMETER(self);
+    IGNORE_PARAMETER(f);
+    return -1;
+}
+
+/* Interface: protocol definition */
+struct pico_protocol pico_proto_igmp = {
+    .name = "igmp",
+    .proto_number = PICO_PROTO_IGMP,
+    .layer = PICO_LAYER_TRANSPORT,
+    .process_in = pico_igmp_process_in,
+    .process_out = pico_igmp_process_out,
+    .q_in = &igmp_in,
+    .q_out = &igmp_out,
+};
+
+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) {
+    IGNORE_PARAMETER(mcast_link);
+    IGNORE_PARAMETER(mcast_group);
+    IGNORE_PARAMETER(filter_mode);
+    IGNORE_PARAMETER(_MCASTFilter);
+    IGNORE_PARAMETER(state);
+    pico_err = PICO_ERR_EPROTONOSUPPORT;
+    return -1;
+}
+#endif
--- a/modules/pico_igmp.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_igmp.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    .
@@ -22,5 +22,5 @@
 
 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);
+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 */
--- a/modules/pico_ipfilter.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_ipfilter.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    Authors: Andrei Carp
@@ -45,41 +45,259 @@
     uint8_t proto;
     int8_t priority;
     uint8_t tos;
-    uint8_t filter_id;
+    uint32_t filter_id;
     int (*function_ptr)(struct filter_node *filter, struct pico_frame *f);
 };
 
 PICO_TREE_DECLARE(filter_tree, &filter_compare);
 
-#define CHECK_AND_RETURN(a, b) do { \
-        if((a) && ((a) != (b))) \
-        { \
-            if((a) > (b)) return 1; \
-            else return -1; \
-        } \
-} while(0) \
+static inline int ipfilter_uint32_cmp(uint32_t a, uint32_t b)
+{
+    if (a < b)
+        return -1;
+
+    if (b < a)
+        return 1;
+
+    return 0;
+}
+
+static inline int ipfilter_uint16_cmp(uint16_t a, uint16_t b)
+{
+    if (a < b)
+        return -1;
+
+    if (b < a)
+        return 1;
+
+    return 0;
+}
+
+static inline int ipfilter_uint8_cmp(uint8_t a, uint8_t b)
+{
+    if (a < b)
+        return -1;
+
+    if (b < a)
+        return 1;
+
+    return 0;
+}
+
+static inline int ipfilter_ptr_cmp(void *a, void *b)
+{
+    if (a < b)
+        return -1;
+
+    if (b < a)
+        return 1;
+
+    return 0;
+}
+
+
+
+static inline int filter_compare_ports(struct filter_node *a, struct filter_node *b)
+{
+    int cmp;
+    cmp = ipfilter_uint16_cmp(a->in_port, b->in_port);
+    if (cmp)
+        return cmp;
+
+    cmp = ipfilter_uint16_cmp(a->out_port, b->out_port);
+    return cmp;
+}
+
+static inline int filter_compare_addresses(struct filter_node *a, struct filter_node *b)
+{
+    int cmp;
+    /* Compare source address */
+    cmp = ipfilter_uint32_cmp((a->in_addr & a->in_addr_netmask), (b->in_addr & b->in_addr_netmask));
+    if (cmp)
+        return cmp;
+
+    /* Compare destination address */
+    cmp = ipfilter_uint32_cmp((a->out_addr & a->out_addr_netmask), (b->out_addr & b->out_addr_netmask));
+    return cmp;
+}
+
+static inline int filter_compare_proto(struct filter_node *a, struct filter_node *b)
+{
+    return ipfilter_uint8_cmp(a->proto, b->proto);
+}
+
+static inline int filter_compare_address_port(struct filter_node *a, struct filter_node *b)
+{
+    int cmp;
+    cmp = filter_compare_addresses(a, b);
+    if (cmp)
+        return cmp;
+
+    return filter_compare_ports(a, b);
+}
+
+static inline int filter_match_packet_dev(struct filter_node *a, struct filter_node *b, struct filter_node *rule)
+{
+    int cmp;
+    /* 1. Compare devices */
+    if (rule->fdev) {
+        cmp = ipfilter_ptr_cmp(a->fdev, b->fdev);
+        if (cmp)
+            return cmp;
+    }
+
+    return 0;
+
+}
+
+static inline int filter_match_packet_proto(struct filter_node *a, struct filter_node *b, struct filter_node *rule)
+{
+    int cmp;
+    /* 2. Compare protocol */
+    if (rule->proto) {
+        cmp = filter_compare_proto(a, b);
+        if (cmp)
+            return cmp;
+    }
+
+    return 0;
+
+}
+static inline int filter_match_packet_addr_in(struct filter_node *a, struct filter_node *b, struct filter_node *rule)
+{
+    int cmp;
+    /* 3. Compare addresses order: in, out */
+    if (rule->in_addr_netmask) {
+        cmp = ipfilter_uint32_cmp(a->in_addr & rule->in_addr_netmask, b->in_addr & rule->in_addr_netmask);
+        if (cmp)
+            return cmp;
+    }
+
+    return 0;
+}
+static inline int filter_match_packet_addr_out(struct filter_node *a, struct filter_node *b, struct filter_node *rule)
+{
+    int cmp;
+    if (rule->out_addr_netmask) {
+        cmp = ipfilter_uint32_cmp(a->out_addr & rule->out_addr_netmask, b->out_addr & rule->out_addr_netmask);
+        if (cmp) {
+            return cmp;
+        }
+    }
+
+    return 0;
+}
+static inline int filter_match_packet_port_in(struct filter_node *a, struct filter_node *b, struct filter_node *rule)
+{
+    int cmp;
+    /* 4. Compare ports     order: in, out */
+    if (rule->in_port) {
+        cmp = ipfilter_uint16_cmp(a->in_port, b->in_port);
+        if (cmp)
+            return cmp;
+    }
+
+    return 0;
+}
+static inline int filter_match_packet_port_out(struct filter_node *a, struct filter_node *b, struct filter_node *rule)
+{
+    int cmp;
+    if (rule->out_port) {
+        cmp = ipfilter_uint16_cmp(a->out_port, b->out_port);
+        if (cmp)
+            return cmp;
+    }
+
+    return 0;
+}
+
+static inline int filter_match_packet_dev_and_proto(struct filter_node *a, struct filter_node *b, struct filter_node *rule)
+{
+    int cmp = filter_match_packet_dev(a, b, rule);
+    if (cmp)
+        return cmp;
+
+    return filter_match_packet_proto(a, b, rule);
+}
+
+static inline int filter_match_packet_addr(struct filter_node *a, struct filter_node *b, struct filter_node *rule)
+{
+    int cmp = filter_match_packet_addr_in(a, b, rule);
+    if (cmp)
+        return cmp;
+
+    return filter_match_packet_addr_out(a, b, rule);
+
+}
+
+static inline int filter_match_packet_port(struct filter_node *a, struct filter_node *b, struct filter_node *rule)
+{
+    int cmp = filter_match_packet_port_in(a, b, rule);
+    if (cmp)
+        return cmp;
+
+    return filter_match_packet_port_out(a, b, rule);
+}
+
+static inline struct filter_node *filter_match_packet_find_rule(struct filter_node *a, struct filter_node *b)
+{
+    if (!a->filter_id)
+        return b;
+
+    return a;
+}
+
+static inline int filter_match_packet(struct filter_node *a, struct filter_node *b)
+{
+    struct filter_node *rule;
+    int cmp = 0;
+    rule = filter_match_packet_find_rule(a, b);
+
+    cmp = filter_match_packet_dev_and_proto(a, b, rule);
+    if (cmp)
+        return cmp;
+
+    cmp = filter_match_packet_addr(a, b, rule);
+    if (cmp)
+        return cmp;
+
+    cmp = filter_match_packet_port(a, b, rule);
+    if (cmp)
+        return cmp;
+
+    return 0;
+}
 
 
 int filter_compare(void *filterA, void *filterB)
 {
-    struct filter_node *filter = (struct filter_node *)filterA,
-    *temp = (struct filter_node *)filterB;
+
+    struct filter_node *a = (struct filter_node *)filterA;
+    struct filter_node *b = (struct filter_node *)filterB;
+    int cmp = 0;
+    if (a->filter_id == 0 || b->filter_id == 0) {
+        return filter_match_packet(a, b);
+    }
 
     /* improve the search */
-    if(temp->filter_id && filter->filter_id == temp->filter_id)
+    if(a->filter_id == b->filter_id)
         return 0;
 
-    ipf_dbg("\nfilter ->> %x %x %x %x %d %d %d\n", filter->in_addr, filter->in_addr_netmask, filter->out_addr, filter->out_addr_netmask, filter->in_port, filter->out_port, filter->proto);
-    ipf_dbg("\ntemp ->> %x %x %x %x %d %d %d\n", temp->in_addr, temp->in_addr_netmask, temp->out_addr, temp->out_addr_netmask, temp->in_port, temp->out_port, filter->proto);
-    CHECK_AND_RETURN(filter->fdev, temp->fdev);
-    CHECK_AND_RETURN((filter->in_addr & filter->in_addr_netmask), (temp->in_addr & filter->in_addr_netmask));
-    CHECK_AND_RETURN((filter->out_addr & filter->out_addr_netmask), (temp->out_addr & filter->out_addr_netmask));
-    CHECK_AND_RETURN(filter->in_port, temp->in_port);
-    CHECK_AND_RETURN(filter->out_port, temp->out_port);
-    CHECK_AND_RETURN(filter->priority, temp->priority);
-    CHECK_AND_RETURN(filter->proto, temp->proto);
+    /* 1. Compare devices */
+    cmp = ipfilter_ptr_cmp(a->fdev, a->fdev);
+    if (cmp)
+        return cmp;
 
-    return 0;
+    /* 2. Compare protocol */
+    cmp = filter_compare_proto(a, b);
+    if(cmp)
+        return cmp;
+
+    /* 3. Compare addresses order: in, out */
+    /* 4. Compare ports     order: in, out */
+    cmp = filter_compare_address_port(a, b);
+
+    return cmp;
 }
 
 /**************** FILTER CALLBACKS ****************/
@@ -97,7 +315,7 @@
 /* TODO check first if sender is pico itself or not */
     IGNORE_PARAMETER(filter);
     ipf_dbg("ipfilter> reject\n");
-    pico_icmp4_packet_filtered(f);
+    (void)pico_icmp4_packet_filtered(f);
     pico_frame_discard(f);
     return 1;
 }
@@ -110,81 +328,77 @@
     return 1;
 }
 
-/**************** FILTER API's ****************/
-int 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)
+struct fp_function {
+    int (*fn)(struct filter_node *filter, struct pico_frame *f);
+};
+
+
+static const struct fp_function fp_function[FILTER_COUNT] =
 {
-    static uint8_t filter_id = 0;
-    struct filter_node *new_filter;
+    {&fp_priority},
+    {&fp_reject},
+    {&fp_drop}
+};
 
-    if (proto != PICO_PROTO_IPV4 || tos != 0 )
-    {
-        pico_err = PICO_ERR_EINVAL;
+static int pico_ipv4_filter_add_validate(int8_t priority, enum filter_action action)
+{
+    if ( priority > MAX_PRIORITY || priority < MIN_PRIORITY) {
+        return -1;
+    }
+
+    if (action >= FILTER_COUNT) {
         return -1;
     }
 
-    if ( priority > MAX_PRIORITY || priority < MIN_PRIORITY) {
+    return 0;
+}
+
+
+/**************** FILTER API's ****************/
+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)
+{
+    static uint32_t filter_id = 1u; /* 0 is a special value used in the binary-tree search for packets being processed */
+    struct filter_node *new_filter;
+
+    if (pico_ipv4_filter_add_validate(priority, action) < 0) {
         pico_err = PICO_ERR_EINVAL;
-        return -1;
+        return 0;
     }
 
-    if (action > FILTER_COUNT) {
-        pico_err = PICO_ERR_EINVAL;
-        return -1;
-    }
-
-    ipf_dbg("ipfilter> adding filter\n");
-
     new_filter = PICO_ZALLOC(sizeof(struct filter_node));
-
     if (!new_filter) {
         pico_err = PICO_ERR_ENOMEM;
-        return -1;
+        return 0;
     }
 
     new_filter->fdev = dev;
     new_filter->proto = proto;
-
     new_filter->out_addr = (!out_addr) ? (0U) : (out_addr->addr);
     new_filter->out_addr_netmask = (!out_addr_netmask) ? (0U) : (out_addr_netmask->addr);
     new_filter->in_addr = (!in_addr) ? (0U) : (in_addr->addr);
     new_filter->in_addr_netmask = (!in_addr_netmask) ? (0U) : (in_addr_netmask->addr);
-
     new_filter->out_port = out_port;
     new_filter->in_port = in_port;
     new_filter->priority = priority;
     new_filter->tos = tos;
-
-    if(filter_id == 0)
-        filter_id = 1;
-
     new_filter->filter_id = filter_id++;
+    new_filter->function_ptr = fp_function[action].fn;
 
-    /*Define filterType_functionPointer here instead of in ipfilter-function, to prevent running multiple times through switch*/
-    switch (action) {
-    case FILTER_PRIORITY:
-        new_filter->function_ptr = fp_priority;
-        break;
-    case FILTER_REJECT:
-        new_filter->function_ptr = fp_reject;
-        break;
-    case FILTER_DROP:
-        new_filter->function_ptr = fp_drop;
-        break;
-    default:
-        ipf_dbg("ipfilter> unknown filter action\n");
-        break;
-    }
     if(pico_tree_insert(&filter_tree, new_filter))
     {
         PICO_FREE(new_filter);
-        ipf_dbg("ipfilter> failed adding filter to tree.\n");
-        return -1;
+        filter_id--;
+        return 0;
     }
 
     return new_filter->filter_id;
 }
 
-int pico_ipv4_filter_del(uint8_t filter_id)
+int pico_ipv4_filter_del(uint32_t filter_id)
 {
     struct filter_node *node = NULL;
     struct filter_node dummy = {
@@ -200,13 +414,24 @@
     return 0;
 }
 
+static int ipfilter_apply_filter(struct pico_frame *f, struct filter_node *pkt)
+{
+    struct filter_node *filter_frame = NULL;
+    filter_frame = pico_tree_findKey(&filter_tree, pkt);
+    if(filter_frame)
+    {
+        filter_frame->function_ptr(filter_frame, f);
+        return 1;
+    }
+
+    return 0;
+}
+
 int ipfilter(struct pico_frame *f)
 {
     struct filter_node temp;
-    struct filter_node *filter_frame = NULL;
     struct pico_ipv4_hdr *ipv4_hdr = (struct pico_ipv4_hdr *) f->net_hdr;
-    struct pico_tcp_hdr *tcp_hdr;
-    struct pico_udp_hdr *udp_hdr;
+    struct pico_trans *trans;
     struct pico_icmp4_hdr *icmp_hdr;
 
     memset(&temp, 0u, sizeof(struct filter_node));
@@ -214,40 +439,20 @@
     temp.fdev = f->dev;
     temp.out_addr = ipv4_hdr->dst.addr;
     temp.in_addr = ipv4_hdr->src.addr;
-
-    if (ipv4_hdr->proto == PICO_PROTO_TCP ) {
-        tcp_hdr = (struct pico_tcp_hdr *) f->transport_hdr;
-        temp.out_port = short_be(tcp_hdr->trans.dport);
-        temp.in_port = short_be(tcp_hdr->trans.sport);
-    }else if (ipv4_hdr->proto == PICO_PROTO_UDP ) {
-        udp_hdr = (struct pico_udp_hdr *) f->transport_hdr;
-        temp.out_port = short_be(udp_hdr->trans.dport);
-        temp.in_port = short_be(udp_hdr->trans.sport);
+    if ((ipv4_hdr->proto == PICO_PROTO_TCP) || (ipv4_hdr->proto == PICO_PROTO_UDP)) {
+        trans = (struct pico_trans *) f->transport_hdr;
+        temp.out_port = short_be(trans->dport);
+        temp.in_port = short_be(trans->sport);
     }
-    else
-    {
-        if(ipv4_hdr->proto == PICO_PROTO_ICMP4)
-        {
-            icmp_hdr = (struct pico_icmp4_hdr *) f->transport_hdr;
-            if(icmp_hdr->type == PICO_ICMP_UNREACH && icmp_hdr->type == PICO_ICMP_UNREACH_FILTER_PROHIB)
-                return 0;
-        }
-
-        temp.out_port = temp.in_port = 0;
+    else if(ipv4_hdr->proto == PICO_PROTO_ICMP4) {
+        icmp_hdr = (struct pico_icmp4_hdr *) f->transport_hdr;
+        if(icmp_hdr->type == PICO_ICMP_UNREACH && icmp_hdr->type == PICO_ICMP_UNREACH_FILTER_PROHIB)
+            return 0;
     }
 
     temp.proto = ipv4_hdr->proto;
     temp.priority = f->priority;
     temp.tos = ipv4_hdr->tos;
-
-    filter_frame = pico_tree_findKey(&filter_tree, &temp);
-    if(filter_frame)
-    {
-        ipf_dbg("Filtering frame %p with filter %p\n", f, filter_frame);
-        filter_frame->function_ptr(filter_frame, f);
-        return 1;
-    }
-
-    return 0;
+    return ipfilter_apply_filter(f, &temp);
 }
 
--- a/modules/pico_ipfilter.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_ipfilter.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    Authors: Simon Maes
@@ -16,12 +16,12 @@
     FILTER_COUNT
 };
 
-int 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);
+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(uint8_t filter_id);
+int pico_ipv4_filter_del(uint32_t filter_id);
 
 int ipfilter(struct pico_frame *f);
 
--- a/modules/pico_ipv4.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_ipv4.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    Authors: Daniele Lacamera, Markian Yskout
@@ -19,7 +19,9 @@
 #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
 
@@ -30,7 +32,7 @@
 /* Default network interface for multicast transmission */
 static struct pico_ipv4_link *mcast_default_link = NULL;
 #endif
-#ifdef PICO_SUPPORT_IPFRAG
+#ifdef PICO_SUPPORT_IPV4FRAG
 /* # define reassembly_dbg dbg */
 # define reassembly_dbg(...) do {} while(0)
 #endif
@@ -47,6 +49,18 @@
 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;
@@ -59,18 +73,18 @@
 
     for(i = 0; i < 4; i++)
     {
-        if(addr[i] > 99) {
+        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) {
+        } else if (addr[i] > 9) {
             *ipbuf++ = (char)('0' + (addr[i] / 10));
             *ipbuf++ = (char)('0' + (addr[i] % 10));
-        }else{
+        } else {
             *ipbuf++ = (char)('0' + addr[i]);
         }
 
-        if(i < 3)
+        if (i < 3)
             *ipbuf++ = '.';
     }
     *ipbuf = '\0';
@@ -81,7 +95,7 @@
 static int pico_string_check_null_args(const char *ipstr, uint32_t *ip)
 {
 
-    if(!ipstr || !ip) {
+    if (!ipstr || !ip) {
         pico_err = PICO_ERR_EINVAL;
         return -1;
     }
@@ -92,7 +106,7 @@
 
 int pico_string_to_ipv4(const char *ipstr, uint32_t *ip)
 {
-    unsigned char buf[4] = {
+    unsigned char buf[PICO_SIZE_IP4] = {
         0
     };
     int cnt = 0;
@@ -101,26 +115,25 @@
     if (pico_string_check_null_args(ipstr, ip) < 0)
         return -1;
 
-
-    while((p = *ipstr++) != 0)
+    while((p = *ipstr++) != 0 && cnt < PICO_SIZE_IP4)
     {
-        if(pico_is_digit(p)) {
+        if (pico_is_digit(p)) {
             buf[cnt] = (uint8_t)((10 * buf[cnt]) + (p - '0'));
-        }else if(p == '.') {
+        } else if (p == '.') {
             cnt++;
-        }else{
+        } else {
             return -1;
         }
     }
     /* Handle short notation */
-    if(cnt == 1) {
+    if (cnt == 1) {
         buf[3] = buf[1];
         buf[1] = 0;
         buf[2] = 0;
-    }else if (cnt == 2) {
+    } else if (cnt == 2) {
         buf[3] = buf[2];
         buf[2] = 0;
-    }else if(cnt != 3) {
+    } else if (cnt != 3) {
         /* String could not be parsed, return error */
         return -1;
     }
@@ -128,7 +141,6 @@
     *ip = long_from(buf);
 
     return 0;
-
 }
 
 int pico_ipv4_valid_netmask(uint32_t mask)
@@ -146,14 +158,14 @@
      * */
 
     for(i = 0; i < 32; i++) {
-        if((mask_swap << i) & 0x80000000) {
-            if(end) {
+        if ((mask_swap << i) & 0x80000000) {
+            if (end) {
                 pico_err = PICO_ERR_EINVAL;
                 return -1;
             }
 
             cnt++;
-        }else{
+        } else {
             end = 1;
         }
     }
@@ -163,7 +175,7 @@
 int pico_ipv4_is_unicast(uint32_t address)
 {
     const unsigned char *addr = (unsigned char *) &address;
-    if((addr[0] & 0xe0) == 0xe0)
+    if ((addr[0] & 0xe0) == 0xe0)
         return 0; /* multicast */
 
     return 1;
@@ -172,7 +184,7 @@
 int pico_ipv4_is_multicast(uint32_t address)
 {
     const unsigned char *addr = (unsigned char *) &address;
-    if((addr[0] != 0xff) && ((addr[0] & 0xe0) == 0xe0))
+    if ((addr[0] != 0xff) && ((addr[0] & 0xe0) == 0xe0))
         return 1; /* multicast */
 
     return 0;
@@ -181,7 +193,7 @@
 int pico_ipv4_is_loopback(uint32_t address)
 {
     const unsigned char *addr = (unsigned char *) &address;
-    if(addr[0] == 0x7f)
+    if (addr[0] == 0x7f)
         return 1;
 
     return 0;
@@ -197,16 +209,18 @@
     if (pico_ipv4_is_broadcast(address)) {
         dbg("Source is a broadcast address, discard packet\n");
         return 0;
-    }
-    else if( pico_ipv4_is_multicast(address)) {
+    } 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)) {
+    } else if (pico_ipv4_is_invalid_loopback(address, dev)) {
         dbg("Source is a loopback address, discard packet\n");
         return 0;
-    }
-    else {
+    } else {
+#ifdef PICO_SUPPORT_AODV
+        union pico_address src;
+        src.ip4.addr = address;
+        pico_aodv_refresh(&src);
+#endif
         return 1;
     }
 }
@@ -222,270 +236,6 @@
     return 0;
 }
 
-#ifdef PICO_SUPPORT_IPFRAG
-struct pico_ipv4_fragmented_packet {
-    uint16_t id;
-    uint8_t proto;
-    struct pico_ip4 src;
-    struct pico_ip4 dst;
-    uint16_t total_len;
-    struct pico_tree *t;
-};
-
-static int pico_ipv4_fragmented_packet_cmp(void *ka, void *kb)
-{
-    struct pico_ipv4_fragmented_packet *a = ka, *b = kb;
-
-    if (a->id < b->id)
-        return -1;
-    else if (a->id > b->id)
-        return 1;
-    else {
-        if (a->proto < b->proto)
-            return -1;
-        else if (a->proto > b->proto)
-            return 1;
-        else {
-            if (a->src.addr < b->src.addr)
-                return -1;
-            else if (a->src.addr > b->src.addr)
-                return 1;
-            else {
-                if (a->dst.addr < b->dst.addr)
-                    return -1;
-                else if (a->dst.addr > b->dst.addr)
-                    return 1;
-                else
-                    return 0;
-            }
-        }
-    }
-}
-
-static int pico_ipv4_fragmented_element_cmp(void *ka, void *kb)
-{
-    struct pico_frame *frame_a = ka, *frame_b = kb;
-    struct pico_ipv4_hdr *a, *b;
-    uint16_t a_frag, b_frag;
-    a = (struct pico_ipv4_hdr *) frame_a->net_hdr;
-    b = (struct pico_ipv4_hdr *) frame_b->net_hdr;
-    a_frag = short_be(a->frag & PICO_IPV4_FRAG_MASK);
-    b_frag = short_be(b->frag & PICO_IPV4_FRAG_MASK);
-
-    if (a_frag < b_frag)
-        return -1;
-
-    if (b_frag < a_frag)
-        return 1;
-    else
-        return 0;
-}
-
-PICO_TREE_DECLARE(pico_ipv4_fragmented_tree, pico_ipv4_fragmented_packet_cmp);
-
-static inline void pico_ipv4_fragmented_cleanup(struct pico_ipv4_fragmented_packet *pfrag)
-{
-    struct pico_tree_node *index = NULL, *_tmp = NULL;
-    struct pico_frame *f_frag = NULL;
-
-    pico_tree_foreach_safe(index, pfrag->t, _tmp) {
-        f_frag = index->keyValue;
-        reassembly_dbg("REASSEMBLY: remove packet with offset %u\n", short_be(((struct pico_ipv4_hdr *)f_frag->net_hdr)->frag) & PICO_IPV4_FRAG_MASK);
-        pico_tree_delete(pfrag->t, f_frag);
-        pico_frame_discard(f_frag);
-    }
-    pico_tree_delete(&pico_ipv4_fragmented_tree, pfrag);
-    PICO_FREE(pfrag->t);
-    PICO_FREE(pfrag);
-}
-#endif /* PICO_SUPPORT_IPFRAG */
-
-#ifdef PICO_SUPPORT_IPFRAG
-
-static inline struct pico_ipv4_fragmented_packet *fragment_find_by_hdr(struct pico_ipv4_hdr *hdr)
-{
-    struct pico_ipv4_fragmented_packet frag = {
-        0
-    };
-    frag.id = short_be(hdr->id);
-    frag.proto = hdr->proto;
-    frag.src.addr = long_be(hdr->src.addr);
-    frag.dst.addr = long_be(hdr->dst.addr);
-    return pico_tree_findKey(&pico_ipv4_fragmented_tree, &frag);
-}
-
-
-static inline int8_t pico_ipv4_fragmented_check(struct pico_protocol *self, struct pico_frame **f)
-{
-    uint8_t *running_pointer = NULL;
-    uint16_t running_offset = 0;
-    uint16_t offset = 0;
-    uint16_t data_len = 0;
-    struct pico_ipv4_hdr *f_frag_hdr = NULL, *hdr = (struct pico_ipv4_hdr *) (*f)->net_hdr;
-    struct pico_ipv4_fragmented_packet *pfrag = NULL;
-    struct pico_frame *f_new = NULL, *f_frag = NULL;
-    struct pico_tree_node *index, *_tmp;
-
-    data_len = (uint16_t)(short_be(hdr->len) - (*f)->net_len);
-    offset = short_be(hdr->frag) & PICO_IPV4_FRAG_MASK;
-    if (short_be(hdr->frag) & PICO_IPV4_MOREFRAG) {
-        if (!offset) {
-            reassembly_dbg("REASSEMBLY: first element of a fragmented packet with id %X and offset %u\n", short_be(hdr->id), offset);
-            if (!pico_tree_empty(&pico_ipv4_fragmented_tree)) {
-                reassembly_dbg("REASSEMBLY: cleanup tree\n");
-                /* only one entry allowed in this tree */
-                pfrag = pico_tree_first(&pico_ipv4_fragmented_tree);
-                pico_ipv4_fragmented_cleanup(pfrag);
-            }
-
-            /* add entry in tree for this ID and create secondary tree to contain fragmented elements */
-            pfrag = PICO_ZALLOC(sizeof(struct pico_ipv4_fragmented_packet));
-            if (!pfrag) {
-                pico_err = PICO_ERR_ENOMEM;
-                return -1;
-            }
-
-            pfrag->id = short_be(hdr->id);
-            pfrag->proto = hdr->proto;
-            pfrag->src.addr = long_be(hdr->src.addr);
-            pfrag->dst.addr = long_be(hdr->dst.addr);
-            pfrag->total_len = (uint16_t)(short_be(hdr->len) - (*f)->net_len);
-            pfrag->t = PICO_ZALLOC(sizeof(struct pico_tree));
-            if (!pfrag->t) {
-                PICO_FREE(pfrag);
-                pico_err = PICO_ERR_ENOMEM;
-                return -1;
-            }
-
-            pfrag->t->root = &LEAF;
-            pfrag->t->compare = pico_ipv4_fragmented_element_cmp;
-
-            pico_tree_insert(pfrag->t, *f);
-            pico_tree_insert(&pico_ipv4_fragmented_tree, pfrag);
-            return 0;
-        }
-        else {
-            reassembly_dbg("REASSEMBLY: intermediate element of a fragmented packet with id %X and offset %u\n", short_be(hdr->id), offset);
-            pfrag = fragment_find_by_hdr(hdr);
-            if (pfrag) {
-                pfrag->total_len = (uint16_t)(pfrag->total_len + (short_be(hdr->len) - (*f)->net_len));
-                if (pfrag->total_len > PICO_IPV4_FRAG_MAX_SIZE) {
-                    reassembly_dbg("BIG frame!!!\n");
-                    pfrag = pico_tree_first(&pico_ipv4_fragmented_tree);
-                    pico_ipv4_fragmented_cleanup(pfrag);
-                    pico_frame_discard(*f);
-                    return 0;
-                }
-
-                pico_tree_insert(pfrag->t, *f);
-                return 0;
-            } else {
-                reassembly_dbg("REASSEMBLY: silently discard intermediate frame, first packet was lost or disallowed (one fragmented packet at a time)\n");
-                pico_frame_discard(*f);
-                return 0;
-            }
-        }
-    } else if (offset) {
-        reassembly_dbg("REASSEMBLY: last element of a fragmented packet with id %X and offset %u\n", short_be(hdr->id), offset);
-        pfrag = fragment_find_by_hdr(hdr);
-        if (pfrag) {
-            pfrag->total_len = (uint16_t)(pfrag->total_len + (short_be(hdr->len) - (*f)->net_len));
-            reassembly_dbg("REASSEMBLY: fragmented packet in tree, reassemble packet of %u data bytes\n", pfrag->total_len);
-            if (pfrag->total_len > PICO_IPV4_FRAG_MAX_SIZE) {
-                reassembly_dbg("BIG frame!!!\n");
-                pfrag = pico_tree_first(&pico_ipv4_fragmented_tree);
-                pico_ipv4_fragmented_cleanup(pfrag);
-                pico_frame_discard(*f);
-                return 0;
-            }
-
-            f_new = self->alloc(self, pfrag->total_len);
-            if (!f_new)
-                return -1;
-
-            f_frag = pico_tree_first(pfrag->t);
-            reassembly_dbg("REASSEMBLY: copy IP header information len = %lu\n", f_frag->net_len);
-            f_frag_hdr = (struct pico_ipv4_hdr *)f_frag->net_hdr;
-            data_len = (uint16_t)(short_be(f_frag_hdr->len) - f_frag->net_len);
-            memcpy(f_new->net_hdr, f_frag->net_hdr, f_frag->net_len);
-            memcpy(f_new->transport_hdr, f_frag->transport_hdr, data_len);
-            f_new->dev = f_frag->dev;
-            running_pointer = f_new->transport_hdr + data_len;
-            offset = short_be(f_frag_hdr->frag) & PICO_IPV4_FRAG_MASK;
-            running_offset = data_len / 8;
-            pico_tree_delete(pfrag->t, f_frag);
-            pico_frame_discard(f_frag);
-            reassembly_dbg("REASSEMBLY: reassembled first packet of %u data bytes, offset = %u next expected offset = %u\n", data_len, offset, running_offset);
-
-            pico_tree_foreach_safe(index, pfrag->t, _tmp)
-            {
-                f_frag = index->keyValue;
-                f_frag_hdr = (struct pico_ipv4_hdr *)f_frag->net_hdr;
-                data_len = (uint16_t)(short_be(f_frag_hdr->len) - f_frag->net_len);
-                memcpy(running_pointer, f_frag->transport_hdr, data_len);
-                running_pointer += data_len;
-                offset = short_be(f_frag_hdr->frag) & PICO_IPV4_FRAG_MASK;
-                if (offset != running_offset) {
-                    reassembly_dbg("REASSEMBLY: error reassembling intermediate packet: offset %u != expected offset %u (missing fragment)\n", offset, running_offset);
-                    pico_ipv4_fragmented_cleanup(pfrag);
-                    return -1;
-                }
-
-                running_offset = (uint16_t)(running_offset + (data_len / 8));
-                pico_tree_delete(pfrag->t, f_frag);
-                pico_frame_discard(f_frag);
-                reassembly_dbg("REASSEMBLY: reassembled intermediate packet of %u data bytes, offset = %u next expected offset = %u\n", data_len, offset, running_offset);
-            }
-            pico_tree_delete(&pico_ipv4_fragmented_tree, pfrag);
-            PICO_FREE(pfrag);
-
-            data_len = (uint16_t)(short_be(hdr->len) - (*f)->net_len);
-            memcpy(running_pointer, (*f)->transport_hdr, data_len);
-            offset = short_be(hdr->frag) & PICO_IPV4_FRAG_MASK;
-            pico_frame_discard(*f);
-            reassembly_dbg("REASSEMBLY: reassembled last packet of %u data bytes, offset = %u\n", data_len, offset);
-
-            hdr = (struct pico_ipv4_hdr *)f_new->net_hdr;
-            hdr->len = pfrag->total_len;
-            hdr->frag = 0; /* flags cleared and no offset */
-            hdr->crc = 0;
-            hdr->crc = short_be(pico_checksum(hdr, f_new->net_len));
-            /* Optional, the UDP/TCP CRC should already be correct */
-            if (0) {
-  #ifdef PICO_SUPPORT_TCP
-            } else if (hdr->proto == PICO_PROTO_TCP) {
-                struct pico_tcp_hdr *tcp_hdr = NULL;
-                tcp_hdr = (struct pico_tcp_hdr *) f_new->transport_hdr;
-                tcp_hdr->crc = 0;
-                tcp_hdr->crc = short_be(pico_tcp_checksum(f_new));
-  #endif
-  #ifdef PICO_SUPPORT_UDP
-            } else if (hdr->proto == PICO_PROTO_UDP) {
-                struct pico_udp_hdr *udp_hdr = NULL;
-                udp_hdr = (struct pico_udp_hdr *) f_new->transport_hdr;
-                udp_hdr->crc = 0;
-                udp_hdr->crc = short_be(pico_udp_checksum_ipv4(f_new));
-  #endif
-            }
-
-            reassembly_dbg("REASSEMBLY: packet with id %X reassembled correctly\n", short_be(hdr->id));
-            *f = f_new;
-            return 1;
-        } else {
-            reassembly_dbg("REASSEMBLY: silently discard last frame, first packet was lost or disallowed (one fragmented packet at a time)\n");
-            pico_frame_discard(*f);
-            return 0;
-        }
-    } else {
-        return 1;
-    }
-}
-#else
-static inline int8_t pico_ipv4_fragmented_check(struct pico_protocol *self, struct pico_frame **f)
-{
-    return 1;
-}
-#endif /* PICO_SUPPORT_IPFRAG */
 
 #ifdef PICO_SUPPORT_CRC
 static inline int pico_ipv4_crc_check(struct pico_frame *f)
@@ -518,11 +268,9 @@
 static int ipv4_link_compare(void *ka, void *kb)
 {
     struct pico_ipv4_link *a = ka, *b = kb;
-    if (a->address.addr < b->address.addr)
-        return -1;
-
-    if (a->address.addr > b->address.addr)
-        return 1;
+    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) {
@@ -549,6 +297,9 @@
         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;
@@ -564,7 +315,7 @@
 {
     struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
     if (pico_ipv4_is_multicast(hdr->dst.addr)) {
-#ifdef PICO_SUPPORT_MCAST
+#ifdef PICO_SUPPORT_IGMP
         /* Receiving UDP multicast datagram TODO set f->flags? */
         if (hdr->proto == PICO_PROTO_IGMP) {
             ip_mcast_dbg("MCAST: received IGMP message\n");
@@ -613,8 +364,7 @@
 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)))
-    {
+    if ((pico_ipv4_is_broadcast(hdr->dst.addr)) || ((f->flags & PICO_FRAME_FLAG_BCAST)!= 0)) {
         /* don't forward broadcast frame, discard! */
         pico_frame_discard(f);
     } else if (pico_ipv4_forward(f) != 0) {
@@ -630,9 +380,12 @@
     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) {
+    if (((hdr->vhl) & 0x0F) > 5) {
         option_len =  (uint8_t)(4 * (((hdr->vhl) & 0x0F) - 5));
     }
 
@@ -640,6 +393,11 @@
     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*/
@@ -648,26 +406,42 @@
 
 #endif
 
+
     /* ret == 1 indicates to continue the function */
     ret = pico_ipv4_crc_check(f);
     if (ret < 1)
         return ret;
 
-    ret = pico_ipv4_fragmented_check(self, &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 & 0x80) {
+    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 | PICO_IPV4_FRAG_MASK))
+    {
+#ifdef PICO_SUPPORT_IPV4FRAG
+        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 quietly */
+        pico_frame_discard(f);
+        return 0;
+    }
+
     if (pico_ipv4_process_bcast_in(f) > 0)
         return 0;
 
@@ -689,13 +463,13 @@
 {
     IGNORE_PARAMETER(self);
     f->start = (uint8_t*) f->net_hdr;
-  #ifdef PICO_SUPPORT_IPFILTER
+#ifdef PICO_SUPPORT_IPFILTER
     if (ipfilter(f)) {
         /*pico_frame is discarded as result of the filtering*/
         return 0;
     }
 
-  #endif
+#endif
     return pico_sendto_dev(f);
 }
 
@@ -732,20 +506,12 @@
     .q_out = &out,
 };
 
-struct pico_ipv4_route
-{
-    struct pico_ip4 dest;
-    struct pico_ip4 netmask;
-    struct pico_ip4 gateway;
-    struct pico_ipv4_link *link;
-    uint32_t metric;
-};
-
 
 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);
@@ -757,11 +523,9 @@
     if (b_nm < a_nm)
         return 1;
 
-    if (a->dest.addr < b->dest.addr)
-        return -1;
-
-    if (a->dest.addr > b->dest.addr)
-        return 1;
+    cmp = pico_ipv4_compare(&a->dest, &b->dest);
+    if (cmp)
+        return cmp;
 
     if (a->metric < b->metric)
         return -1;
@@ -772,34 +536,37 @@
     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)
-    {
+    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;
             }
         }
-    }
-    else
-    {
-        r = pico_tree_first(&Routes);
-        if(!r->netmask.addr)
-        {
-            return r;
-        }
-        else
-        {
-            dbg("WARNING: no default route for a global broadcast found\n");
-        }
+        return NULL;
     }
 
-    return NULL;
+    return route_find_default_bcast();
 }
 
 struct pico_ip4 pico_ipv4_route_get_gateway(struct pico_ip4 *addr)
@@ -808,7 +575,7 @@
     struct pico_ipv4_route *route;
     nullip.addr = 0U;
 
-    if(!addr) {
+    if (!addr) {
         pico_err = PICO_ERR_EINVAL;
         return nullip;
     }
@@ -826,21 +593,49 @@
 {
     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);
 
-    if(!dst) {
+#endif
+
+    if (!dst) {
         pico_err = PICO_ERR_EINVAL;
         return NULL;
     }
 
     rt = route_find(dst);
-    if (rt) {
+    if (rt && rt->link) {
         myself = &rt->link->address;
-    } else
+    } 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
@@ -859,25 +654,13 @@
 static int ipv4_mcast_groups_cmp(void *ka, void *kb)
 {
     struct pico_mcast_group *a = ka, *b = kb;
-    if (a->mcast_addr.addr < b->mcast_addr.addr) {
-        return -1;
-    } else if (a->mcast_addr.addr > b->mcast_addr.addr) {
-        return 1;
-    } else {
-        return 0;
-    }
+    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;
-    if (a->addr < b->addr)
-        return -1;
-
-    if (a->addr > b->addr)
-        return 1;
-
-    return 0;
+    return pico_ipv4_compare(a, b);
 }
 
 static void pico_ipv4_mcast_print_groups(struct pico_ipv4_link *mcast_link)
@@ -894,12 +677,10 @@
     ip_mcast_dbg("+  nr  |    interface     | host group | reference count | filter mode |  source  +\n");
     ip_mcast_dbg("+---------------------------------------------------------------------------------+\n");
 
-    pico_tree_foreach(index, mcast_link->MCASTGroups)
-    {
+    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)
-        {
+        pico_tree_foreach(index2, &g->MCASTSources) {
             source = index2->keyValue;
             ip_mcast_dbg("+ %4s | %16s |  %8s  |      %5s      |      %s      | %08X +\n", "", "", "", "", "", source->addr);
         }
@@ -913,16 +694,14 @@
     struct pico_tree_node *index = NULL, *_tmp = NULL;
     struct pico_ip4 *source = NULL;
     /* cleanup filter */
-    pico_tree_foreach_safe(index, &g->MCASTSources, _tmp)
-    {
+    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)
-        {
+        pico_tree_foreach(index, MCASTFilter) {
             if (index->keyValue) {
                 source = PICO_ZALLOC(sizeof(struct pico_ip4));
                 if (!source) {
@@ -949,7 +728,8 @@
 
     if (mcast_link)
         link = pico_ipv4_link_get(mcast_link);
-    else
+
+    if (!link)
         link = mcast_default_link;
 
     test.mcast_addr = *mcast_group;
@@ -997,7 +777,8 @@
 
     if (mcast_link)
         link = pico_ipv4_link_get(mcast_link);
-    else
+
+    if (!link)
         link = mcast_default_link;
 
     test.mcast_addr = *mcast_group;
@@ -1009,8 +790,7 @@
         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)
-            {
+            pico_tree_foreach_safe(index, &g->MCASTSources, _tmp) {
                 source = index->keyValue;
                 pico_tree_delete(&g->MCASTSources, source);
                 PICO_FREE(source);
@@ -1044,19 +824,16 @@
 
     test.mcast_addr = hdr->dst;
 
-    pico_tree_foreach(index, &Tree_dev_link)
-    {
+    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)
-                {
+                switch (g->filter_mode) {
                 case PICO_IP_MULTICAST_INCLUDE:
-                    pico_tree_foreach(index2, &g->MCASTSources)
-                    {
+                    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;
@@ -1066,8 +843,7 @@
                     return -1;
 
                 case PICO_IP_MULTICAST_EXCLUDE:
-                    pico_tree_foreach(index2, &g->MCASTSources)
-                    {
+                    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;
@@ -1096,11 +872,13 @@
     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;
@@ -1108,15 +886,21 @@
 }
 #endif /* PICO_SUPPORT_MCAST */
 
+/* #define DEBUG_ROUTE */
 #ifdef DEBUG_ROUTE
-static void dbg_route(void)
+void dbg_route(void)
 {
     struct pico_ipv4_route *r;
     struct pico_tree_node *index;
-    pico_tree_foreach(index, &Routes){
+    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)
@@ -1135,11 +919,12 @@
     struct pico_tree_node *index;
 #endif
 
-    if(!f || !dst) {
+    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");
@@ -1166,10 +951,11 @@
         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)
+                if (pico_udp_get_mc_ttl(f->sock, &ttl) < 0)
                     ttl = PICO_IP_DEFAULT_MULTICAST_TTL;
 
                 break;
+#ifdef PICO_SUPPORT_IGMP
             case PICO_PROTO_IGMP:
                 vhl = 0x46; /* header length 24 */
                 ttl = 1;
@@ -1187,6 +973,7 @@
                 }
 
                 break;
+#endif
             default:
                 ttl = PICO_IPV4_DEFAULT_TTL;
             }
@@ -1198,19 +985,27 @@
     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)) &&
+#ifdef PICO_SUPPORT_IPV4FRAG
+        ( (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_IPV4FRAG
 #  ifdef PICO_SUPPORT_UDP
     if (proto == PICO_PROTO_UDP) {
         /* first fragment, can not use transport_len to calculate IP length */
@@ -1218,14 +1013,16 @@
             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 = f->frag;
+        hdr->frag = short_be(f->frag);
     }
 
     if (proto == PICO_PROTO_ICMP4)
-        hdr->frag = f->frag;
+    {
+        hdr->frag = short_be(f->frag);
+    }
 
 #   endif
-#endif /* PICO_SUPPORT_IPFRAG */
+#endif /* PICO_SUPPORT_IPV4FRAG */
     pico_ipv4_checksum(f);
 
     if (f->sock && f->sock->dev) {
@@ -1233,6 +1030,8 @@
         f->dev = f->sock->dev;
     } else {
         f->dev = link->dev;
+        if (f->sock)
+            f->sock->dev = f->dev;
     }
 
 #ifdef PICO_SUPPORT_MCAST
@@ -1248,10 +1047,20 @@
 
 #endif
 
-    if(pico_ipv4_link_get(&hdr->dst)) {
+/* #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{
+    } else{
         /* TODO: Check if there are members subscribed here */
         return pico_enqueue(&out, f);
     }
@@ -1283,14 +1092,14 @@
 }
 
 
-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 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)) {
+    if (pico_tree_findKey(&Routes, &test)) {
         pico_err = PICO_ERR_EINVAL;
         return -1;
     }
@@ -1332,7 +1141,7 @@
     }
 
     pico_tree_insert(&Routes, new);
-    /* dbg_route(); */
+    dbg_route();
     return 0;
 }
 
@@ -1350,7 +1159,7 @@
         pico_tree_delete(&Routes, found);
         PICO_FREE(found);
 
-        /* dbg_route(); */
+        dbg_route();
         return 0;
     }
 
@@ -1365,7 +1174,7 @@
     struct pico_ip4 network, gateway;
     char ipstr[30];
 
-    if(!dev) {
+    if (!dev) {
         pico_err = PICO_ERR_EINVAL;
         return -1;
     }
@@ -1375,8 +1184,7 @@
     test.dev = dev;
     /** XXX: Valid netmask / unicast address test **/
 
-    if(pico_tree_findKey(&Tree_dev_link, &test)) {
-        dbg("IPv4: Trying to assign an invalid address (in use)\n");
+    if (pico_tree_findKey(&Tree_dev_link, &test)) {
         pico_err = PICO_ERR_EADDRINUSE;
         return -1;
     }
@@ -1403,9 +1211,11 @@
 
     new->MCASTGroups->root = &LEAF;
     new->MCASTGroups->compare = ipv4_mcast_groups_cmp;
+#ifdef PICO_SUPPORT_IGMP
     new->mcast_compatibility = PICO_IGMPV3; /* default RFC 3376 $7.2.1 */
     new->mcast_last_query_interval = PICO_IGMP_QUERY_INTERVAL;
 #endif
+#endif
 
     pico_tree_insert(&Tree_dev_link, new);
 #ifdef PICO_SUPPORT_MCAST
@@ -1429,6 +1239,9 @@
     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;
 }
 
@@ -1437,8 +1250,7 @@
     struct pico_tree_node *index = NULL, *tmp = NULL;
     struct pico_ipv4_route *route = NULL;
 
-    pico_tree_foreach_safe(index, &Routes, tmp)
-    {
+    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);
@@ -1446,11 +1258,17 @@
     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) {
+    if (!dev) {
         pico_err = PICO_ERR_EINVAL;
         return -1;
     }
@@ -1477,8 +1295,7 @@
 
         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)
-        {
+        pico_tree_foreach_safe(index, found->MCASTGroups, _tmp) {
             g = index->keyValue;
             pico_tree_delete(found->MCASTGroups, g);
             PICO_FREE(g);
@@ -1488,6 +1305,9 @@
 
     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;
@@ -1508,13 +1328,12 @@
         return found;
 }
 
-struct pico_ipv4_link *pico_ipv4_link_by_dev(struct pico_device *dev)
+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)
-    {
+    pico_tree_foreach(index, &Tree_dev_link) {
         link = index->keyValue;
         if (link->dev == dev)
             return link;
@@ -1531,8 +1350,7 @@
     if (last == NULL)
         valid = 1;
 
-    pico_tree_foreach(index, &Tree_dev_link)
-    {
+    pico_tree_foreach(index, &Tree_dev_link) {
         link = index->keyValue;
         if (link->dev == dev) {
             if (last == link)
@@ -1544,10 +1362,10 @@
     return NULL;
 }
 
-struct pico_device *pico_ipv4_link_find(struct pico_ip4 *address)
+struct pico_device *MOCKABLE pico_ipv4_link_find(struct pico_ip4 *address)
 {
     struct pico_ipv4_link test, *found;
-    if(!address) {
+    if (!address) {
         pico_err = PICO_ERR_EINVAL;
         return NULL;
     }
@@ -1564,10 +1382,10 @@
 }
 
 
-
 static int pico_ipv4_rebound_large(struct pico_frame *f)
 {
-    uint32_t total_payload_written = 0;
+#ifdef PICO_SUPPORT_IPV4FRAG
+    uint16_t total_payload_written = 0;
     uint32_t len = f->transport_len;
     struct pico_frame *fr;
     struct pico_ip4 dst;
@@ -1575,7 +1393,6 @@
     hdr = (struct pico_ipv4_hdr *) f->net_hdr;
     dst.addr = hdr->src.addr;
 
-#ifdef PICO_SUPPORT_IPFRAG
     while(total_payload_written < len) {
         uint32_t space = (uint32_t)len - total_payload_written;
         if (space > PICO_IPV4_MAXPAYLOAD)
@@ -1588,15 +1405,19 @@
         }
 
         if (space + total_payload_written < len)
-            fr->frag |= short_be(PICO_IPV4_MOREFRAG);
+        {
+            fr->frag |= PICO_IPV4_MOREFRAG;
+        }
         else
-            fr->frag &= short_be(PICO_IPV4_FRAG_MASK);
+        {
+            fr->frag &= PICO_IPV4_FRAG_MASK;
+        }
 
-        fr->frag |= short_be((uint16_t)((total_payload_written) >> 3u));
+        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 += fr->transport_len;
+            total_payload_written = (uint16_t)((uint16_t)fr->transport_len + total_payload_written);
         } else {
             pico_frame_discard(fr);
             break;
@@ -1604,6 +1425,7 @@
     } /* while() */
     return (int)total_payload_written;
 #else
+    (void)f;
     return -1;
 #endif
 }
@@ -1612,7 +1434,7 @@
 {
     struct pico_ip4 dst;
     struct pico_ipv4_hdr *hdr;
-    if(!f) {
+    if (!f) {
         pico_err = PICO_ERR_EINVAL;
         return -1;
     }
@@ -1631,6 +1453,60 @@
     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;
@@ -1646,20 +1522,16 @@
     }
 
     f->dev = rt->link->dev;
-    hdr->ttl = (uint8_t)(hdr->ttl - 1);
-    if (hdr->ttl < 1) {
-        pico_notify_ttl_expired(f);
-        dbg(" ------------------- TTL EXPIRED\n");
+
+    if (pico_ipv4_pre_forward_checks(f) < 0)
         return -1;
-    }
-
-    hdr->crc++;
 
     pico_ipv4_nat_outbound(f, &rt->link->address);
 
     f->start = f->net_hdr;
-    if(f->dev->eth != NULL)
-        f->len -= PICO_SIZE_ETHHDR;
+
+    if (pico_ipv4_forward_check_dev(f) < 0)
+        return -1;
 
     pico_sendto_dev(f);
     return 0;
@@ -1695,8 +1567,7 @@
     struct pico_tree_node *index = NULL, *_tmp = NULL;
     struct pico_ipv4_link *link = NULL;
 
-    pico_tree_foreach_safe(index, &Tree_dev_link, _tmp)
-    {
+    pico_tree_foreach_safe(index, &Tree_dev_link, _tmp) {
         link = index->keyValue;
         if (dev == link->dev)
             pico_ipv4_link_del(dev, link->address);
--- a/modules/pico_ipv4.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_ipv4.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    .
@@ -17,9 +17,10 @@
 #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 0x4000
-#define PICO_IPV4_MOREFRAG 0x2000
-#define PICO_IPV4_FRAG_MASK 0x1FFF
+#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)
@@ -76,6 +77,19 @@
 };
 #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);
@@ -95,9 +109,11 @@
 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);
--- a/modules/pico_ipv6.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_ipv6.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    .
@@ -9,13 +9,29 @@
 #define _INCLUDE_PICO_IPV6
 #include "pico_addressing.h"
 #include "pico_protocol.h"
-
+#include "pico_ipv4.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_STRING 46
+
+#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
+
+
+#define PICO_IPV6_EXTHDR_OPT_ROUTER_ALERT 5
+#define PICO_IPV6_EXTHDR_OPT_ROUTER_ALERT_DATALEN 2
+
+#define HBH_LEN(hbh) ((((hbh->ext.hopbyhop.len + 1) << 3) - 2)) /* len in bytes, minus nxthdr and len byte */
 
 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;
@@ -43,33 +59,74 @@
     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;
+#ifdef PICO_SUPPORT_MCAST
+    struct pico_tree *MCASTGroups;
+    uint8_t mcast_compatibility;
+    uint8_t mcast_last_query_interval;
+#endif
+
+};
+union pico_link {
+    struct pico_ipv4_link ipv4;
+    struct pico_ipv6_link ipv6;
+};
+
+struct pico_ipv6_hbhoption {
+    uint8_t type;
+    uint8_t len;
+    uint8_t options[0];
+};
+#ifdef PICO_SUPPORT_MCAST
+struct pico_ipv6_mcast_group {
+    uint8_t filter_mode;
+    uint16_t reference_count;
+    struct pico_ip6 mcast_addr;
+    struct pico_tree MCASTSources;
+};
+#endif
+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;
 
-    union ipv6_ext_u {
-        struct {
+    PACKED_UNION_DEF ipv6_ext_u {
+        PEDANTIC_STRUCT_DEF hopbyhop_s {
             uint8_t len;
             uint8_t options[0];
         } hopbyhop;
 
-        struct {
+        PEDANTIC_STRUCT_DEF destopt_s {
             uint8_t len;
             uint8_t options[0];
         } destopt;
 
-        struct {
+        PEDANTIC_STRUCT_DEF routing_s {
             uint8_t len;
             uint8_t routtype;
             uint8_t segleft;
         } routing;
 
-        struct {
+        PEDANTIC_STRUCT_DEF fragmentation_s {
             uint8_t res;
-            uint8_t frm[2];
+            uint8_t om[2];
             uint8_t id[4];
-        } fragm;
+        } frag;
     } ext;
 };
 
@@ -78,19 +135,22 @@
 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 *dst, uint8_t proto);
-int pico_ipv6_rebound(struct pico_frame *f);
+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);
 
-int pico_ipv6_link_add(struct pico_device *dev, struct pico_ip6 address, struct pico_ip6 netmask);
+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);
@@ -98,6 +158,23 @@
 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);
+
+int pico_ipv6_mcast_join(struct pico_ip6 *mcast_link, struct pico_ip6 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *_MCASTFilter);
+int pico_ipv6_mcast_leave(struct pico_ip6 *mcast_link, struct pico_ip6 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *_MCASTFilter);
+
+struct pico_ipv6_link *pico_ipv6_get_default_mcastlink(void);
+
+int pico_ipv6_is_null_address(struct pico_ip6 * ip6);
 #endif
--- a/modules/pico_ipv6_nd.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_ipv6_nd.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
  *********************************************************************/
@@ -12,7 +12,7 @@
 #define PICO_ND_RETRANS_TIMER          1000 /* msec */
 
 struct pico_nd_hostvars {
-    uint32_t mtu;
+    uint8_t routing;
     uint8_t hoplimit;
     pico_time basetime;
     pico_time reachabletime;
@@ -20,10 +20,7 @@
 };
 
 void pico_ipv6_nd_init(void);
-struct pico_eth *pico_nd_get(struct pico_frame *f);
-int pico_nd_neigh_sol_recv(struct pico_frame *f);
-int pico_nd_neigh_adv_recv(struct pico_frame *f);
-int pico_nd_router_sol_recv(struct pico_frame *f);
-int pico_nd_router_adv_recv(struct pico_frame *f);
-int pico_nd_redirect_recv(struct pico_frame *f);
+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
--- a/modules/pico_mm.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_mm.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   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
@@ -9,7 +9,7 @@
 #ifndef _INCLUDE_PICO_MM
 #define _INCLUDE_PICO_MM
 
-#include <stdint.h>
+#include "pico_config.h"
 
 /*
  * Memory init function, this will create a memory manager instance
--- a/modules/pico_nat.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_nat.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    .
@@ -44,48 +44,76 @@
 
 static struct pico_ipv4_link *nat_link = NULL;
 
-static int nat_cmp_inbound(void *ka, void *kb)
+static int nat_cmp_natport(struct pico_nat_tuple *a, struct pico_nat_tuple *b)
 {
-    struct pico_nat_tuple *a = ka, *b = kb;
 
     if (a->nat_port < b->nat_port)
         return -1;
 
     if (a->nat_port > b->nat_port)
+
         return 1;
 
+    return 0;
+
+}
+
+static int nat_cmp_srcport(struct pico_nat_tuple *a, struct pico_nat_tuple *b)
+{
+
+    if (a->src_port < b->src_port)
+        return -1;
+
+    if (a->src_port > b->src_port)
+
+        return 1;
+
+    return 0;
+
+}
+
+static int nat_cmp_proto(struct pico_nat_tuple *a, struct pico_nat_tuple *b)
+{
     if (a->proto < b->proto)
         return -1;
 
     if (a->proto > b->proto)
         return 1;
 
-    return 0; /* identical */
+    return 0;
+}
+
+static int nat_cmp_address(struct pico_nat_tuple *a, struct pico_nat_tuple *b)
+{
+    return pico_ipv4_compare(&a->src_addr, &b->src_addr);
 }
 
+static int nat_cmp_inbound(void *ka, void *kb)
+{
+    struct pico_nat_tuple *a = ka, *b = kb;
+    int cport = nat_cmp_natport(a, b);
+    if (cport)
+        return cport;
+
+    return nat_cmp_proto(a, b);
+}
+
+
 static int nat_cmp_outbound(void *ka, void *kb)
 {
     struct pico_nat_tuple *a = ka, *b = kb;
-
-    if (a->src_addr.addr < b->src_addr.addr)
-        return -1;
+    int caddr, cport;
 
-    if (a->src_addr.addr > b->src_addr.addr)
-        return 1;
-
-    if (a->src_port < b->src_port)
-        return -1;
+    caddr = nat_cmp_address(a, b);
+    if (caddr)
+        return caddr;
 
-    if (a->src_port > b->src_port)
-        return 1;
+    cport = nat_cmp_srcport(a, b);
 
-    if (a->proto < b->proto)
-        return -1;
+    if (cport)
+        return cport;
 
-    if (a->proto > b->proto)
-        return 1;
-
-    return 0; /* identical */
+    return nat_cmp_proto(a, b);
 }
 
 PICO_TREE_DECLARE(NATOutbound, nat_cmp_outbound);
@@ -203,6 +231,29 @@
     return 0;
 }
 
+static struct pico_trans *pico_nat_generate_tuple_trans(struct pico_ipv4_hdr *net, struct pico_frame *f)
+{
+    struct pico_trans *trans = NULL;
+    switch (net->proto) {
+    case PICO_PROTO_TCP:
+    {
+        struct pico_tcp_hdr *tcp = (struct pico_tcp_hdr *)f->transport_hdr;
+        trans = (struct pico_trans *)&tcp->trans;
+        break;
+    }
+    case PICO_PROTO_UDP:
+    {
+        struct pico_udp_hdr *udp = (struct pico_udp_hdr *)f->transport_hdr;
+        trans = (struct pico_trans *)&udp->trans;
+        break;
+    }
+    case PICO_PROTO_ICMP4:
+        /* XXX: implement */
+        break;
+    }
+    return trans;
+}
+
 static struct pico_nat_tuple *pico_ipv4_nat_generate_tuple(struct pico_frame *f)
 {
     struct pico_trans *trans = NULL;
@@ -224,51 +275,38 @@
     if (!retry)
         return NULL;
 
-    switch (net->proto) {
-    case PICO_PROTO_TCP:
-    {
-        struct pico_tcp_hdr *tcp = (struct pico_tcp_hdr *)f->transport_hdr;
-        trans = (struct pico_trans *)&tcp->trans;
-        break;
-    }
-    case PICO_PROTO_UDP:
-    {
-        struct pico_udp_hdr *udp = (struct pico_udp_hdr *)f->transport_hdr;
-        trans = (struct pico_trans *)&udp->trans;
-        break;
-    }
-    case PICO_PROTO_ICMP4:
-        /* XXX: implement */
-        break;
-
-    default:
+    trans = pico_nat_generate_tuple_trans(net, f);
+    if(!trans)
         return NULL;
-    }
 
     return pico_ipv4_nat_add(net->dst, trans->dport, net->src, trans->sport, nat_link->address, nport, net->proto);
     /* XXX return pico_ipv4_nat_add(nat_link->address, port, net->src, trans->sport, net->proto); */
 }
 
-static int pico_ipv4_nat_snif_session(struct pico_nat_tuple *t, struct pico_frame *f, uint8_t direction)
+static inline void pico_ipv4_nat_set_tcp_flags(struct pico_nat_tuple *t, struct pico_frame *f, uint8_t direction)
+{
+    struct pico_tcp_hdr *tcp = (struct pico_tcp_hdr *)f->transport_hdr;
+    if (tcp->flags & PICO_TCP_SYN)
+        t->syn = 1;
+
+    if (tcp->flags & PICO_TCP_RST)
+        t->rst = 1;
+
+    if ((tcp->flags & PICO_TCP_FIN) && (direction == PICO_NAT_INBOUND))
+        t->fin_in = 1;
+
+    if ((tcp->flags & PICO_TCP_FIN) && (direction == PICO_NAT_OUTBOUND))
+        t->fin_out = 1;
+}
+
+static int pico_ipv4_nat_sniff_session(struct pico_nat_tuple *t, struct pico_frame *f, uint8_t direction)
 {
     struct pico_ipv4_hdr *net = (struct pico_ipv4_hdr *)f->net_hdr;
 
     switch (net->proto) {
     case PICO_PROTO_TCP:
     {
-        struct pico_tcp_hdr *tcp = (struct pico_tcp_hdr *)f->transport_hdr;
-        if (tcp->flags & PICO_TCP_SYN)
-            t->syn = 1;
-
-        if (tcp->flags & PICO_TCP_RST)
-            t->rst = 1;
-
-        if ((tcp->flags & PICO_TCP_FIN) && (direction == PICO_NAT_INBOUND))
-            t->fin_in = 1;
-
-        if ((tcp->flags & PICO_TCP_FIN) && (direction == PICO_NAT_OUTBOUND))
-            t->fin_out = 1;
-
+        pico_ipv4_nat_set_tcp_flags(t, f, direction);
         break;
     }
 
@@ -430,7 +468,7 @@
         return -1;
     }
 
-    pico_ipv4_nat_snif_session(tuple, f, PICO_NAT_INBOUND);
+    pico_ipv4_nat_sniff_session(tuple, f, PICO_NAT_INBOUND);
     net->crc = 0;
     net->crc = short_be(pico_checksum(net, f->net_len));
 
@@ -495,7 +533,7 @@
         return -1;
     }
 
-    pico_ipv4_nat_snif_session(tuple, f, PICO_NAT_OUTBOUND);
+    pico_ipv4_nat_sniff_session(tuple, f, PICO_NAT_OUTBOUND);
     net->crc = 0;
     net->crc = short_be(pico_checksum(net, f->net_len));
 
--- a/modules/pico_nat.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_nat.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    .
@@ -29,18 +29,23 @@
 #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;
 }
@@ -53,18 +58,29 @@
 
 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;
 }
--- a/modules/pico_slaacv4.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_slaacv4.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    Authors: Bogdan Lupu
@@ -11,12 +11,12 @@
 
 #ifdef PICO_SUPPORT_SLAACV4
 
-#define SLAACV4_ADDRESS  0xa9fe0000
-#define SLAACV4_MASK     0x0000FFFF
-#define SLAACV4_MINRANGE 0x00000100
-#define SLAACV4_MAXRANGE 0x0000FD00
+#define SLAACV4_NETWORK  ((long_be(0xa9fe0000)))
+#define SLAACV4_NETMASK  ((long_be(0xFFFF0000)))
+#define SLAACV4_MINRANGE  (0x00000100) /* In host order */
+#define SLAACV4_MAXRANGE  (0x0000FDFF) /* In host order */
 
-#define SLAACV4_CREATE_IPV4(seed) (((seed % SLAACV4_MAXRANGE) + SLAACV4_MINRANGE) & SLAACV4_MASK) | SLAACV4_ADDRESS
+#define SLAACV4_CREATE_IPV4(seed) ((long_be((seed % SLAACV4_MAXRANGE) + SLAACV4_MINRANGE) & ~SLAACV4_NETMASK) | SLAACV4_NETWORK)
 
 #define PROBE_WAIT           1 /* delay between two tries during claim */
 #define PROBE_NB             3 /* number of probe packets during claim */
@@ -172,7 +172,7 @@
         return -1;
     }
 
-    ip.addr = long_be(pico_slaacv4_getip(dev, 0));
+    ip.addr = pico_slaacv4_getip(dev, 0);
 
     pico_slaacv4_init_cookie(&ip, dev, &slaacv4_local, cb);
     pico_arp_register_ipconflict(&ip, &dev->eth->mac, pico_slaacv4_receive_ipconflict);
--- a/modules/pico_slaacv4.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_slaacv4.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    Authors: Bogdan Lupu
--- a/modules/pico_socket_tcp.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_socket_tcp.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,10 +1,12 @@
-#include <stdint.h>
+#include "pico_config.h"
 #include "pico_socket.h"
 #include "pico_ipv4.h"
 #include "pico_ipv6.h"
 #include "pico_tcp.h"
+#include "pico_socket_tcp.h"
 
-int pico_getsockopt_tcp(struct pico_socket *s, int option, void *value)
+
+static int sockopt_validate_args(struct pico_socket *s,  void *value)
 {
     if (!value) {
         pico_err = PICO_ERR_EINVAL;
@@ -16,6 +18,14 @@
         return -1;
     }
 
+    return 0;
+}
+
+int pico_getsockopt_tcp(struct pico_socket *s, int option, void *value)
+{
+    if (sockopt_validate_args(s, value) < 0)
+        return -1;
+
 #ifdef PICO_SUPPORT_TCP
     if (option == PICO_TCP_NODELAY) {
         /* state of the NODELAY option */
@@ -26,7 +36,7 @@
         return pico_tcp_get_bufsize_in(s, (uint32_t *)value);
     }
 
-    if (option == PICO_SOCKET_OPT_SNDBUF) {
+    else if (option == PICO_SOCKET_OPT_SNDBUF) {
         return pico_tcp_get_bufsize_out(s, (uint32_t *)value);
     }
 
@@ -34,43 +44,58 @@
     return -1;
 }
 
+static void tcp_set_nagle_option(struct pico_socket *s, void *value)
+{
+    int *val = (int*)value;
+    if (*val > 0) {
+        dbg("setsockopt: Nagle algorithm disabled.\n");
+        PICO_SOCKET_SETOPT_EN(s, PICO_SOCKET_OPT_TCPNODELAY);
+    } else {
+        dbg("setsockopt: Nagle algorithm enabled.\n");
+        PICO_SOCKET_SETOPT_DIS(s, PICO_SOCKET_OPT_TCPNODELAY);
+    }
+}
+
 int pico_setsockopt_tcp(struct pico_socket *s, int option, void *value)
 {
-    if (!value) {
-        pico_err = PICO_ERR_EINVAL;
+    if (sockopt_validate_args(s, value) < 0)
         return -1;
-    }
-
-    if (s->proto->proto_number != PICO_PROTO_TCP) {
-        pico_err = PICO_ERR_EPROTONOSUPPORT;
-        return -1;
-    }
 
 #ifdef PICO_SUPPORT_TCP
     if (option ==  PICO_TCP_NODELAY) {
-        int *val = (int*)value;
-        if (*val > 0) {
-            dbg("setsockopt: Nagle algorithm disabled.\n");
-            PICO_SOCKET_SETOPT_EN(s, PICO_SOCKET_OPT_TCPNODELAY);
-        } else {
-            dbg("setsockopt: Nagle algorithm enabled.\n");
-            PICO_SOCKET_SETOPT_DIS(s, PICO_SOCKET_OPT_TCPNODELAY);
-        }
-
+        tcp_set_nagle_option(s, value);
         return 0;
     }
-
-    if (option == PICO_SOCKET_OPT_RCVBUF) {
+    else if (option == PICO_SOCKET_OPT_RCVBUF) {
         uint32_t *val = (uint32_t*)value;
         pico_tcp_set_bufsize_in(s, *val);
         return 0;
     }
-
-    if (option == PICO_SOCKET_OPT_SNDBUF) {
+    else if (option == PICO_SOCKET_OPT_SNDBUF) {
         uint32_t *val = (uint32_t*)value;
         pico_tcp_set_bufsize_out(s, *val);
         return 0;
     }
+    else if (option == PICO_SOCKET_OPT_KEEPCNT) {
+        uint32_t *val = (uint32_t*)value;
+        pico_tcp_set_keepalive_probes(s, *val);
+        return 0;
+    }
+    else if (option == PICO_SOCKET_OPT_KEEPIDLE) {
+        uint32_t *val = (uint32_t*)value;
+        pico_tcp_set_keepalive_time(s, *val);
+        return 0;
+    }
+    else if (option == PICO_SOCKET_OPT_KEEPINTVL) {
+        uint32_t *val = (uint32_t*)value;
+        pico_tcp_set_keepalive_intvl(s, *val);
+        return 0;
+    }
+    else if (option == PICO_SOCKET_OPT_LINGER) {
+        uint32_t *val = (uint32_t*)value;
+        pico_tcp_set_linger(s, *val);
+        return 0;
+    }
 
 #endif
     pico_err = PICO_ERR_EINVAL;
@@ -81,7 +106,7 @@
 {
 #ifdef PICO_SUPPORT_TCP
     /* for tcp sockets go further and clean the sockets inside queue */
-    if(sock->proto == &pico_proto_tcp)
+    if(is_sock_tcp(sock))
         pico_tcp_cleanup_queues(sock);
 
 #endif
@@ -97,74 +122,102 @@
 #endif
 }
 
+static struct pico_socket *socket_tcp_deliver_ipv4(struct pico_socket *s, struct pico_frame *f)
+{
+    struct pico_socket *found = NULL;
+    #ifdef PICO_SUPPORT_IPV4
+    struct pico_ip4 s_local, s_remote, p_src, p_dst;
+    struct pico_ipv4_hdr *ip4hdr = (struct pico_ipv4_hdr*)(f->net_hdr);
+    struct pico_trans *tr = (struct pico_trans *) f->transport_hdr;
+    s_local.addr = s->local_addr.ip4.addr;
+    s_remote.addr = s->remote_addr.ip4.addr;
+    p_src.addr = ip4hdr->src.addr;
+    p_dst.addr = ip4hdr->dst.addr;
+    if ((s->remote_port == tr->sport) && /* remote port check */
+        (s_remote.addr == p_src.addr) && /* remote addr check */
+        ((s_local.addr == PICO_IPV4_INADDR_ANY) || (s_local.addr == p_dst.addr))) { /* Either local socket is ANY, or matches dst */
+        found = s;
+        return found;
+    } else if ((s->remote_port == 0)  && /* not connected... listening */
+               ((s_local.addr == PICO_IPV4_INADDR_ANY) || (s_local.addr == p_dst.addr))) { /* Either local socket is ANY, or matches dst */
+        /* listen socket */
+        found = s;
+    }
+
+    #endif
+    return found;
+}
+
+static struct pico_socket *socket_tcp_deliver_ipv6(struct pico_socket *s, struct pico_frame *f)
+{
+    struct pico_socket *found = NULL;
+    #ifdef PICO_SUPPORT_IPV6
+    struct pico_trans *tr = (struct pico_trans *) f->transport_hdr;
+    struct pico_ip6 s_local = {{0}}, s_remote = {{0}}, p_src = {{0}}, p_dst = {{0}};
+    struct pico_ipv6_hdr *ip6hdr = (struct pico_ipv6_hdr *)(f->net_hdr);
+    s_local = s->local_addr.ip6;
+    s_remote = s->remote_addr.ip6;
+    p_src = ip6hdr->src;
+    p_dst = ip6hdr->dst;
+    if ((s->remote_port == tr->sport) &&
+        (!memcmp(s_remote.addr, p_src.addr, PICO_SIZE_IP6)) &&
+        ((!memcmp(s_local.addr, PICO_IP6_ANY, PICO_SIZE_IP6)) || (!memcmp(s_local.addr, p_dst.addr, PICO_SIZE_IP6)))) {
+        found = s;
+        return found;
+    } else if ((s->remote_port == 0)  && /* not connected... listening */
+               ((!memcmp(s_local.addr, PICO_IP6_ANY, PICO_SIZE_IP6)) || (!memcmp(s_local.addr, p_dst.addr, PICO_SIZE_IP6)))) {
+        /* listen socket */
+        found = s;
+    }
+
+    #else
+    (void) s;
+    (void) f;
+    #endif
+    return found;
+}
+
+static int socket_tcp_do_deliver(struct pico_socket *s, struct pico_frame *f)
+{
+    if (s != NULL) {
+        pico_tcp_input(s, f);
+        if ((s->ev_pending) && s->wakeup) {
+            s->wakeup(s->ev_pending, s);
+            if(!s->parent)
+                s->ev_pending = 0;
+        }
+
+        return 0;
+    }
+
+    dbg("TCP SOCKET> Not s.\n");
+    return -1;
+}
+
 int pico_socket_tcp_deliver(struct pico_sockport *sp, struct pico_frame *f)
 {
     struct pico_socket *found = NULL;
     struct pico_tree_node *index = NULL;
     struct pico_tree_node *_tmp;
-    struct pico_trans *tr = (struct pico_trans *) f->transport_hdr;
     struct pico_socket *s = NULL;
 
 
     pico_tree_foreach_safe(index, &sp->socks, _tmp){
         s = index->keyValue;
         /* 4-tuple identification of socket (port-IP) */
-        #ifdef PICO_SUPPORT_IPV4
         if (IS_IPV4(f)) {
-            struct pico_ip4 s_local, s_remote, p_src, p_dst;
-            struct pico_ipv4_hdr *ip4hdr = (struct pico_ipv4_hdr*)(f->net_hdr);
-            s_local.addr = s->local_addr.ip4.addr;
-            s_remote.addr = s->remote_addr.ip4.addr;
-            p_src.addr = ip4hdr->src.addr;
-            p_dst.addr = ip4hdr->dst.addr;
-            if ((s->remote_port == tr->sport) && /* remote port check */
-                (s_remote.addr == p_src.addr) && /* remote addr check */
-                ((s_local.addr == PICO_IPV4_INADDR_ANY) || (s_local.addr == p_dst.addr))) { /* Either local socket is ANY, or matches dst */
-                found = s;
-                break;
-            } else if ((s->remote_port == 0)  && /* not connected... listening */
-                       ((s_local.addr == PICO_IPV4_INADDR_ANY) || (s_local.addr == p_dst.addr))) { /* Either local socket is ANY, or matches dst */
-                /* listen socket */
-                found = s;
-            }
+            found = socket_tcp_deliver_ipv4(s, f);
         }
 
-        #endif
-        #ifdef PICO_SUPPORT_IPV6
         if (IS_IPV6(f)) {
-            struct pico_ip6 s_local = {{0}}, s_remote = {{0}}, p_src = {{0}}, p_dst = {{0}};
-            struct pico_ipv6_hdr *ip6hdr = (struct pico_ipv6_hdr *)(f->net_hdr);
-            s_local = s->local_addr.ip6;
-            s_remote = s->remote_addr.ip6;
-            p_src = ip6hdr->src;
-            p_dst = ip6hdr->dst;
-            if ((s->remote_port == tr->sport) &&
-                (!memcmp(s_remote.addr, p_src.addr, PICO_SIZE_IP6)) &&
-                ((!memcmp(s_local.addr, PICO_IP6_ANY, PICO_SIZE_IP6)) || (!memcmp(s_local.addr, p_dst.addr, PICO_SIZE_IP6)))) {
-                found = s;
-                break;
-            } else if ((s->remote_port == 0)  && /* not connected... listening */
-                       ((!memcmp(s_local.addr, PICO_IP6_ANY, PICO_SIZE_IP6)) || (!memcmp(s_local.addr, p_dst.addr, PICO_SIZE_IP6)))) {
-                /* listen socket */
-                found = s;
-            }
+            found = socket_tcp_deliver_ipv6(s, f);
         }
 
-        #endif
+        if (found)
+            break;
     } /* FOREACH */
-    if (found != NULL) {
-        pico_tcp_input(found, f);
-        if ((found->ev_pending) && found->wakeup) {
-            found->wakeup(found->ev_pending, found);
-            if(!found->parent)
-                found->ev_pending = 0;
-        }
 
-        return 0;
-    } else {
-        dbg("TCP SOCKET> Not found.\n");
-        return -1;
-    }
+    return socket_tcp_do_deliver(found, f);
 }
 
 struct pico_socket *pico_socket_tcp_open(uint16_t family)
@@ -207,6 +260,8 @@
 void transport_flags_update(struct pico_frame *f, struct pico_socket *s)
 {
 #ifdef PICO_SUPPORT_TCP
-    pico_tcp_flags_update(f, s);
+    if(is_sock_tcp(s))
+        pico_tcp_flags_update(f, s);
+
 #endif
 }
--- a/modules/pico_socket_udp.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_socket_udp.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,9 +1,10 @@
-#include <stdint.h>
+#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))
 
@@ -19,69 +20,47 @@
     }
 
     s->proto = &pico_proto_udp;
-    s->q_in.overhead = s->q_out.overhead = UDP_FRAME_OVERHEAD;
+    s->q_in.overhead = UDP_FRAME_OVERHEAD;
+    s->q_out.overhead = UDP_FRAME_OVERHEAD;
 #endif
     return s;
 }
 
 
 #ifdef PICO_SUPPORT_IPV4
-static int pico_socket_udp_deliver_ipv4(struct pico_socket *s, struct pico_frame *f)
+static inline int pico_socket_udp_deliver_ipv4_mcast_initial_checks(struct pico_socket *s, struct pico_frame *f)
 {
-    struct pico_ip4 s_local, p_dst;
+    struct pico_ip4 p_dst;
     struct pico_ipv4_hdr *ip4hdr;
-    struct pico_frame *cpy;
-    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)) {
-        struct pico_device *dev = pico_ipv4_link_find(&s->local_addr.ip4);
-        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 ((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);
+    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;
 
-        }
-    } else if ((s_local.addr == PICO_IPV4_INADDR_ANY) || (s_local.addr == p_dst.addr))
-    { /* 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);
-        }
+    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;
     }
 
-    pico_frame_discard(f);
     return 0;
 }
-#endif
+
 
-#ifdef PICO_SUPPORT_IPV6
-static int pico_socket_udp_deliver_ipv6(struct pico_socket *s, struct pico_frame *f)
+static int pico_socket_udp_deliver_ipv4_mcast(struct pico_socket *s, struct pico_frame *f)
 {
-    struct pico_ip6 s_local, p_dst;
-    struct pico_ipv6_hdr *ip6hdr;
+    struct pico_ip4 s_local;
     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))) {
+    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;
@@ -92,11 +71,104 @@
         }
         else
             pico_frame_discard(cpy);
-    } else if (pico_ipv6_is_unspecified(s->local_addr.ip6.addr) || (pico_ipv6_compare(&s_local, &p_dst) == 0))
+    }
+
+    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))) {
+        int retval = pico_socket_udp_deliver_ipv6_mcast(s, f);
+        pico_frame_discard(f);
+        return retval;
+    } 
+    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)
+        {
+            pico_frame_discard(f);
             return -1;
+        }
 
         if (pico_enqueue(&s->q_in, cpy) > 0) {
             if (s->wakeup)
@@ -124,10 +196,12 @@
 #ifdef PICO_SUPPORT_IPV4
             return pico_socket_udp_deliver_ipv4(s, f);
 #endif
-        } else {
+        } 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);
--- a/modules/pico_socket_udp.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_socket_udp.h	Mon Sep 28 13:16:18 2015 +0200
@@ -8,7 +8,7 @@
 #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)
+#   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)
--- a/modules/pico_tcp.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_tcp.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    .
@@ -21,16 +21,16 @@
 #define SEQN(f) ((f) ? (long_be(((struct pico_tcp_hdr *)((f)->transport_hdr))->seq)) : 0)
 #define ACKN(f) ((f) ? (long_be(((struct pico_tcp_hdr *)((f)->transport_hdr))->ack)) : 0)
 
-#define TCP_TIME (PICO_TIME_MS())
+#define TCP_TIME (pico_time)(PICO_TIME_MS())
 
 #define PICO_TCP_RTO_MIN (70)
 #define PICO_TCP_RTO_MAX (120000)
 #define PICO_TCP_IW          2
-#define PICO_TCP_SYN_TO  1000u
+#define PICO_TCP_SYN_TO  2000u
 #define PICO_TCP_ZOMBIE_TO 30000
 
 #define PICO_TCP_MAX_RETRANS         10
-#define PICO_TCP_MAX_CONNECT_RETRIES 7
+#define PICO_TCP_MAX_CONNECT_RETRIES 3
 
 #define PICO_TCP_LOOKAHEAD      0x00
 #define PICO_TCP_FIRST_DUPACK   0x01
@@ -40,6 +40,8 @@
 #define PICO_TCP_UNREACHABLE    0x05
 #define PICO_TCP_WINDOW_FULL    0x06
 
+#define ONE_GIGABYTE ((uint32_t)(1024UL * 1024UL * 1024UL))
+
 /* check if the Nagle algorithm is enabled on the socket */
 #define IS_NAGLE_ENABLED(s)     (!(!(!(s->opt_flags & (1u << PICO_SOCKET_OPT_TCPNODELAY)))))
 /* check if tcp connection is "idle" according to Nagle (RFC 896) */
@@ -61,43 +63,9 @@
 
 #ifdef PICO_SUPPORT_MUTEX
 static void *Mutex = NULL;
-#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)
-
-#else
-#define PICOTCP_MUTEX_LOCK(x) do {} while(0)
-#define PICOTCP_MUTEX_UNLOCK(x) do {} while(0)
 #endif
 
 
-static /* inline*/ int32_t 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;
-}
 
 /* Input segment, used to keep only needed data, not the full frame */
 struct tcp_input_segment
@@ -112,13 +80,13 @@
 static int input_segment_compare(void *ka, void *kb)
 {
     struct tcp_input_segment *a = ka, *b = kb;
-    return seq_compare(a->seq, b->seq);
+    return pico_seq_compare(a->seq, b->seq);
 }
 
 static struct tcp_input_segment *segment_from_frame(struct pico_frame *f)
 {
     struct tcp_input_segment *seg = PICO_ZALLOC(sizeof(struct tcp_input_segment));
-    if(!seg)
+    if ((!seg) || (!f->payload_len))
         return NULL;
 
     seg->payload = PICO_ZALLOC(f->payload_len);
@@ -137,7 +105,7 @@
 static int segment_compare(void *ka, void *kb)
 {
     struct pico_frame *a = ka, *b = kb;
-    return seq_compare(SEQN(a), SEQN(b));
+    return pico_seq_compare(SEQN(a), SEQN(b));
 }
 
 struct pico_tcp_queue
@@ -193,24 +161,19 @@
     }
 }
 
-static int32_t pico_enqueue_segment(struct pico_tcp_queue *tq, void *f)
+static uint16_t enqueue_segment_len(struct pico_tcp_queue *tq, void *f)
+{
+    if (IS_INPUT_QUEUE(tq)) {
+        return ((struct tcp_input_segment *)f)->payload_len;
+    } else {
+        return (uint16_t)(((struct pico_frame *)f)->buffer_len);
+    }
+}
+
+
+static int32_t do_enqueue_segment(struct pico_tcp_queue *tq, void *f, uint16_t payload_len)
 {
     int32_t ret = -1;
-    uint16_t payload_len;
-
-    if (!f)
-        return -1;
-
-    payload_len = (uint16_t)((IS_INPUT_QUEUE(tq)) ?
-                             (((struct tcp_input_segment *)f)->payload_len) :
-                             (((struct pico_frame *)f)->buffer_len));
-
-    if (payload_len <= 0) {
-        tcp_dbg("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TRIED TO ENQUEUE INVALID SEGMENT!\n");
-        ret = -2; /* Fail harder */
-        goto out;
-    }
-
     PICOTCP_MUTEX_LOCK(Mutex);
     if ((tq->size + payload_len) > tq->max_size)
     {
@@ -235,6 +198,24 @@
     return ret;
 }
 
+static int32_t pico_enqueue_segment(struct pico_tcp_queue *tq, void *f)
+{
+    uint16_t payload_len;
+
+    if (!f)
+        return -1;
+
+    payload_len = enqueue_segment_len(tq, f);
+
+
+    if (payload_len == 0) {
+        tcp_dbg("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TRIED TO ENQUEUE INVALID SEGMENT!\n");
+        return -1;
+    }
+
+    return do_enqueue_segment(tq, f, payload_len);
+}
+
 static void pico_discard_segment(struct pico_tcp_queue *tq, void *f)
 {
     void *f1;
@@ -313,12 +294,24 @@
     uint8_t scale_ok;
     struct tcp_sack_block *sacks;
     uint8_t jumbo;
+    uint32_t linger_timeout;
 
     /* Transmission */
     uint8_t x_mode;
     uint8_t dupacks;
     uint8_t backoff;
     uint8_t localZeroWindow;
+
+    /* Keepalive */
+    struct pico_timer *keepalive_tmr;
+    pico_time ack_timestamp;
+    uint32_t ka_time;
+    uint32_t ka_intvl;
+    uint32_t ka_probes;
+    uint32_t ka_retries_count;
+
+    /* FIN timer */
+    struct pico_timer *fin_tmr;
 };
 
 /* Queues */
@@ -357,14 +350,14 @@
         void *cur = head;
 
         if (IS_INPUT_QUEUE(q))
-            seq_result = seq_compare(((struct tcp_input_segment *)head)->seq + ((struct tcp_input_segment *)head)->payload_len, seq);
+            seq_result = pico_seq_compare(((struct tcp_input_segment *)head)->seq + ((struct tcp_input_segment *)head)->payload_len, seq);
         else
-            seq_result = seq_compare(SEQN((struct pico_frame *)head) + ((struct pico_frame *)head)->payload_len, seq);
+            seq_result = pico_seq_compare(SEQN((struct pico_frame *)head) + ((struct pico_frame *)head)->payload_len, seq);
 
         if (seq_result <= 0)
         {
             head = next_segment(q, cur);
-            tcp_dbg("Releasing %08x, len: %d\n", SEQN((struct pico_frame *)head), ((struct pico_frame *)head)->payload_len);
+            //tcp_dbg("Releasing %08x, len: %d\n", SEQN((struct pico_frame *)head), ((struct pico_frame *)head)->payload_len);
             pico_discard_segment(q, cur);
             ret++;
         } else {
@@ -388,13 +381,13 @@
         f = idx->keyValue;
 
         if (IS_INPUT_QUEUE(q))
-            seq_result = seq_compare(((struct tcp_input_segment *)f)->seq + ((struct tcp_input_segment *)f)->payload_len, seq);
+            seq_result = pico_seq_compare(((struct tcp_input_segment *)f)->seq + ((struct tcp_input_segment *)f)->payload_len, seq);
         else
-            seq_result = seq_compare(SEQN((struct pico_frame *)f) + ((struct pico_frame *)f)->payload_len, seq);
+            seq_result = pico_seq_compare(SEQN((struct pico_frame *)f) + ((struct pico_frame *)f)->payload_len, seq);
 
         if (seq_result <= 0) {
             tcp_dbg("Releasing %p\n", f);
-            if(seq_result == 0)
+            if ((seq_result == 0) && !IS_INPUT_QUEUE(q))
                 *timestamp = ((struct pico_frame *)f)->timestamp;
 
             pico_discard_segment(q, f);
@@ -422,8 +415,8 @@
         pseudo.src.addr = s->local_addr.ip4.addr;
         pseudo.dst.addr = s->remote_addr.ip4.addr;
     } else {
-        /* Case of incomming frame */
-        /* dbg("TCP CRC: on incomming frame\n"); */
+        /* Case of incoming frame */
+        /* dbg("TCP CRC: on incoming frame\n"); */
         pseudo.src.addr = hdr->src.addr;
         pseudo.dst.addr = hdr->dst.addr;
     }
@@ -450,7 +443,7 @@
         pseudo.src = s->local_addr.ip6;
         pseudo.dst = s->remote_addr.ip6;
     } else {
-        /* Case of incomming frame */
+        /* Case of incoming frame */
         pseudo.src = ipv6_hdr->src;
         pseudo.dst = ipv6_hdr->dst;
     }
@@ -465,22 +458,32 @@
 }
 #endif
 
+#ifdef PICO_SUPPORT_IPV4
+static inline int checksum_is_ipv4(struct pico_frame *f)
+{
+    return (IS_IPV4(f) || (f->sock && (f->sock->net == &pico_proto_ipv4)));
+}
+#endif
+
+#ifdef PICO_SUPPORT_IPV6
+static inline int checksum_is_ipv6(struct pico_frame *f)
+{
+    return ((IS_IPV6(f)) || (f->sock && (f->sock->net == &pico_proto_ipv6)));
+}
+#endif
+
 uint16_t pico_tcp_checksum(struct pico_frame *f)
 {
     (void)f;
+
     #ifdef PICO_SUPPORT_IPV4
-    if (IS_IPV4(f))
-        return pico_tcp_checksum_ipv4(f);
-
-    if (f->sock && (f->sock->net == &pico_proto_ipv4))
+    if (checksum_is_ipv4(f))
         return pico_tcp_checksum_ipv4(f);
 
     #endif
+
     #ifdef PICO_SUPPORT_IPV6
-    if (IS_IPV6(f))
-        return pico_tcp_checksum_ipv6(f);
-
-    if (f->sock && (f->sock->net == &pico_proto_ipv6))
+    if (checksum_is_ipv6(f))
         return pico_tcp_checksum_ipv6(f);
 
     #endif
@@ -502,7 +505,7 @@
     }
 
     if (f->payload_len > 0) {
-        if (seq_compare(SEQN(f) + f->payload_len, t->snd_nxt) > 0) {
+        if (pico_seq_compare(SEQN(f) + f->payload_len, t->snd_nxt) > 0) {
             t->snd_nxt = SEQN(f) + f->payload_len;
             tcp_dbg("%s: snd_nxt is now %08x\n", __FUNCTION__, t->snd_nxt);
         }
@@ -537,6 +540,28 @@
     return long_be(_paws);
 }
 
+static inline void tcp_add_sack_option(struct pico_socket_tcp *ts, struct pico_frame *f, uint16_t flags, uint32_t *ii)
+{
+    if (flags & PICO_TCP_ACK) {
+        struct tcp_sack_block *sb;
+        uint32_t len_off;
+
+        if (ts->sack_ok && ts->sacks) {
+            f->start[(*ii)++] = PICO_TCP_OPTION_SACK;
+            len_off = *ii;
+            f->start[(*ii)++] = PICO_TCPOPTLEN_SACK;
+            while(ts->sacks) {
+                sb = ts->sacks;
+                ts->sacks = sb->next;
+                memcpy(f->start + *ii, sb, 2 * sizeof(uint32_t));
+                *ii += (2 * (uint32_t)sizeof(uint32_t));
+                f->start[len_off] = (uint8_t)(f->start[len_off] + (2 * sizeof(uint32_t)));
+                PICO_FREE(sb);
+            }
+        }
+    }
+}
+
 static void tcp_add_options(struct pico_socket_tcp *ts, struct pico_frame *f, uint16_t flags, uint16_t optsiz)
 {
     uint32_t tsval = long_be((uint32_t)TCP_TIME);
@@ -568,24 +593,7 @@
         i += 4;
     }
 
-    if (flags & PICO_TCP_ACK) {
-        struct tcp_sack_block *sb;
-        uint32_t len_off;
-
-        if (ts->sack_ok && ts->sacks) {
-            f->start[i++] = PICO_TCP_OPTION_SACK;
-            len_off = i;
-            f->start[i++] = PICO_TCPOPTLEN_SACK;
-            while(ts->sacks) {
-                sb = ts->sacks;
-                ts->sacks = sb->next;
-                memcpy(f->start + i, sb, 2 * sizeof(uint32_t));
-                i += (2 * (uint32_t)sizeof(uint32_t));
-                f->start[len_off] = (uint8_t)(f->start[len_off] + (2 * sizeof(uint32_t)));
-                PICO_FREE(sb);
-            }
-        }
-    }
+    tcp_add_sack_option(ts, f, flags, &i);
 
     if (i < optsiz)
         f->start[ optsiz - 1 ] = PICO_TCP_OPTION_END;
@@ -637,24 +645,8 @@
 static void tcp_send_ack(struct pico_socket_tcp *t);
 #define tcp_send_windowUpdate(t) (tcp_send_ack(t))
 
-static void tcp_set_space(struct pico_socket_tcp *t)
+static inline void tcp_set_space_check_winupdate(struct pico_socket_tcp *t, int32_t space, uint32_t shift)
 {
-    int32_t space;
-    uint32_t shift = 0;
-
-    if (t->tcpq_in.max_size == 0) {
-        space = 1024 * 1024 * 1024; /* One Gigabyte, for unlimited sockets. */
-    } else {
-        space = (int32_t)(t->tcpq_in.max_size - t->tcpq_in.size);
-    }
-
-    if (space < 0)
-        space = 0;
-
-    while(space > 0xFFFF) {
-        space = (int32_t)(((uint32_t)space >> 1u));
-        shift++;
-    }
     if (((uint32_t)space != t->wnd) || (shift != t->wnd_scale) || ((space - t->wnd) > (int32_t)((uint32_t)space >> 2u))) {
         t->wnd = (uint16_t)space;
         t->wnd_scale = (uint16_t)shift;
@@ -669,6 +661,27 @@
     }
 }
 
+static void tcp_set_space(struct pico_socket_tcp *t)
+{
+    int32_t space;
+    uint32_t shift = 0;
+
+    if (t->tcpq_in.max_size == 0) {
+        space = ONE_GIGABYTE;
+    } else {
+        space = (int32_t)(t->tcpq_in.max_size - t->tcpq_in.size);
+    }
+
+    if (space < 0)
+        space = 0;
+
+    while(space > 0xFFFF) {
+        space = (int32_t)(((uint32_t)space >> 1u));
+        shift++;
+    }
+    tcp_set_space_check_winupdate(t, space, shift);
+}
+
 /* Return 32-bit aligned option size */
 static uint16_t tcp_options_size(struct pico_socket_tcp *t, uint16_t flags)
 {
@@ -709,34 +722,37 @@
 
 }
 
+static inline int tcp_sack_marker(struct pico_frame *f, uint32_t start, uint32_t end, uint16_t *count)
+{
+    int cmp;
+    cmp = pico_seq_compare(SEQN(f), start);
+    if (cmp > 0)
+        return 0;
+
+    if (cmp == 0) {
+        cmp = pico_seq_compare(SEQN(f) + f->payload_len, end);
+        if (cmp > 0) {
+            tcp_dbg("Invalid SACK: ignoring.\n");
+        }
+
+        tcp_dbg("Marking (by SACK) segment %08x BLK:[%08x::%08x]\n", SEQN(f), start, end);
+        f->flags |= PICO_FRAME_FLAG_SACKED;
+        (*count)++;
+    }
+
+    return cmp;
+}
+
 static void tcp_process_sack(struct pico_socket_tcp *t, uint32_t start, uint32_t end)
 {
     struct pico_frame *f;
     struct pico_tree_node *index, *temp;
-    int cmp;
     uint16_t count = 0;
 
     pico_tree_foreach_safe(index, &t->tcpq_out.pool, temp){
         f = index->keyValue;
-        cmp = seq_compare(SEQN(f), start);
-        if (cmp > 0)
+        if (tcp_sack_marker(f, start, end, &count) == 0)
             goto done;
-
-        if (cmp == 0) {
-            cmp = seq_compare(SEQN(f) + f->payload_len, end);
-            if (cmp > 0) {
-                tcp_dbg("Invalid SACK: ignoring.\n");
-            }
-
-            tcp_dbg("Marking (by SACK) segment %08x BLK:[%08x::%08x]\n", SEQN(f), start, end);
-            f->flags |= PICO_FRAME_FLAG_SACKED;
-            count++;
-
-            if (cmp == 0) {
-                /* that was last segment sacked. Job done */
-                goto done;
-            }
-        }
     }
 
 done:
@@ -778,6 +794,63 @@
     }
 }
 
+static int tcpopt_len_check(uint32_t *idx, uint8_t len, uint8_t expected)
+{
+    if (len != expected) {
+        *idx = *idx + len - 2;
+        return -1;
+    }
+
+    return 0;
+}
+
+static inline void tcp_parse_option_ws(struct pico_socket_tcp *t, uint8_t len, uint8_t *opt, uint32_t *idx)
+{
+    if (tcpopt_len_check(idx, len, PICO_TCPOPTLEN_WS) < 0)
+        return;
+
+    t->recv_wnd_scale = opt[(*idx)++];
+    tcp_dbg_options("TCP Window scale: received %d\n", t->recv_wnd_scale);
+
+}
+
+static inline void tcp_parse_option_sack_ok(struct pico_socket_tcp *t, struct pico_frame *f, uint8_t len, uint32_t *idx)
+{
+    if (tcpopt_len_check(idx, len, PICO_TCPOPTLEN_SACK_OK) < 0)
+        return;
+
+    if(((struct pico_tcp_hdr *)(f->transport_hdr))->flags & PICO_TCP_SYN )
+        t->sack_ok = 1;
+}
+
+static inline void tcp_parse_option_mss(struct pico_socket_tcp *t, uint8_t len, uint8_t *opt, uint32_t *idx)
+{
+    uint16_t mss;
+    if (tcpopt_len_check(idx, len, PICO_TCPOPTLEN_MSS) < 0)
+        return;
+
+    t->mss_ok = 1;
+    mss = short_from(opt + *idx);
+    *idx += (uint32_t)sizeof(uint16_t);
+    if (t->mss > short_be(mss))
+        t->mss = short_be(mss);
+}
+
+static inline void tcp_parse_option_timestamp(struct pico_socket_tcp *t, struct pico_frame *f, uint8_t len, uint8_t *opt, uint32_t *idx)
+{
+    uint32_t tsval, tsecr;
+    if (tcpopt_len_check(idx, len, PICO_TCPOPTLEN_TIMESTAMP) < 0)
+        return;
+
+    t->ts_ok = 1;
+    tsval = long_from(opt + *idx);
+    *idx += (uint32_t)sizeof(uint32_t);
+    tsecr = long_from(opt + *idx);
+    f->timestamp = long_be(tsecr);
+    *idx += (uint32_t)sizeof(uint32_t);
+    t->ts_nxt = long_be(tsval);
+}
+
 static void tcp_parse_options(struct pico_frame *f)
 {
     struct pico_socket_tcp *t = (struct pico_socket_tcp *)f->sock;
@@ -801,65 +874,22 @@
         case PICO_TCP_OPTION_END:
             break;
         case PICO_TCP_OPTION_WS:
-            if (len != PICO_TCPOPTLEN_WS) {
-                tcp_dbg_options("TCP Window scale: bad len received (%d).\n", len);
-                i = i + len - 2;
-                break;
-            }
-
-            t->recv_wnd_scale = opt[i++];
-            tcp_dbg_options("TCP Window scale: received %d\n", t->recv_wnd_scale);
+            tcp_parse_option_ws(t, len, opt, &i);
             break;
         case PICO_TCP_OPTION_SACK_OK:
-            if (len != PICO_TCPOPTLEN_SACK_OK) {
-                tcp_dbg_options("TCP option sack: bad len received.\n");
-                i = i + len - 2;
-                break;
-            }
-
-            if(((struct pico_tcp_hdr *)(f->transport_hdr))->flags & PICO_TCP_SYN )
-                t->sack_ok = 1;
-
+            tcp_parse_option_sack_ok(t, f, len, &i);
+            break;
+        case PICO_TCP_OPTION_MSS:
+            tcp_parse_option_mss(t, len, opt, &i);
             break;
-        case PICO_TCP_OPTION_MSS: {
-            uint16_t mss;
-            if (len != PICO_TCPOPTLEN_MSS) {
-                tcp_dbg_options("TCP option mss: bad len received.\n");
-                i = i + len - 2;
-                break;
-            }
-
-            t->mss_ok = 1;
-            mss = short_from(opt + i);
-            i += (uint32_t)sizeof(uint16_t);
-            if (t->mss > short_be(mss))
-                t->mss = short_be(mss);
-
+        case PICO_TCP_OPTION_TIMESTAMP:
+            tcp_parse_option_timestamp(t, f, len, opt, &i);
             break;
-        }
-        case PICO_TCP_OPTION_TIMESTAMP: {
-            uint32_t tsval, tsecr;
-            if (len != PICO_TCPOPTLEN_TIMESTAMP) {
-                tcp_dbg_options("TCP option timestamp: bad len received.\n");
-                i = i + len - 2;
-                break;
-            }
-
-            t->ts_ok = 1;
-            tsval = long_from(opt + i);
-            i += (uint32_t)sizeof(uint32_t);
-            tsecr = long_from(opt + i);
-            f->timestamp = long_be(tsecr);
-            i += (uint32_t)sizeof(uint32_t);
-            t->ts_nxt = long_be(tsval);
-            break;
-        }
+
         case PICO_TCP_OPTION_SACK:
-        {
             tcp_rcv_sack(t, opt + i, len - 2);
             i = i + len - 2;
             break;
-        }
         default:
             tcp_dbg_options("TCP: received unsupported option %u\n", type);
             i = i + len - 2;
@@ -867,17 +897,11 @@
     }
 }
 
-static int tcp_send(struct pico_socket_tcp *ts, struct pico_frame *f)
+static inline void tcp_send_add_tcpflags(struct pico_socket_tcp *ts, struct pico_frame *f)
 {
     struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) f->transport_hdr;
-    struct pico_frame *cpy;
-    hdr->trans.sport = ts->sock.local_port;
-    hdr->trans.dport = ts->sock.remote_port;
-    if (!hdr->seq)
-        hdr->seq = long_be(ts->snd_nxt);
-
     if (ts->rcv_nxt != 0) {
-        if ((ts->rcv_ackd == 0) || (seq_compare(ts->rcv_ackd, ts->rcv_nxt) != 0) || (hdr->flags & PICO_TCP_ACK)) {
+        if ((ts->rcv_ackd == 0) || (pico_seq_compare(ts->rcv_ackd, ts->rcv_nxt) != 0) || (hdr->flags & PICO_TCP_ACK)) {
             hdr->flags |= PICO_TCP_ACK;
             hdr->ack = long_be(ts->rcv_nxt);
             ts->rcv_ackd = ts->rcv_nxt;
@@ -892,13 +916,14 @@
         hdr->flags |= PICO_TCP_PSH | PICO_TCP_ACK;
         hdr->ack = long_be(ts->rcv_nxt);
         ts->rcv_ackd = ts->rcv_nxt;
-        /* XXX pico_keepalive_reschedule(ts); */
     }
-
-    f->start = f->transport_hdr + PICO_SIZE_TCPHDR;
-    hdr->rwnd = short_be(ts->wnd);
-    hdr->crc = 0;
-    hdr->crc = short_be(pico_tcp_checksum(f));
+}
+
+static inline int tcp_send_try_enqueue(struct pico_socket_tcp *ts, struct pico_frame *f)
+{
+    struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+    struct pico_frame *cpy;
+    (void)hdr;
 
     /* TCP: ENQUEUE to PROTO ( Transmit ) */
     cpy = pico_frame_copy(f);
@@ -920,6 +945,26 @@
     }
 
     return 0;
+
+}
+
+static int tcp_send(struct pico_socket_tcp *ts, struct pico_frame *f)
+{
+    struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+    hdr->trans.sport = ts->sock.local_port;
+    hdr->trans.dport = ts->sock.remote_port;
+    if (!hdr->seq)
+        hdr->seq = long_be(ts->snd_nxt);
+
+    tcp_send_add_tcpflags(ts, f);
+
+    f->start = f->transport_hdr + PICO_SIZE_TCPHDR;
+    hdr->rwnd = short_be(ts->wnd);
+    hdr->crc = 0;
+    hdr->crc = short_be(pico_tcp_checksum(f));
+
+    return tcp_send_try_enqueue(ts, f);
+
 }
 
 /* #define PICO_TCP_SUPPORT_SOCKET_STATS */
@@ -934,6 +979,49 @@
 }
 #endif
 
+static void tcp_send_probe(struct pico_socket_tcp *t);
+
+static void pico_tcp_keepalive(pico_time now, void *arg)
+{
+    struct pico_socket_tcp *t = (struct pico_socket_tcp *)arg;
+    if (((t->sock.state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_ESTABLISHED)  && (t->ka_time > 0)) {
+        if (t->ka_time < (now - t->ack_timestamp)) {
+            if (t->ka_retries_count == 0) {
+                /* First probe */
+                tcp_send_probe(t);
+                t->ka_retries_count++;
+            }
+            if (t->ka_retries_count > t->ka_probes) {
+                if (t->sock.wakeup)
+                {
+                    pico_err = PICO_ERR_ECONNRESET;
+                    t->sock.wakeup(PICO_SOCK_EV_ERR, &t->sock);
+                }
+            }
+            if (((t->ka_retries_count * t->ka_intvl) + t->ka_time) < (now - t->ack_timestamp)) {
+                /* Next probe */
+                tcp_send_probe(t);
+                t->ka_retries_count++;
+            }
+        } else {
+            t->ka_retries_count = 0;
+        }
+    }
+    t->keepalive_tmr = pico_timer_add(1000, pico_tcp_keepalive, t);
+}
+
+static inline void rto_set(struct pico_socket_tcp *t, uint32_t rto)
+{
+    if (rto < PICO_TCP_RTO_MIN)
+        rto = PICO_TCP_RTO_MIN;
+
+    if (rto > PICO_TCP_RTO_MAX)
+        rto = PICO_TCP_RTO_MAX;
+
+    t->rto = rto;
+}
+
+
 struct pico_socket *pico_tcp_open(uint16_t family)
 {
     struct pico_socket_tcp *t = PICO_ZALLOC(sizeof(struct pico_socket_tcp));
@@ -942,72 +1030,38 @@
 
     t->sock.timestamp = TCP_TIME;
     pico_socket_set_family(&t->sock, family);
-    t->mss = (uint16_t)(pico_socket_get_mtu(&t->sock) - PICO_SIZE_TCPHDR);
+    t->mss = (uint16_t)(pico_socket_get_mss(&t->sock) - PICO_SIZE_TCPHDR);
     t->tcpq_in.pool.root = t->tcpq_hold.pool.root = t->tcpq_out.pool.root = &LEAF;
     t->tcpq_hold.pool.compare = t->tcpq_out.pool.compare = segment_compare;
     t->tcpq_in.pool.compare = input_segment_compare;
     t->tcpq_in.max_size = PICO_DEFAULT_SOCKETQ;
     t->tcpq_out.max_size = PICO_DEFAULT_SOCKETQ;
     t->tcpq_hold.max_size = 2u * t->mss;
-    /* disable Nagle by default */
+    rto_set(t, PICO_TCP_RTO_MIN);
+
+    /* Uncomment next line and disable Nagle by default */
     t->sock.opt_flags |= (1 << PICO_SOCKET_OPT_TCPNODELAY);
-    /* Nagle is enabled by default */
+
+    /* Uncomment next line and Nagle is enabled by default */
     /* t->sock.opt_flags &= (uint16_t) ~(1 << PICO_SOCKET_OPT_TCPNODELAY); */
 
+    /* Set default linger for the socket */
+    t->linger_timeout = PICO_SOCKET_LINGER_TIMEOUT;
+
+
 #ifdef PICO_TCP_SUPPORT_SOCKET_STATS
     pico_timer_add(2000, sock_stats, t);
 #endif
+
+    t->keepalive_tmr = pico_timer_add(1000, pico_tcp_keepalive, t);
     tcp_set_space(t);
 
     return &t->sock;
 }
 
-uint32_t pico_tcp_read(struct pico_socket *s, void *buf, uint32_t len)
+static uint32_t tcp_read_finish(struct pico_socket *s, uint32_t tot_rd_len)
 {
     struct pico_socket_tcp *t = TCP_SOCK(s);
-    struct tcp_input_segment *f;
-    int32_t in_frame_off;
-    uint32_t in_frame_len;
-    uint32_t tot_rd_len = 0;
-
-    while (tot_rd_len < len) {
-        /* To be sure we don't have garbage at the beginning */
-        release_until(&t->tcpq_in, t->rcv_processed);
-        f = first_segment(&t->tcpq_in);
-        if (!f)
-            goto out;
-
-        in_frame_off = seq_compare(t->rcv_processed, f->seq);
-        /* Hole at the beginning of data, awaiting retransmissions. */
-        if (in_frame_off < 0) {
-            tcp_dbg("TCP> read hole beginning of data, %08x - %08x. rcv_nxt is %08x\n", t->rcv_processed, f->seq, t->rcv_nxt);
-            goto out;
-        }
-
-        else if (in_frame_off > 0)
-        {
-            if ((uint32_t)in_frame_off > f->payload_len)
-                dbg("FATAL TCP ERR: in_frame_off > f->payload_len\n");
-
-            in_frame_len = f->payload_len - (uint32_t)in_frame_off;
-        } else {
-            in_frame_len = f->payload_len;
-        }
-
-
-        if ((in_frame_len + tot_rd_len) > (uint32_t)len) {
-            in_frame_len = len - tot_rd_len;
-        }
-
-        memcpy((uint8_t *)buf + tot_rd_len, f->payload + in_frame_off, in_frame_len);
-        tot_rd_len += in_frame_len;
-        t->rcv_processed += in_frame_len;
-
-        if ((in_frame_len == 0u) || (in_frame_len == (uint32_t)f->payload_len)) {
-            pico_discard_segment(&t->tcpq_in, f);
-        }
-    }
-out:
     tcp_set_space(t);
     if (t->tcpq_in.size == 0) {
         s->ev_pending &= (uint16_t)(~PICO_SOCK_EV_RD);
@@ -1027,19 +1081,85 @@
     return tot_rd_len;
 }
 
+static inline uint32_t tcp_read_in_frame_len(struct tcp_input_segment *f, int32_t in_frame_off, uint32_t tot_rd_len, uint32_t read_op_len)
+{
+    uint32_t in_frame_len = 0;
+    if (in_frame_off > 0)
+    {
+        if ((uint32_t)in_frame_off > f->payload_len) {
+            tcp_dbg("FATAL TCP ERR: in_frame_off > f->payload_len\n");
+        }
+
+        in_frame_len = f->payload_len - (uint32_t)in_frame_off;
+    } else { /* in_frame_off == 0 */
+        in_frame_len = f->payload_len;
+    }
+
+    if ((in_frame_len + tot_rd_len) > (uint32_t)read_op_len) {
+        in_frame_len = read_op_len - tot_rd_len;
+    }
+
+    return in_frame_len;
+
+}
+
+static inline void tcp_read_check_segment_done(struct pico_socket_tcp *t, struct tcp_input_segment *f, uint32_t in_frame_len)
+{
+    if ((in_frame_len == 0u) || (in_frame_len == (uint32_t)f->payload_len)) {
+        pico_discard_segment(&t->tcpq_in, f);
+    }
+}
+
+uint32_t pico_tcp_read(struct pico_socket *s, void *buf, uint32_t len)
+{
+    struct pico_socket_tcp *t = TCP_SOCK(s);
+    struct tcp_input_segment *f;
+    int32_t in_frame_off;
+    uint32_t in_frame_len;
+    uint32_t tot_rd_len = 0;
+
+    while (tot_rd_len < len) {
+        /* To be sure we don't have garbage at the beginning */
+        release_until(&t->tcpq_in, t->rcv_processed);
+        f = first_segment(&t->tcpq_in);
+        if (!f)
+            return tcp_read_finish(s, tot_rd_len);
+
+        in_frame_off = pico_seq_compare(t->rcv_processed, f->seq);
+        /* Check for hole at the beginning of data, awaiting retransmissions. */
+        if (in_frame_off < 0) {
+            tcp_dbg("TCP> read hole beginning of data, %08x - %08x. rcv_nxt is %08x\n", t->rcv_processed, f->seq, t->rcv_nxt);
+            return tcp_read_finish(s, tot_rd_len);
+        }
+
+        in_frame_len = tcp_read_in_frame_len(f, in_frame_off, tot_rd_len, len);
+
+
+        memcpy((uint8_t *)buf + tot_rd_len, f->payload + in_frame_off, in_frame_len);
+        tot_rd_len += in_frame_len;
+        t->rcv_processed += in_frame_len;
+
+        tcp_read_check_segment_done(t, f, in_frame_len);
+
+    }
+    return tcp_read_finish(s, tot_rd_len);
+}
+
 int pico_tcp_initconn(struct pico_socket *s);
 static void initconn_retry(pico_time when, void *arg)
 {
     struct pico_socket_tcp *t = (struct pico_socket_tcp *)arg;
     IGNORE_PARAMETER(when);
-    if (TCPSTATE(&t->sock) == PICO_SOCKET_STATE_TCP_SYN_SENT
-        && !(t->sock.state & PICO_SOCKET_STATE_SHUT_LOCAL)
-        && !(t->sock.state & PICO_SOCKET_STATE_SHUT_REMOTE)) {
+    if (TCPSTATE(&t->sock) != PICO_SOCKET_STATE_TCP_ESTABLISHED)
+    {
         if (t->backoff > PICO_TCP_MAX_CONNECT_RETRIES) {
             tcp_dbg("TCP> Connection timeout. \n");
             if (t->sock.wakeup)
+            {
+                pico_err = PICO_ERR_ECONNREFUSED;
                 t->sock.wakeup(PICO_SOCK_EV_ERR, &t->sock);
-
+            }
+            pico_socket_del(&t->sock);
             return;
         }
 
@@ -1069,7 +1189,7 @@
 
     ts->snd_last = ts->snd_nxt;
     ts->cwnd = PICO_TCP_IW;
-    mtu = pico_socket_get_mtu(s);
+    mtu = (uint16_t)pico_socket_get_mss(s);
     ts->mss = (uint16_t)(mtu - PICO_SIZE_TCPHDR);
     ts->ssthresh = (uint16_t)((uint16_t)(PICO_DEFAULT_SOCKETQ / ts->mss) -  (((uint16_t)(PICO_DEFAULT_SOCKETQ / ts->mss)) >> 3u));
     syn->sock = s;
@@ -1141,8 +1261,9 @@
     hdr->trans.sport = t->sock.local_port;
     hdr->trans.dport = t->sock.remote_port;
     hdr->seq = long_be(t->snd_nxt);
-    if ((flags & PICO_TCP_ACK) != 0)
+    if ((flags & PICO_TCP_ACK) != 0) {
         hdr->ack = long_be(t->rcv_nxt);
+    }
 
     if (is_keepalive)
         hdr->seq = long_be(t->snd_nxt - 1);
@@ -1169,25 +1290,19 @@
     tcp_send_empty(t, PICO_TCP_PSHACK, 1);
 }
 
-static int tcp_send_rst(struct pico_socket *s, struct pico_frame *fr)
+static int tcp_do_send_rst(struct pico_socket *s, uint32_t seq)
 {
     struct pico_socket_tcp *t = (struct pico_socket_tcp *) s;
+    uint16_t opt_len = tcp_options_size(t, PICO_TCP_RST);
     struct pico_frame *f;
-    struct pico_tcp_hdr *hdr, *hdr_rcv;
-    uint16_t opt_len = tcp_options_size(t, PICO_TCP_RST);
-    int close;
-
-    tcp_dbg("TCP SEND_RST >>>>>>>>>>>>>>> START\n");
-
+    struct pico_tcp_hdr *hdr;
     f = t->sock.net->alloc(t->sock.net, (uint16_t)(PICO_SIZE_TCPHDR + opt_len));
-
     if (!f) {
         return -1;
     }
-
-    hdr_rcv = (struct pico_tcp_hdr *) fr->transport_hdr;
-
     f->sock = &t->sock;
+    tcp_dbg("TCP SEND_RST >>>>>>>>>>>>>>> START\n");
+
     hdr = (struct pico_tcp_hdr *) f->transport_hdr;
     hdr->len = (uint8_t)((PICO_SIZE_TCPHDR + opt_len) << 2 | t->jumbo);
     hdr->flags = PICO_TCP_RST;
@@ -1196,21 +1311,7 @@
     tcp_add_options(t, f, PICO_TCP_RST, opt_len);
     hdr->trans.sport = t->sock.local_port;
     hdr->trans.dport = t->sock.remote_port;
-    hdr->seq = long_be(t->snd_nxt);
-
-    /* check if state is synchronized */
-    if (((s->state & PICO_SOCKET_STATE_TCP) > PICO_SOCKET_STATE_TCP_SYN_RECV)) {
-        /* in synchronized state: send RST with seq = ack from previous segment */
-        hdr->seq = hdr_rcv->ack;
-        close = 0;
-    } else {
-        /* non-synchronized state */
-        /* go to CLOSED here to prevent timer callback to go on after timeout */
-        (t->sock).state &= 0x00FFU;
-        (t->sock).state |= PICO_SOCKET_STATE_TCP_CLOSED;
-        close = 1;
-    }
-
+    hdr->seq = seq;
     hdr->ack = long_be(t->rcv_nxt);
     t->rcv_ackd = t->rcv_nxt;
     f->start = f->transport_hdr + PICO_SIZE_TCPHDR;
@@ -1220,9 +1321,28 @@
 
     /* TCP: ENQUEUE to PROTO */
     pico_enqueue(&tcp_out, f);
-
-    /* goto CLOSED */
-    if (close) {
+    tcp_dbg("TCP SEND_RST >>>>>>>>>>>>>>> DONE\n");
+    return 0;
+}
+
+static int tcp_send_rst(struct pico_socket *s, struct pico_frame *fr)
+{
+    struct pico_socket_tcp *t = (struct pico_socket_tcp *) s;
+    struct pico_tcp_hdr *hdr_rcv;
+    int ret;
+
+    if (fr && ((s->state & PICO_SOCKET_STATE_TCP) > PICO_SOCKET_STATE_TCP_SYN_RECV)) {
+        /* in synchronized state: send RST with seq = ack from previous segment */
+        hdr_rcv = (struct pico_tcp_hdr *) fr->transport_hdr;
+        ret = tcp_do_send_rst(s, hdr_rcv->ack);       
+    } else {
+        /* non-synchronized state */
+        /* go to CLOSED here to prevent timer callback to go on after timeout */
+        (t->sock).state &= 0x00FFU;
+        (t->sock).state |= PICO_SOCKET_STATE_TCP_CLOSED;
+        ret = tcp_do_send_rst(s, long_be(t->snd_nxt));       
+
+        /* Set generic socket state to CLOSED, too */
         (t->sock).state &= 0xFF00U;
         (t->sock).state |= PICO_SOCKET_STATE_CLOSED;
 
@@ -1232,11 +1352,49 @@
 
         /* delete socket */
         pico_socket_del(&t->sock);
-
-        tcp_dbg("TCP SEND_RST >>>>>>>>>>>>>>> DONE, deleted socket\n");
+    }
+    return ret;
+}
+
+static inline void tcp_fill_rst_payload(struct pico_frame *fr, struct pico_frame *f)
+{
+    /* fill in IP data from original frame */
+    if (IS_IPV4(fr)) {
+        memcpy(f->net_hdr, fr->net_hdr, sizeof(struct pico_ipv4_hdr));
+        ((struct pico_ipv4_hdr *)(f->net_hdr))->dst.addr = ((struct pico_ipv4_hdr *)(fr->net_hdr))->src.addr;
+        ((struct pico_ipv4_hdr *)(f->net_hdr))->src.addr = ((struct pico_ipv4_hdr *)(fr->net_hdr))->dst.addr;
+        tcp_dbg("Making IPv4 reset frame...\n");
+
+    } else {
+        memcpy(f->net_hdr, fr->net_hdr, sizeof(struct pico_ipv6_hdr));
+        ((struct pico_ipv6_hdr *)(f->net_hdr))->dst = ((struct pico_ipv6_hdr *)(fr->net_hdr))->src;
+        ((struct pico_ipv6_hdr *)(f->net_hdr))->src = ((struct pico_ipv6_hdr *)(fr->net_hdr))->dst;
     }
 
-    return 0;
+    /* fill in TCP data from original frame */
+    ((struct pico_tcp_hdr *)(f->transport_hdr))->trans.dport = ((struct pico_tcp_hdr *)(fr->transport_hdr))->trans.sport;
+    ((struct pico_tcp_hdr *)(f->transport_hdr))->trans.sport = ((struct pico_tcp_hdr *)(fr->transport_hdr))->trans.dport;
+
+}
+
+
+static inline void tcp_fill_rst_header(struct pico_frame *fr, struct pico_tcp_hdr *hdr1, struct pico_frame *f, struct pico_tcp_hdr *hdr)
+{
+    if(!(hdr1->flags & PICO_TCP_ACK))
+        hdr->flags |= PICO_TCP_ACK;
+
+    hdr->rwnd  = 0;
+    if (((struct pico_tcp_hdr *)(fr->transport_hdr))->flags & PICO_TCP_ACK) {
+        hdr->seq = ((struct pico_tcp_hdr *)(fr->transport_hdr))->ack;
+    } else {
+        hdr->seq = 0U;
+    }
+
+    hdr->ack = 0;
+    if(!(hdr1->flags & PICO_TCP_ACK))
+        hdr->ack = long_be(long_be(((struct pico_tcp_hdr *)(fr->transport_hdr))->seq) + fr->payload_len);
+
+    hdr->crc = short_be(pico_tcp_checksum(f));
 }
 
 int pico_tcp_reply_rst(struct pico_frame *fr)
@@ -1258,46 +1416,23 @@
         return -1;
     }
 
-    /* fill in IP data from original frame */
-    if (IS_IPV4(fr)) {
-        memcpy(f->net_hdr, fr->net_hdr, sizeof(struct pico_ipv4_hdr));
-        ((struct pico_ipv4_hdr *)(f->net_hdr))->dst.addr = ((struct pico_ipv4_hdr *)(fr->net_hdr))->src.addr;
-        ((struct pico_ipv4_hdr *)(f->net_hdr))->src.addr = ((struct pico_ipv4_hdr *)(fr->net_hdr))->dst.addr;
-        tcp_dbg("Making IPv4 reset frame...\n");
-
-    } else {
-        memcpy(f->net_hdr, fr->net_hdr, sizeof(struct pico_ipv6_hdr));
-        ((struct pico_ipv6_hdr *)(f->net_hdr))->dst = ((struct pico_ipv6_hdr *)(fr->net_hdr))->src;
-        ((struct pico_ipv6_hdr *)(f->net_hdr))->src = ((struct pico_ipv6_hdr *)(fr->net_hdr))->dst;
-    }
-
-    /* fill in TCP data from original frame */
-    ((struct pico_tcp_hdr *)(f->transport_hdr))->trans.dport = ((struct pico_tcp_hdr *)(fr->transport_hdr))->trans.sport;
-    ((struct pico_tcp_hdr *)(f->transport_hdr))->trans.sport = ((struct pico_tcp_hdr *)(fr->transport_hdr))->trans.dport;
+    tcp_fill_rst_payload(fr, f);
+
     hdr = (struct pico_tcp_hdr *) f->transport_hdr;
     hdr->len   = (uint8_t)(size << 2);
     hdr->flags = PICO_TCP_RST;
-    if(!(hdr1->flags & PICO_TCP_ACK))
-        hdr->flags |= PICO_TCP_ACK;
-
-    hdr->rwnd  = 0;
-    if (((struct pico_tcp_hdr *)(fr->transport_hdr))->flags & PICO_TCP_ACK) {
-        hdr->seq = ((struct pico_tcp_hdr *)(fr->transport_hdr))->ack;
-    } else {
-        hdr->seq = 0U;
-    }
-
-    hdr->ack = 0;
-    if(!(hdr1->flags & PICO_TCP_ACK))
-        hdr->ack = long_be(long_be(((struct pico_tcp_hdr *)(fr->transport_hdr))->seq) + fr->payload_len);
-
-    hdr->crc = short_be(pico_tcp_checksum(f));
-    if (IS_IPV4(f)) {
+
+    tcp_fill_rst_header(fr, hdr1, f, hdr);
+
+    if (0) {
+#ifdef PICO_SUPPORT_IPV4
+    } else if (IS_IPV4(f)) {
         tcp_dbg("Pushing IPv4 reset frame...\n");
         pico_ipv4_frame_push(f, &(((struct pico_ipv4_hdr *)(f->net_hdr))->dst), PICO_PROTO_TCP);
+#endif
 #ifdef PICO_SUPPORT_IPV6
     } else {
-        pico_ipv6_frame_push(f, &(((struct pico_ipv6_hdr *)(f->net_hdr))->dst), PICO_PROTO_TCP);
+        pico_ipv6_frame_push(f, NULL, &(((struct pico_ipv6_hdr *)(f->net_hdr))->dst), PICO_PROTO_TCP, 0);
 #endif
     }
 
@@ -1365,6 +1500,16 @@
     return 0;
 }
 
+static void tcp_deltcb(pico_time when, void *arg);
+
+static void tcp_linger(struct pico_socket_tcp *t)
+{
+    if (t->fin_tmr) {
+        pico_timer_cancel(t->fin_tmr);
+    }
+    t->fin_tmr = pico_timer_add(t->linger_timeout, tcp_deltcb, t);
+}
+
 static void tcp_send_fin(struct pico_socket_tcp *t)
 {
     struct pico_frame *f;
@@ -1386,16 +1531,20 @@
     tcp_add_options(t, f, PICO_TCP_FIN, opt_len);
     hdr->trans.sport = t->sock.local_port;
     hdr->trans.dport = t->sock.remote_port;
-    hdr->seq = long_be(t->snd_nxt); /* XXX TODO check correct ?? --> snd_last? otherwise maybe data after FIN */
+    hdr->seq = long_be(t->snd_nxt);
 
     f->start = f->transport_hdr + PICO_SIZE_TCPHDR;
     hdr->rwnd = short_be(t->wnd);
     hdr->crc = 0;
     hdr->crc = short_be(pico_tcp_checksum(f));
     /* tcp_dbg("SENDING FIN...\n"); */
-    /* TCP: ENQUEUE to PROTO ( Pure ACK ) */
-    pico_enqueue(&tcp_out, f);
-    t->snd_nxt++;
+    if (t->linger_timeout > 0) {
+        pico_enqueue(&tcp_out, f);
+        t->snd_nxt++;
+    } else {
+        pico_frame_discard(f);
+    }
+    tcp_linger(t);
 }
 
 static void tcp_sack_prepare(struct pico_socket_tcp *t)
@@ -1460,76 +1609,95 @@
     }
 }
 
+static inline int tcp_data_in_expected(struct pico_socket_tcp *t, struct pico_frame *f)
+{
+    struct tcp_input_segment *nxt;
+    if (pico_seq_compare(SEQN(f), t->rcv_nxt) == 0) { /* Exactly what we expected */
+        /* Create new segment and enqueue it */
+        struct tcp_input_segment *input = segment_from_frame(f);
+        if (!input) {
+            pico_err = PICO_ERR_ENOMEM;
+            return -1;
+        }
+
+        if(pico_enqueue_segment(&t->tcpq_in, input) <= 0)
+        {
+            /* failed to enqueue, destroy segment */
+            PICO_FREE(input->payload);
+            PICO_FREE(input);
+            return -1;
+        } else {
+            t->rcv_nxt = SEQN(f) + f->payload_len;
+            nxt = peek_segment(&t->tcpq_in, t->rcv_nxt);
+            while(nxt) {
+                tcp_dbg("scrolling rcv_nxt...%08x\n", t->rcv_nxt);
+                t->rcv_nxt += nxt->payload_len;
+                nxt = peek_segment(&t->tcpq_in, t->rcv_nxt);
+            }
+            t->sock.ev_pending |= PICO_SOCK_EV_RD;
+        }
+    } else {
+        tcp_dbg("TCP> lo segment. Uninteresting retransmission. (exp: %x got: %x)\n", t->rcv_nxt, SEQN(f));
+    }
+
+    return 0;
+}
+
+static inline int tcp_data_in_high_segment(struct pico_socket_tcp *t, struct pico_frame *f)
+{
+    tcp_dbg("TCP> hi segment. Possible packet loss. I'll dupack this. (exp: %x got: %x)\n", t->rcv_nxt, SEQN(f));
+    if (t->sack_ok) {
+        struct tcp_input_segment *input = segment_from_frame(f);
+        if (!input) {
+            pico_err = PICO_ERR_ENOMEM;
+            return -1;
+        }
+
+        if(pico_enqueue_segment(&t->tcpq_in, input) <= 0) {
+            /* failed to enqueue, destroy segment */
+            PICO_FREE(input->payload);
+            PICO_FREE(input);
+            return -1;
+        }
+
+        tcp_sack_prepare(t);
+    }
+
+    return 0;
+}
+
+static inline void tcp_data_in_send_ack(struct pico_socket_tcp *t, struct pico_frame *f)
+{
+    struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+    /* In either case, ack til recv_nxt, unless received data raises a RST flag. */
+    if (((t->sock.state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_CLOSE_WAIT) &&
+        ((t->sock.state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_SYN_SENT) &&
+        ((t->sock.state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_SYN_RECV) &&
+        ((hdr->flags & PICO_TCP_RST) == 0))
+        tcp_send_ack(t);
+}
+
 static int tcp_data_in(struct pico_socket *s, struct pico_frame *f)
 {
     struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
     struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) f->transport_hdr;
     uint16_t payload_len = (uint16_t)(f->transport_len - ((hdr->len & 0xf0u) >> 2u));
     int ret = 0;
+    (void)hdr;
+
     if (((hdr->len & 0xf0u) >> 2u) <= f->transport_len) {
         tcp_parse_options(f);
         f->payload = f->transport_hdr + ((hdr->len & 0xf0u) >> 2u);
         f->payload_len = payload_len;
         tcp_dbg("TCP> Received segment. (exp: %x got: %x)\n", t->rcv_nxt, SEQN(f));
 
-        if (seq_compare(SEQN(f), t->rcv_nxt) <= 0) {
-            struct tcp_input_segment *nxt;
-            if (seq_compare(SEQN(f), t->rcv_nxt) == 0) { /* Exactly what we expected */
-                /* Create new segment and enqueue it */
-                struct tcp_input_segment *input = segment_from_frame(f);
-                if (!input) {
-                    pico_err = PICO_ERR_ENOMEM;
-                    return -1;
-                }
-
-                if(pico_enqueue_segment(&t->tcpq_in, input) <= 0)
-                {
-                    /* failed to enqueue, destroy segment */
-                    PICO_FREE(input->payload);
-                    PICO_FREE(input);
-                    ret = -1;
-                } else {
-                    t->rcv_nxt = SEQN(f) + f->payload_len;
-                    nxt = peek_segment(&t->tcpq_in, t->rcv_nxt);
-                    while(nxt) {
-                        tcp_dbg("scrolling rcv_nxt...%08x\n", t->rcv_nxt);
-                        t->rcv_nxt += nxt->payload_len;
-                        nxt = peek_segment(&t->tcpq_in, t->rcv_nxt);
-                    }
-                    t->sock.ev_pending |= PICO_SOCK_EV_RD;
-                }
-            } else {
-                tcp_dbg("TCP> lo segment. Uninteresting retransmission. (exp: %x got: %x)\n", t->rcv_nxt, SEQN(f));
-            }
+        if (pico_seq_compare(SEQN(f), t->rcv_nxt) <= 0) {
+            ret = tcp_data_in_expected(t, f);
         } else {
-            tcp_dbg("TCP> hi segment. Possible packet loss. I'll dupack this. (exp: %x got: %x)\n", t->rcv_nxt, SEQN(f));
-            if (t->sack_ok) {
-                struct tcp_input_segment *input = segment_from_frame(f);
-                if (!input) {
-                    pico_err = PICO_ERR_ENOMEM;
-                    return -1;
-                }
-
-                if(pico_enqueue_segment(&t->tcpq_in, input) <= 0) {
-                    /* failed to enqueue, destroy segment */
-                    PICO_FREE(input->payload);
-                    PICO_FREE(input);
-                    return -1;
-                }
-
-                tcp_sack_prepare(t);
-            }
+            ret = tcp_data_in_high_segment(t, f);
         }
 
-        /* In either case, ack til recv_nxt. */
-        if (((t->sock.state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_CLOSE_WAIT) && ((t->sock.state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_SYN_SENT) && ((t->sock.state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_SYN_RECV)) {
-            /* tcp_dbg("SENDACK CALLED FROM OUTSIDE tcp_synack, state %x\n",t->sock.state); */
-            if (hdr->flags & PICO_TCP_RST)
-                return -1;
-
-            tcp_send_ack(t);
-        }
-
+        tcp_data_in_send_ack(t, f);
         return ret;
     } else {
         tcp_dbg("TCP: invalid data in pkt len, exp: %d, got %d\n", (hdr->len & 0xf0) >> 2, f->transport_len);
@@ -1555,17 +1723,6 @@
         return (uint16_t)(b - a);
 }
 
-static inline void rto_set(struct pico_socket_tcp *t, uint32_t rto)
-{
-    if (rto < PICO_TCP_RTO_MIN)
-        rto = PICO_TCP_RTO_MIN;
-
-    if (rto > PICO_TCP_RTO_MAX)
-        rto = PICO_TCP_RTO_MAX;
-
-    t->rto = rto;
-}
-
 static void tcp_rtt(struct pico_socket_tcp *t, uint32_t rtt)
 {
 
@@ -1680,10 +1837,39 @@
            ((t->backoff < PICO_TCP_MAX_RETRANS));
 }
 
+static inline int tcp_retrans_timeout_check_queue(struct pico_socket_tcp *t)
+{
+    struct pico_frame *f = NULL;
+    f = first_segment(&t->tcpq_out);
+    while (f) {
+        tcp_dbg("Checking frame in queue \n");
+        if (t->x_mode == PICO_TCP_WINDOW_FULL) {
+            tcp_dbg("TCP BLACKOUT> TIMED OUT (output) frame %08x, len= %d rto=%d Win full: %d frame flags: %04x\n", SEQN(f), f->payload_len, t->rto, t->x_mode == PICO_TCP_WINDOW_FULL, f->flags);
+            tcp_next_zerowindow_probe(t);
+            return -1;
+        }
+
+        if (t->x_mode != PICO_TCP_BLACKOUT)
+            tcp_first_timeout(t);
+
+        tcp_add_header(t, f);
+        if (tcp_rto_xmit(t, f) > 0) /* A segment has been rexmit'd */
+            return -1;
+
+        f = next_segment(&t->tcpq_out, f);
+    }
+    if (t->tcpq_out.size < t->tcpq_out.max_size)
+        t->sock.ev_pending |= PICO_SOCK_EV_WR;
+
+    return 0;
+
+
+
+}
+
 static void tcp_retrans_timeout(pico_time val, void *sock)
 {
     struct pico_socket_tcp *t = (struct pico_socket_tcp *) sock;
-    struct pico_frame *f = NULL;
 
     t->retrans_tmr = NULL;
 
@@ -1701,26 +1887,8 @@
     t->retrans_tmr_due = 0ull;
 
     if (tcp_is_allowed_to_send(t)) {
-        f = first_segment(&t->tcpq_out);
-        while (f) {
-            tcp_dbg("Checking frame in queue \n");
-            if (t->x_mode == PICO_TCP_WINDOW_FULL) {
-                tcp_dbg("TCP BLACKOUT> TIMED OUT (output) frame %08x, len= %d rto=%d Win full: %d frame flags: %04x\n", SEQN(f), f->payload_len, t->rto, t->x_mode == PICO_TCP_WINDOW_FULL, f->flags);
-                tcp_next_zerowindow_probe(t);
-                return;
-            }
-
-            if (t->x_mode != PICO_TCP_BLACKOUT)
-                tcp_first_timeout(t);
-
-            tcp_add_header(t, f);
-            if (tcp_rto_xmit(t, f) > 0) /* A segment has been rexmit'd */
-                return;
-
-            f = next_segment(&t->tcpq_out, f);
-        }
-        if (t->tcpq_out.size < t->tcpq_out.max_size)
-            t->sock.ev_pending |= PICO_SOCK_EV_WR;
+        if (tcp_retrans_timeout_check_queue(t) < 0)
+            return;
     }
     else if(t->backoff >= PICO_TCP_MAX_RETRANS && (t->sock.state & 0xFF00) == PICO_SOCKET_STATE_TCP_ESTABLISHED )
     {
@@ -1871,6 +2039,7 @@
 
     acked = (uint16_t)tcp_ack_advance_una(t, f, &acked_timestamp);
     una = first_segment(&t->tcpq_out);
+    t->ack_timestamp = TCP_TIME;
 
     if ((t->x_mode == PICO_TCP_BLACKOUT) ||
         ((t->x_mode == PICO_TCP_WINDOW_FULL) && ((t->recv_wnd << t->recv_wnd_scale) > t->mss))) {
@@ -1905,7 +2074,7 @@
             if (rtt)
                 tcp_rtt(t, rtt);
         } else if(acked_timestamp) {
-            /* If no timestamps are there, use conservatve estimation on the una */
+            /* If no timestamps are there, use conservative estimation on the una */
             rtt = time_diff(TCP_TIME, acked_timestamp);
             if (rtt)
                 tcp_rtt(t, rtt);
@@ -1929,7 +2098,11 @@
             tcp_dbg("Mode: DUPACK %d, due to PURE ACK %0x, len = %d\n", t->x_mode, SEQN(f), f->payload_len);
             /* tcp_dbg("ACK: %x - QUEUE: %x\n", ACKN(f), SEQN(first_segment(&t->tcpq_out))); */
             if (t->x_mode == PICO_TCP_RECOVER) {              /* Switching mode */
-                t->cwnd = (uint16_t)t->in_flight;
+                if (t->in_flight > PICO_TCP_IW)
+                    t->cwnd = (uint16_t)t->in_flight;
+                else
+                    t->cwnd = PICO_TCP_IW;
+
                 t->snd_retry = SEQN((struct pico_frame *)first_segment(&t->tcpq_out));
                 if (t->ssthresh > t->cwnd)
                     t->ssthresh >>= 2;
@@ -1950,10 +2123,10 @@
                     tcp_dbg("Skipping %08x because it is sacked.\n", SEQN(nxt));
                     nxt = next_segment(&t->tcpq_out, nxt);
                 }
-                if (nxt && (seq_compare(SEQN(nxt), t->snd_nxt)) > 0)
+                if (nxt && (pico_seq_compare(SEQN(nxt), t->snd_nxt)) > 0)
                     nxt = NULL;
 
-                if (nxt && (seq_compare(SEQN(nxt), SEQN((struct pico_frame *)first_segment(&t->tcpq_out))) > (int)(t->recv_wnd << t->recv_wnd_scale)))
+                if (nxt && (pico_seq_compare(SEQN(nxt), SEQN((struct pico_frame *)first_segment(&t->tcpq_out))) > (int)(t->recv_wnd << t->recv_wnd_scale)))
                     nxt = NULL;
 
                 if(!nxt)
@@ -1981,7 +2154,7 @@
     /* Linux very special zero-window probe detection (see bug #107) */
     if ((0 == (hdr->flags & (PICO_TCP_PSH | PICO_TCP_SYN))) && /* This is a pure ack, and... */
         (ACKN(f) == t->snd_nxt) &&                           /* it's acking our snd_nxt, and... */
-        (seq_compare(SEQN(f), t->rcv_nxt) < 0))             /* Has an old seq number */
+        (pico_seq_compare(SEQN(f), t->rcv_nxt) < 0))             /* Has an old seq number */
     {
         tcp_send_ack(t);
     }
@@ -2025,15 +2198,20 @@
 
 static int tcp_finwaitack(struct pico_socket *s, struct pico_frame *f)
 {
+    struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
     tcp_dbg("RECEIVED ACK IN FIN_WAIT1\n");
-    tcp_dbg("TCP> IN STATE FIN_WAIT2\n");
 
     /* acking part */
     tcp_ack(s, f);
-    /* update TCP state */
-    s->state &= 0x00FFU;
-    s->state |= PICO_SOCKET_STATE_TCP_FIN_WAIT2;
-
+
+    
+    tcp_dbg("FIN_WAIT1: ack is %08x - snd_nxt is %08x\n", ACKN(f), t->snd_nxt);
+    if (ACKN(f) == (t->snd_nxt - 1u)) {
+        /* update TCP state */
+        s->state &= 0x00FFU;
+        s->state |= PICO_SOCKET_STATE_TCP_FIN_WAIT2;
+        tcp_dbg("TCP> IN STATE FIN_WAIT2\n");
+    }
     return 0;
 }
 
@@ -2042,23 +2220,27 @@
     struct pico_socket_tcp *t = (struct pico_socket_tcp *)arg;
     IGNORE_PARAMETER(when);
 
-    if (TCPSTATE(&t->sock) == PICO_SOCKET_STATE_TCP_TIME_WAIT) {
-        tcp_dbg("TCP> state: time_wait, final timer expired, going to closed state\n");
-        /* update state */
-        (t->sock).state &= 0x00FFU;
-        (t->sock).state |= PICO_SOCKET_STATE_TCP_CLOSED;
-        (t->sock).state &= 0xFF00U;
-        (t->sock).state |= PICO_SOCKET_STATE_CLOSED;
-        /* call EV_FIN wakeup before deleting */
-        if (t->sock.wakeup) {
-            (t->sock).wakeup(PICO_SOCK_EV_FIN, &(t->sock));
-        }
-
-        /* delete socket */
-        pico_socket_del(&t->sock);
+    /* send RST if not yet in TIME_WAIT */
+    if ( (((t->sock).state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_TIME_WAIT)
+      && (((t->sock).state & PICO_SOCKET_STATE_TCP) != PICO_SOCKET_STATE_TCP_CLOSING) ) {
+        tcp_dbg("Called deltcb in state = %04x (sending reset!)\n", (t->sock).state);
+        tcp_do_send_rst(&t->sock, long_be(t->snd_nxt));
     } else {
-        tcp_dbg("TCP> trying to go to closed, wrong state\n");
+        tcp_dbg("Called deltcb in state = %04x\n", (t->sock).state);
     }
+
+    /* update state */
+    (t->sock).state &= 0x00FFU;
+    (t->sock).state |= PICO_SOCKET_STATE_TCP_CLOSED;
+    (t->sock).state &= 0xFF00U;
+    (t->sock).state |= PICO_SOCKET_STATE_CLOSED;
+    /* call EV_FIN wakeup before deleting */
+    if (t->sock.wakeup) {
+        (t->sock).wakeup(PICO_SOCK_EV_FIN, &(t->sock));
+    }
+
+    /* delete socket */
+    pico_socket_del(&t->sock);
 }
 
 static int tcp_finwaitfin(struct pico_socket *s, struct pico_frame *f)
@@ -2080,39 +2262,45 @@
 
     /* send ACK */
     tcp_send_ack(t);
-    /* set timer */
-    pico_timer_add(200, tcp_deltcb, t);
+    /* linger */
+    tcp_linger(t);
     return 0;
 }
 
-static int tcp_closewaitack(struct pico_socket *s, struct pico_frame *f)
+static int tcp_closing_ack(struct pico_socket *s, struct pico_frame *f)
 {
     struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
     tcp_dbg("TCP> received ack in CLOSING\n");
     /* acking part */
     tcp_ack(s, f);
-    /* update TCP state */
-    s->state &= 0x00FFU;
-    s->state |= PICO_SOCKET_STATE_TCP_TIME_WAIT;
-    /* set timer */
-    pico_timer_add(200, tcp_deltcb, t);
+
+    /* update TCP state DLA TODO: Only if FIN is acked! */
+    tcp_dbg("CLOSING: ack is %08x - snd_nxt is %08x\n", ACKN(f), t->snd_nxt);
+    if (ACKN(f) == t->snd_nxt) {
+        s->state &= 0x00FFU;
+        s->state |= PICO_SOCKET_STATE_TCP_TIME_WAIT;
+        /* set timer */
+        tcp_linger(t);
+    }
     return 0;
 }
 
 static int tcp_lastackwait(struct pico_socket *s, struct pico_frame *f)
 {
-    IGNORE_PARAMETER(f);
-    tcp_dbg("TCP> state: last_ack, received ack, to closed\n");
-    s->state &= 0x00FFU;
-    s->state |= PICO_SOCKET_STATE_TCP_CLOSED;
-    s->state &= 0xFF00U;
-    s->state |= PICO_SOCKET_STATE_CLOSED;
-    /* call socket wakeup with EV_FIN */
-    if (s->wakeup)
-        s->wakeup(PICO_SOCK_EV_FIN, s);
-
-    /* delete socket */
-    pico_socket_del(s);
+    struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
+    tcp_dbg("LAST_ACK: ack is %08x - snd_nxt is %08x\n", ACKN(f), t->snd_nxt);
+    if (ACKN(f) == t->snd_nxt) {
+        s->state &= 0x00FFU;
+        s->state |= PICO_SOCKET_STATE_TCP_CLOSED;
+        s->state &= 0xFF00U;
+        s->state |= PICO_SOCKET_STATE_CLOSED;
+        /* call socket wakeup with EV_FIN */
+        if (s->wakeup)
+            s->wakeup(PICO_SOCK_EV_FIN, s);
+
+        /* delete socket */
+        pico_socket_del(s);
+    }
     return 0;
 }
 
@@ -2148,11 +2336,9 @@
     }
 
 #endif
-
-
     f->sock = &new->sock;
     tcp_parse_options(f);
-    mtu = pico_socket_get_mtu(s);
+    mtu = (uint16_t)pico_socket_get_mss(&new->sock);
     new->mss = (uint16_t)(mtu - PICO_SIZE_TCPHDR);
     new->tcpq_in.max_size = PICO_DEFAULT_SOCKETQ;
     new->tcpq_out.max_size = PICO_DEFAULT_SOCKETQ;
@@ -2164,9 +2350,11 @@
     new->ssthresh = (uint16_t)((uint16_t)(PICO_DEFAULT_SOCKETQ / new->mss) -  (((uint16_t)(PICO_DEFAULT_SOCKETQ / new->mss)) >> 3u));
     new->recv_wnd = short_be(hdr->rwnd);
     new->jumbo = hdr->len & 0x07;
+    new->linger_timeout = PICO_SOCKET_LINGER_TIMEOUT;
     s->number_of_pending_conn++;
     new->sock.parent = s;
     new->sock.wakeup = s->wakeup;
+    rto_set(new, PICO_TCP_RTO_MIN);
     /* Initialize timestamp values */
     new->sock.state = PICO_SOCKET_STATE_BOUND | PICO_SOCKET_STATE_CONNECTED | PICO_SOCKET_STATE_TCP_SYN_RECV;
     pico_socket_add(&new->sock);
@@ -2180,11 +2368,11 @@
     struct pico_tcp_hdr *hdr = NULL;
     struct pico_socket_tcp *t = TCP_SOCK(s);
     hdr = (struct pico_tcp_hdr *)f->transport_hdr;
-    if (t->rcv_nxt == long_be(hdr->seq) + 1) {
+    if (t->rcv_nxt == long_be(hdr->seq) + 1u) {
         /* take back our own SEQ number to its original value,
-         * so the synack retransmitted is identical to the original. 
+         * so the synack retransmitted is identical to the original.
          */
-        t->snd_nxt--; 
+        t->snd_nxt--;
         tcp_send_synack(s);
     } else {
         tcp_send_rst(s, f);
@@ -2201,13 +2389,13 @@
 }
 
 
-uint16_t pico_tcp_get_socket_mtu(struct pico_socket *s)
+uint16_t pico_tcp_get_socket_mss(struct pico_socket *s)
 {
     struct pico_socket_tcp *t = (struct pico_socket_tcp *) s;
     if (t->mss > 0)
         return (uint16_t)(t->mss + PICO_SIZE_TCPHDR);
     else
-        return pico_socket_get_mtu(s);
+        return (uint16_t)pico_socket_get_mss(s);
 }
 
 static int tcp_synack(struct pico_socket *s, struct pico_frame *f)
@@ -2285,25 +2473,14 @@
     }
 }
 
-static int tcp_closewait(struct pico_socket *s, struct pico_frame *f)
+static void tcp_attempt_closewait(struct pico_socket *s, struct pico_frame *f)
 {
-
     struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
     struct pico_tcp_hdr *hdr  = (struct pico_tcp_hdr *) (f->transport_hdr);
-
-
-    if (f->payload_len > 0)
-        tcp_data_in(s, f);
-
-    if (f->flags & PICO_TCP_ACK)
-        tcp_ack(s, f);
-
-    tcp_dbg("called close_wait, in state %08x\n", s->state);
-
-    if (seq_compare(SEQN(f), t->rcv_nxt) == 0) {
+    if (pico_seq_compare(SEQN(f), t->rcv_nxt) == 0) {
         /* received FIN, increase ACK nr */
         t->rcv_nxt = long_be(hdr->seq) + 1;
-        if (seq_compare(SEQN(f), t->rcv_processed) == 0) {
+        if (pico_seq_compare(SEQN(f), t->rcv_processed) == 0) {
             if ((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_ESTABLISHED) {
                 tcp_dbg("Changing state to CLOSE_WAIT\n");
                 s->state &= 0x00FFU;
@@ -2321,6 +2498,22 @@
         }
     }
 
+
+}
+
+static int tcp_closewait(struct pico_socket *s, struct pico_frame *f)
+{
+
+    struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
+    if (f->payload_len > 0)
+        tcp_data_in(s, f);
+
+    if (f->flags & PICO_TCP_ACK)
+        tcp_ack(s, f);
+
+    tcp_dbg("called close_wait, in state %08x\n", s->state);
+    tcp_attempt_closewait(s, f);
+
     /* Ensure that the notification given to the socket
      * did not put us in LAST_ACK state before sending the ACK: i.e. if
      * pico_socket_close() has been called in the socket callback, we don't need to send
@@ -2369,7 +2562,8 @@
     s->state |= PICO_SOCKET_STATE_TCP_TIME_WAIT;
     /* set SHUT_REMOTE */
     s->state |= PICO_SOCKET_STATE_SHUT_REMOTE;
-    pico_timer_add(2000, tcp_deltcb, t);
+
+    tcp_linger(t);
 
     return 0;
 }
@@ -2382,6 +2576,18 @@
     (t->sock).state |= PICO_SOCKET_STATE_TCP_CLOSED;
     (t->sock).state &= 0xFF00U;
     (t->sock).state |= PICO_SOCKET_STATE_CLOSED;
+    /* call EV_ERR wakeup before deleting */
+    if (((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_ESTABLISHED)) {
+        if ((t->sock).wakeup)
+            (t->sock).wakeup(PICO_SOCK_EV_FIN, &(t->sock));
+    } else {
+        pico_err = PICO_ERR_ECONNRESET;
+        if ((t->sock).wakeup)
+            (t->sock).wakeup(PICO_SOCK_EV_ERR, &(t->sock));
+
+        /* delete socket */
+        pico_socket_del(&t->sock);
+    }
 }
 
 static void tcp_wakeup_pending(struct pico_socket *s, uint16_t ev)
@@ -2401,9 +2607,6 @@
         /* the RST is acceptable if the ACK field acknowledges the SYN */
         if ((t->snd_nxt + 1u) == ACKN(f)) {              /* valid, got to closed state */
             tcp_force_closed(s);
-            pico_err = PICO_ERR_ECONNRESET;
-            tcp_wakeup_pending(s, PICO_SOCK_EV_ERR);
-            pico_socket_del(&t->sock);              /* delete socket */
         } else {                  /* not valid, ignore */
             tcp_dbg("TCP RST> IGNORE\n");
             return 0;
@@ -2413,20 +2616,7 @@
            a reset is valid if its sequence number is in the window */
         uint32_t this_seq = long_be(hdr->seq);
         if ((this_seq >= t->rcv_ackd) && (this_seq <= ((uint32_t)(short_be(hdr->rwnd) << (t->wnd_scale)) + t->rcv_ackd))) {
-            if ((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_SYN_RECV) {
-                tcp_force_closed(s);
-                pico_err = PICO_ERR_ECONNRESET;
-                tcp_wakeup_pending(s, PICO_SOCK_EV_ERR);
-                pico_socket_del(&t->sock);              /* delete socket */
-                tcp_dbg("TCP RST> SOCKET BACK TO LISTEN\n");
-                /*   pico_socket_del(s); */
-            } else {
-                tcp_force_closed(s);
-                tcp_wakeup_pending(s, PICO_SOCK_EV_FIN);
-                pico_err = PICO_ERR_ECONNRESET;
-                tcp_wakeup_pending(s, PICO_SOCK_EV_ERR);
-                pico_socket_del(&t->sock);              /* delete socket */
-            }
+            tcp_force_closed(s);
         } else {                  /* not valid, ignore */
             tcp_dbg("TCP RST> IGNORE\n");
             return 0;
@@ -2448,7 +2638,7 @@
     struct pico_socket_tcp *t = (struct pico_socket_tcp *) s;
     struct pico_tcp_hdr *hdr  = (struct pico_tcp_hdr *) (fr->transport_hdr);
 
-    if (seq_compare(SEQN(fr), t->rcv_nxt) == 0) {
+    if (pico_seq_compare(SEQN(fr), t->rcv_nxt) == 0) {
         /* received FIN, increase ACK nr */
         t->rcv_nxt = long_be(hdr->seq) + 1;
         s->state &= 0x00FFU;
@@ -2485,11 +2675,11 @@
     { PICO_SOCKET_STATE_TCP_LAST_ACK,     NULL,            &tcp_ack,          &tcp_lastackwait,  &tcp_send_rst,   &tcp_send_rst,   &tcp_send_rst,   &tcp_rst },
     { PICO_SOCKET_STATE_TCP_FIN_WAIT1,    NULL,            &tcp_ack,          &tcp_finwaitack,   &tcp_data_in,    &tcp_rcvfin,     &tcp_finack,     &tcp_rst },
     { PICO_SOCKET_STATE_TCP_FIN_WAIT2,    NULL,            &tcp_ack,          &tcp_ack,          &tcp_data_in,    &tcp_finwaitfin, &tcp_finack,     &tcp_rst },
-    { PICO_SOCKET_STATE_TCP_CLOSING,      NULL,            &tcp_ack,          &tcp_closewaitack, &tcp_send_rst,   &tcp_send_rst,   &tcp_send_rst,   &tcp_rst },
-    { PICO_SOCKET_STATE_TCP_TIME_WAIT,    NULL,            &tcp_ack,          &tcp_send_rst,     &tcp_send_rst,   &tcp_send_rst,   &tcp_send_rst,   &tcp_rst }
+    { PICO_SOCKET_STATE_TCP_CLOSING,      NULL,            &tcp_ack,          &tcp_closing_ack, &tcp_send_rst,   &tcp_send_rst,   &tcp_send_rst,   &tcp_rst },
+    { PICO_SOCKET_STATE_TCP_TIME_WAIT,    NULL,            NULL,          NULL,     &tcp_send_rst,   NULL, NULL, NULL}
 };
 
-#define MAX_VALID_FLAGS  9  /* Maximum number of valid flag combinations */
+#define MAX_VALID_FLAGS  10  /* Maximum number of valid flag combinations */
 static uint8_t invalid_flags(struct pico_socket *s, uint8_t flags)
 {
     uint8_t i;
@@ -2499,7 +2689,7 @@
         { /* PICO_SOCKET_STATE_TCP_LISTEN     */ PICO_TCP_SYN },
         { /* PICO_SOCKET_STATE_TCP_SYN_SENT   */ PICO_TCP_SYNACK, PICO_TCP_RST, PICO_TCP_RSTACK},
         { /* PICO_SOCKET_STATE_TCP_SYN_RECV   */ PICO_TCP_SYN, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST},
-        { /* PICO_SOCKET_STATE_TCP_ESTABLISHED*/ PICO_TCP_SYN, PICO_TCP_SYNACK, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FIN, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST},
+        { /* PICO_SOCKET_STATE_TCP_ESTABLISHED*/ PICO_TCP_SYN, PICO_TCP_SYNACK, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FIN, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST, PICO_TCP_RSTACK},
         { /* PICO_SOCKET_STATE_TCP_CLOSE_WAIT */ PICO_TCP_SYNACK, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FIN, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST},
         { /* PICO_SOCKET_STATE_TCP_LAST_ACK   */ PICO_TCP_SYNACK, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FIN, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST},
         { /* PICO_SOCKET_STATE_TCP_FIN_WAIT1  */ PICO_TCP_SYNACK, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FIN, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST},
@@ -2516,6 +2706,42 @@
     }
     return 1;
 }
+
+static void tcp_action_call(int (*call)(struct pico_socket *s, struct pico_frame *f), struct pico_socket *s, struct pico_frame *f )
+{
+    if (call)
+        call(s, f);
+}
+
+static int tcp_action_by_flags(const struct tcp_action_entry *action, struct pico_socket *s, struct pico_frame *f, uint8_t flags)
+{
+    int ret = 0;
+    if ((flags == PICO_TCP_ACK) || (flags == (PICO_TCP_ACK | PICO_TCP_PSH))) {
+        tcp_action_call(action->ack, s, f);
+    }
+
+    if ((f->payload_len > 0 || (flags & PICO_TCP_PSH)) &&
+        !(s->state & PICO_SOCKET_STATE_CLOSED) && !TCP_IS_STATE(s, PICO_SOCKET_STATE_TCP_LISTEN))
+    {
+        ret = f->payload_len;
+        tcp_action_call(action->data, s, f);
+    }
+
+    if (flags == PICO_TCP_FIN) {
+        tcp_action_call(action->fin, s, f);
+    }
+
+    if ((flags == (PICO_TCP_FIN | PICO_TCP_ACK)) || (flags == (PICO_TCP_FIN | PICO_TCP_ACK | PICO_TCP_PSH))) {
+        tcp_action_call(action->finack, s, f);
+    }
+
+    if (flags & PICO_TCP_RST) {
+        tcp_action_call(action->rst, s, f);
+    }
+
+    return ret;
+}
+
 int pico_tcp_input(struct pico_socket *s, struct pico_frame *f)
 {
     struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) (f->transport_hdr);
@@ -2529,7 +2755,7 @@
     tcp_dbg("[sam] TCP> [tcp input] t_len: %u\n", f->transport_len);
     tcp_dbg("[sam] TCP> flags = %02x\n", hdr->flags);
     tcp_dbg("[sam] TCP> s->state >> 8 = %u\n", s->state >> 8);
-    tcp_dbg("[%lu] TCP> [tcp input] socket: %p state: %d <-- local port:%u remote port: %u seq: %08x ack: %08x flags: %02x t_len: %u, hdr: %u payload: %d\n", TCP_TIME, s, s->state >> 8, short_be(hdr->trans.dport), short_be(hdr->trans.sport), SEQN(f), ACKN(f), hdr->flags, f->transport_len, (hdr->len & 0xf0) >> 2, f->payload_len );
+    tcp_dbg("[sam] TCP> [tcp input] socket: %p state: %d <-- local port:%u remote port: %u seq: %08x ack: %08x flags: %02x t_len: %u, hdr: %u payload: %d\n", s, s->state >> 8, short_be(hdr->trans.dport), short_be(hdr->trans.sport), SEQN(f), ACKN(f), hdr->flags, f->transport_len, (hdr->len & 0xf0) >> 2, f->payload_len );
 
     /* This copy of the frame has the current socket as owner */
     f->sock = s;
@@ -2540,40 +2766,11 @@
         pico_tcp_reply_rst(f);
     }
     else if (flags == PICO_TCP_SYN) {
-        if (action->syn)
-            action->syn(s, f);
+        tcp_action_call(action->syn, s, f);
     } else if (flags == (PICO_TCP_SYN | PICO_TCP_ACK)) {
-        if (action->synack)
-            action->synack(s, f);
+        tcp_action_call(action->synack, s, f);
     } else {
-        if ((flags == PICO_TCP_ACK) || (flags == (PICO_TCP_ACK | PICO_TCP_PSH))) {
-            if (action->ack) {
-                action->ack(s, f);
-            }
-        }
-
-        if ((f->payload_len > 0 || (flags & PICO_TCP_PSH)) &&
-            !(s->state & PICO_SOCKET_STATE_CLOSED) && !TCP_IS_STATE(s, PICO_SOCKET_STATE_TCP_LISTEN))
-        {
-            ret = f->payload_len;
-            if (action->data)
-                action->data(s, f);
-        }
-
-        if (flags == PICO_TCP_FIN) {
-            if (action->fin)
-                action->fin(s, f);
-        }
-
-        if ((flags == (PICO_TCP_FIN | PICO_TCP_ACK)) || (flags == (PICO_TCP_FIN | PICO_TCP_ACK | PICO_TCP_PSH))) {
-            if (action->finack)
-                action->finack(s, f);
-        }
-
-        if (flags & PICO_TCP_RST) {
-            if (action->rst)
-                action->rst(s, f);
-        }
+        ret = tcp_action_by_flags(action, s, f, flags);
     }
 
     if (s->ev_pending)
@@ -2664,9 +2861,9 @@
         f->timestamp = TCP_TIME;
         add_retransmission_timer(t, t->rto + TCP_TIME);
         tcp_add_options_frame(t, f);
-        seq_diff = seq_compare(SEQN(f), SEQN(una));
+        seq_diff = pico_seq_compare(SEQN(f), SEQN(una));
         if (seq_diff < 0) {
-            dbg(">>> FATAL: seq diff is negative!\n");
+            tcp_dbg(">>> FATAL: seq diff is negative!\n");
             break;
         }
 
@@ -2716,7 +2913,7 @@
         /* Nothing to transmit. */
     }
 
-    if ((t->tcpq_out.frames == 0) && (s->state & PICO_SOCKET_STATE_SHUT_LOCAL)) {              /* if no more packets in queue, XXX replacled !f by tcpq check */
+    if ((t->tcpq_out.frames == 0) && (s->state & PICO_SOCKET_STATE_SHUT_LOCAL)) {              /* if no more packets in queue, XXX replaced !f by tcpq check */
         if(!checkLocalClosing(&t->sock))              /* check if local closing started and send fin */
         {
             checkRemoteClosing(&t->sock);              /* check if remote closing started and send fin */
@@ -2756,7 +2953,7 @@
         return f_new;
     }
 
-    pico_tcp_flags_update(f_new,&t->sock);
+    pico_tcp_flags_update(f_new, &t->sock);
     hdr = (struct pico_tcp_hdr *) f_new->transport_hdr;
     /* init new frame */
     f_new->payload += off;
@@ -2780,19 +2977,82 @@
     hdr->len = (uint8_t)((f_new->payload - f_new->transport_hdr) << 2u | (int8_t)t->jumbo);
 
     tcp_dbg_nagle("NAGLE make - joined %d segments, len %d bytes\n", test, total_payload_len);
-    tcp_add_options_frame(t,f_new);
+    tcp_add_options_frame(t, f_new);
 
     return f_new;
 }
 
+
+
+static int pico_tcp_push_nagle_enqueue(struct pico_socket_tcp *t, struct pico_frame *f)
+{
+    if (pico_enqueue_segment(&t->tcpq_out, f) > 0) {
+        tcp_dbg_nagle("TCP_PUSH - NAGLE - Pushing segment %08x, len %08x to socket %p\n", t->snd_last + 1, f->payload_len, t);
+        t->snd_last += f->payload_len;
+        return f->payload_len;
+    } else {
+        tcp_dbg("Enqueue failed.\n");
+        return 0;
+    }
+}
+
+static int pico_tcp_push_nagle_hold(struct pico_socket_tcp *t, struct pico_frame *f)
+{
+    struct pico_frame *f_new;
+    uint32_t total_len = 0;
+    total_len = f->payload_len + t->tcpq_hold.size;
+    if ((total_len >= t->mss) && ((t->tcpq_out.max_size - t->tcpq_out.size) >= t->mss)) {
+        /* IF enough data in hold (>mss) AND space in out queue (>mss) */
+        /* add current frame in hold and make new segment */
+        if (pico_enqueue_segment(&t->tcpq_hold, f) > 0 ) {
+            tcp_dbg_nagle("TCP_PUSH - NAGLE - Pushed into hold, make new (enqueued frames out %d)\n", t->tcpq_out.frames);
+            t->snd_last += f->payload_len;              /* XXX  WATCH OUT */
+            f_new = pico_hold_segment_make(t);
+        } else {
+            tcp_dbg_nagle("TCP_PUSH - NAGLE - enqueue hold failed 1\n");
+            return 0;
+        }
+
+        /* and put new frame in out queue */
+        if ((f_new != NULL) && (pico_enqueue_segment(&t->tcpq_out, f_new) > 0)) {
+            return f_new->payload_len;
+        } else {
+            tcp_dbg_nagle("TCP_PUSH - NAGLE - enqueue out failed, f_new = %p\n", f_new);
+            return -1;              /* XXX something seriously wrong */
+        }
+    } else {
+        /* ELSE put frame in hold queue */
+        if (pico_enqueue_segment(&t->tcpq_hold, f) > 0) {
+            tcp_dbg_nagle("TCP_PUSH - NAGLE - Pushed into hold (enqueued frames out %d)\n", t->tcpq_out.frames);
+            t->snd_last += f->payload_len;              /* XXX  WATCH OUT */
+            return f->payload_len;
+        } else {
+            pico_err = PICO_ERR_EAGAIN;
+            tcp_dbg_nagle("TCP_PUSH - NAGLE - enqueue hold failed 2\n");
+        }
+    }
+
+    return 0;
+}
+
+
+static int pico_tcp_push_nagle_on(struct pico_socket_tcp *t, struct pico_frame *f)
+{
+    /* Nagle's algorithm enabled, check if ready to send, or put frame in hold queue */
+    if (IS_TCP_IDLE(t) && IS_TCP_HOLDQ_EMPTY(t))
+        return pico_tcp_push_nagle_enqueue(t, f);
+
+    return pico_tcp_push_nagle_hold(t, f);
+}
+
+
+
 /* original behavior kept when Nagle disabled;
    Nagle algorithm added here, keeping hold frame queue instead of eg linked list of data */
 int pico_tcp_push(struct pico_protocol *self, struct pico_frame *f)
 {
     struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *)f->transport_hdr;
     struct pico_socket_tcp *t = (struct pico_socket_tcp *) f->sock;
-    struct pico_frame *f_new;
-    uint32_t total_len = 0;
     IGNORE_PARAMETER(self);
     pico_err = PICO_ERR_NOERR;
     hdr->trans.sport = t->sock.local_port;
@@ -2815,56 +3075,10 @@
             tcp_dbg("Enqueue failed.\n");
             return 0;
         }
+    } else {
+        return pico_tcp_push_nagle_on(t, f);
     }
-    /***************************************************************************/
-    else {
-        /* Nagle's algorithm enabled, check if ready to send, or put frame in hold queue */
-        if (IS_TCP_IDLE(t) && IS_TCP_HOLDQ_EMPTY(t)) {              /* opt 1. send frame */
-            if (pico_enqueue_segment(&t->tcpq_out, f) > 0) {
-                tcp_dbg_nagle("TCP_PUSH - NAGLE - Pushing segment %08x, len %08x to socket %p\n", t->snd_last + 1, f->payload_len, t);
-                t->snd_last += f->payload_len;
-                return f->payload_len;
-            } else {
-                tcp_dbg("Enqueue failed.\n");
-                return 0;
-            }
-        } else {                                    /* opt 2. hold data back */
-            total_len = f->payload_len + t->tcpq_hold.size;
-            if ((total_len >= t->mss) && ((t->tcpq_out.max_size - t->tcpq_out.size) >= t->mss)) {              /* TODO check mss socket */
-                /* IF enough data in hold (>mss) AND space in out queue (>mss) */
-                /* add current frame in hold and make new segment */
-                if (pico_enqueue_segment(&t->tcpq_hold, f) > 0 ) {
-                    tcp_dbg_nagle("TCP_PUSH - NAGLE - Pushed into hold, make new (enqueued frames out %d)\n", t->tcpq_out.frames);
-                    t->snd_last += f->payload_len;              /* XXX  WATCH OUT */
-                    f_new = pico_hold_segment_make(t);
-                } else {
-                    tcp_dbg_nagle("TCP_PUSH - NAGLE - enqueue hold failed 1\n");
-                    return 0;
-                }
-
-                /* and put new frame in out queue */
-                if ((f_new != NULL) && (pico_enqueue_segment(&t->tcpq_out, f_new) > 0)) {
-                    return f_new->payload_len;
-                } else {
-                    tcp_dbg_nagle("TCP_PUSH - NAGLE - enqueue out failed, f_new = %p\n", f_new);
-                    return -1;              /* XXX something seriously wrong */
-                }
-            } else {
-                /* ELSE put frame in hold queue */
-                if (pico_enqueue_segment(&t->tcpq_hold, f) > 0) {
-                    tcp_dbg_nagle("TCP_PUSH - NAGLE - Pushed into hold (enqueued frames out %d)\n", t->tcpq_out.frames);
-                    t->snd_last += f->payload_len;              /* XXX  WATCH OUT */
-                    return f->payload_len;
-                } else {
-                    pico_err = PICO_ERR_EAGAIN;
-                    tcp_dbg_nagle("TCP_PUSH - NAGLE - enqueue hold failed 2\n");
-                    return 0;
-                }
-            }
-        }
-    }
-
-    /***************************************************************************/
+
 }
 
 inline static void tcp_discard_all_segments(struct pico_tcp_queue *tq)
@@ -2899,7 +3113,14 @@
         pico_timer_cancel(tcp->retrans_tmr);
         tcp->retrans_tmr = NULL;
     }
-
+    if(tcp->keepalive_tmr) {
+        pico_timer_cancel(tcp->keepalive_tmr);
+        tcp->keepalive_tmr = NULL;
+    }
+    if(tcp->fin_tmr) {
+        pico_timer_cancel(tcp->fin_tmr);
+        tcp->fin_tmr = NULL;
+    }
     tcp_discard_all_segments(&tcp->tcpq_in);
     tcp_discard_all_segments(&tcp->tcpq_out);
     tcp_discard_all_segments(&tcp->tcpq_hold);
@@ -2942,13 +3163,6 @@
     struct pico_socket_tcp *t = (struct pico_socket_tcp *)sck;
     if(t->tcpq_out.frames == 0)
     {
-        /* canceling retrans timer when closing */
-        if(t->retrans_tmr)
-        {
-            pico_timer_cancel(t->retrans_tmr);
-            t->retrans_tmr = NULL;
-        }
-
         if(!checkLocalClosing(sck))
             checkRemoteClosing(sck);
     }
@@ -2961,6 +3175,7 @@
         pico_socket_del(s);
         return 0;
     }
+
     return -1;
 }
 
@@ -2997,4 +3212,32 @@
     return 0;
 }
 
+int pico_tcp_set_keepalive_probes(struct pico_socket *s, uint32_t value)
+{
+    struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
+    t->ka_probes = value;
+    return 0;
+}
+
+int pico_tcp_set_keepalive_intvl(struct pico_socket *s, uint32_t value)
+{
+    struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
+    t->ka_intvl = value;
+    return 0;
+}
+
+int pico_tcp_set_keepalive_time(struct pico_socket *s, uint32_t value)
+{
+    struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
+    t->ka_time = value;
+    return 0;
+}
+
+int pico_tcp_set_linger(struct pico_socket *s, uint32_t value)
+{
+    struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
+    t->linger_timeout = value;
+    return 0;
+}
+
 #endif /* PICO_SUPPORT_TCP */
--- a/modules/pico_tcp.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_tcp.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    .
@@ -96,7 +96,11 @@
 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_mtu(struct pico_socket *s);
+int pico_tcp_set_keepalive_probes(struct pico_socket *s, uint32_t value);
+int pico_tcp_set_keepalive_intvl(struct pico_socket *s, uint32_t value);
+int pico_tcp_set_keepalive_time(struct pico_socket *s, uint32_t value);
+int pico_tcp_set_linger(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
--- a/modules/pico_udp.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_udp.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    .
@@ -15,7 +15,7 @@
 #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 = {
@@ -37,12 +37,12 @@
 
     if (s) {
         /* Case of outgoing frame */
-        /* dbg("UDP CRC: on outgoing frame\n"); */
+        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 */
-        /* dbg("UDP CRC: on incomming frame\n"); */
+        udp_dbg("UDP CRC: on incomming frame\n");
         pseudo.src.addr = hdr->src.addr;
         pseudo.dst.addr = hdr->dst.addr;
     }
@@ -59,7 +59,9 @@
 {
     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;
+    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;
 
@@ -154,7 +156,28 @@
     return &u->sock;
 }
 
-uint16_t pico_udp_recv(struct pico_socket *s, void *buf, uint16_t len, void *src, uint16_t *port)
+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) {
@@ -163,7 +186,7 @@
             f->payload_len = (uint16_t)(f->transport_len - sizeof(struct pico_udp_hdr));
         }
 
-        /*    dbg("expected: %d, got: %d\n", len, f->payload_len); */
+        udp_dbg("expected: %d, got: %d\n", len, f->payload_len);
         if (src)
             pico_store_network_origin(src, f);
 
@@ -172,6 +195,10 @@
             *port = hdr->sport;
         }
 
+        if (msginfo) {
+            pico_udp_get_msginfo(f, msginfo);
+        }
+
         if (f->payload_len > len) {
             memcpy(buf, f->payload, len);
             f->payload += len;
--- a/modules/pico_udp.h	Wed Apr 09 17:32:25 2014 +0200
+++ b/modules/pico_udp.h	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    .
@@ -32,7 +32,7 @@
 #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);
+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
--- a/stack/pico_device.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/stack/pico_device.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    .
@@ -7,7 +7,6 @@
    Authors: Daniele Lacamera
  *********************************************************************/
 
-
 #include "pico_config.h"
 #include "pico_device.h"
 #include "pico_stack.h"
@@ -16,6 +15,8 @@
 #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;
@@ -39,14 +40,114 @@
 
 PICO_TREE_DECLARE(Device_tree, pico_dev_cmp);
 
-int pico_device_init(struct pico_device *dev, const char *name, uint8_t *mac)
+#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;
 
@@ -56,80 +157,26 @@
     Devices_rr_info.node_in  = NULL;
     Devices_rr_info.node_out = NULL;
     dev->q_in = PICO_ZALLOC(sizeof(struct pico_queue));
-    dev->q_out = PICO_ZALLOC(sizeof(struct pico_queue));
-    if (!dev->q_in || !dev->q_out)
+    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) {
-        dev->eth = PICO_ZALLOC(sizeof(struct pico_ethdev));
-        if (dev->eth) {
-            memcpy(dev->eth->mac.addr, mac, PICO_SIZE_ETH);
-            #ifdef PICO_SUPPORT_IPV6
-            /* modified EUI-64 + invert universal/local bit */
-            linklocal.addr[8] = (mac[0] ^ 0x02);
-            linklocal.addr[9] = mac[1];
-            linklocal.addr[10] = mac[2];
-            linklocal.addr[13] = mac[3];
-            linklocal.addr[14] = mac[4];
-            linklocal.addr[15] = mac[5];
-            if (pico_ipv6_link_add(dev, linklocal, netmask6)) {
-                PICO_FREE(dev->q_in);
-                PICO_FREE(dev->q_out);
-                PICO_FREE(dev->eth);
-                return -1;
-            }
-
-            #endif
-        }
-
-
+        ret = device_init_mac(dev, mac);
     } else {
-        dev->eth = NULL;
-        #ifdef PICO_SUPPORT_IPV6
-        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)) {
-                PICO_FREE(dev->q_in);
-                PICO_FREE(dev->q_out);
-                return -1;
-            }
-        }
-
-        #endif
+        ret = device_init_nomac(dev);
     }
 
-    #ifdef PICO_SUPPORT_IPV6
-    if (dev->eth)
-    {
-        dev->hostvars.mtu = PICO_ETH_MTU;
-        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;
-    #endif
-
-
-    return 0;
+    return ret;
 }
 
 static void pico_queue_destroy(struct pico_queue *q)
@@ -159,8 +206,6 @@
 #endif
     pico_tree_delete(&Device_tree, dev);
 
-
-    pico_tree_delete(&Device_tree, dev);
     Devices_rr_info.node_in  = NULL;
     Devices_rr_info.node_out = NULL;
     PICO_FREE(dev);
@@ -197,7 +242,7 @@
         if (f) {
             if (dev->eth) {
                 f->datalink_hdr = f->buffer;
-                pico_ethernet_receive(f);
+                (void)pico_ethernet_receive(f);
             } else {
                 f->net_hdr = f->buffer;
                 pico_network_receive(f);
@@ -212,29 +257,12 @@
 static int devloop_sendto_dev(struct pico_device *dev, struct pico_frame *f)
 {
 
-    int ret;
     if (dev->eth) {
-        ret = pico_ethernet_send(f);
-        if (0 <= ret) {
-            return -1;
-        } else {
-            if (!pico_source_is_local(f)) {
-                dbg("Destination unreachable -------> SEND ICMP\n");
-                pico_notify_dest_unreachable(f);
-            } else {
-                dbg("Destination unreachable -------> LOCAL\n");
-            }
-
-            pico_frame_discard(f);
-            return 1;
-        }
+        /* Ethernet: pass management of the frame to the pico_ethernet_send() rdv function */
+        return pico_ethernet_send(f);
     } else {
-        /* non-ethernet */
-        if (dev->send(dev, f->start, (int)f->len) <= 0)
-            return -1;
-
-        pico_frame_discard(f);
-        return 1;
+        /* 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 */
     }
 }
 
@@ -246,14 +274,17 @@
             break;
 
         /* Device dequeue + send */
-        f = pico_dequeue(dev->q_out);
+        f = pico_queue_peek(dev->q_out);
         if (!f)
             break;
 
-        if (devloop_sendto_dev(dev, f) < 0)
-            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 */
 
-        loop_score--;
     }
     return loop_score;
 }
@@ -354,7 +385,7 @@
             struct pico_frame *copy = pico_frame_copy(f);
 
             if(!copy)
-                return -1;
+                break;
 
             copy->dev = dev;
             copy->dev->send(copy->dev, copy->start, (int)copy->len);
@@ -365,6 +396,13 @@
             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);
+}
--- a/stack/pico_frame.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/stack/pico_frame.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    .
@@ -10,6 +10,8 @@
 
 #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;
@@ -23,12 +25,18 @@
 
     (*f->usage_count)--;
     if (*f->usage_count <= 0) {
-        PICO_FREE(f->usage_count);
+        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
-        PICO_FREE(f->buffer);
+        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);
     }
@@ -57,38 +65,54 @@
 }
 
 
-static struct pico_frame *pico_frame_do_alloc(uint32_t size, int zerocopy)
+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) {
-        p->buffer = PICO_ZALLOC(size);
+        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->usage_count = PICO_ZALLOC(sizeof(uint32_t));
-    if (!p->usage_count) {
-        if (p->buffer)
-            PICO_FREE(p->buffer);
-
-        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);
@@ -98,12 +122,68 @@
 
 struct pico_frame *pico_frame_alloc(uint32_t size)
 {
-    return pico_frame_do_alloc(size, 0);
+    return pico_frame_do_alloc(size, 0, 0);
 }
 
-struct pico_frame *pico_frame_alloc_skeleton(uint32_t size)
+int pico_frame_grow(struct pico_frame *f, uint32_t size)
 {
-    return pico_frame_do_alloc(size, 1);
+    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)
@@ -138,6 +218,7 @@
 
     /* 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;
@@ -151,51 +232,55 @@
     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)
 {
-    uint8_t *buf = (uint8_t *) inbuf;
-    uint32_t tmp = 0;
-    uint32_t sum = 0;
-    uint32_t i = 0;
+    uint32_t sum;
 
-    for(i = 0; i < len; i += 2u) {
-        tmp = buf[i];
-        sum += (tmp << 8lu);
-        if (len > (i + 1))
-            sum += buf[i + 1];
-    }
-    while (sum >> 16) { /* a second carry is possible! */
-        sum = (sum & 0x0000FFFF) + (sum >> 16);
-    }
-    return (uint16_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)
 {
-    uint8_t *b1 = (uint8_t *) inbuf1;
-    uint8_t *b2 = (uint8_t *) inbuf2;
-    uint32_t tmp = 0;
-    uint32_t sum = 0;
-    uint32_t i = 0;
+    uint32_t sum;
 
-    for(i = 0; i < len1; i += 2u) {
-        tmp = b1[i];
-        sum += (tmp << 8lu);
-        if (len1 > (i + 1))
-            sum += b1[i + 1];
-    }
-    for(i = 0; i < len2; i += 2u) {
-        tmp = b2[i];
-        sum += (tmp << 8lu);
-        if (len2 > (i + 1))
-            sum += b2[i + 1];
-    }
-    while (sum >> 16) { /* a second carry is possible! */
-        sum = (sum & 0x0000FFFF) + (sum >> 16);
-    }
-    return (uint16_t) (~sum);
+    sum = pico_checksum_adder(0, inbuf1, len1);
+    sum = pico_checksum_adder(sum, inbuf2, len2);
+    return pico_checksum_finalize(sum);
 }
 
--- a/stack/pico_protocol.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/stack/pico_protocol.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    .
@@ -123,8 +123,8 @@
 
     if (!next_node)
         return loop_score;
-    else
-        next = next_node->keyValue;
+
+    next = next_node->keyValue;
 
     /* init start node */
     start = next;
--- a/stack/pico_socket.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/stack/pico_socket.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
 
@@ -28,22 +28,11 @@
 
 
 #define PROTO(s) ((s)->proto->proto_number)
-#define PICO_SOCKET4_MTU 1480 /* Ethernet MTU(1500) - IP header size(20) */
-#define PICO_SOCKET6_MTU 1460 /* Ethernet MTU(1500) - IP header size(40) */
+#define PICO_MIN_MSS (1280)
 #define TCP_STATE(s) (s->state & PICO_SOCKET_STATE_TCP)
 
 #ifdef PICO_SUPPORT_MUTEX
 static void *Mutex = NULL;
-#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)
-
-#else
-#define PICOTCP_MUTEX_LOCK(x) do {} while(0)
-#define PICOTCP_MUTEX_UNLOCK(x) do {} while(0)
 #endif
 
 
@@ -51,18 +40,6 @@
 
 #define PICO_SOCKET_MTU 1480 /* Ethernet MTU(1500) - IP header size(20) */
 
-#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
-
 # define frag_dbg(...) do {} while(0)
 
 
@@ -171,7 +148,7 @@
 
 #define INIT_SOCKPORT { {&LEAF, socket_cmp}, 0, 0 }
 
-int sockport_cmp(void *ka, void *kb)
+static int sockport_cmp(void *ka, void *kb)
 {
     struct pico_sockport *a = ka, *b = kb;
     if (a->number < b->number)
@@ -286,9 +263,9 @@
     struct pico_ip6 ip;
     /* IPv6 */
     if (addr)
-        memcpy(&ip.addr, ((struct pico_ip6 *)addr)->addr, sizeof(struct pico_ip6));
+        memcpy(ip.addr, ((struct pico_ip6 *)addr)->addr, sizeof(struct pico_ip6));
     else
-        memcpy(&ip.addr, PICO_IP6_ANY, sizeof(struct pico_ip6));
+        memcpy(ip.addr, PICO_IP6_ANY, sizeof(struct pico_ip6));
 
     if (memcmp(ip.addr, PICO_IP6_ANY, sizeof(struct pico_ip6)) ==  0) {
         if (!sp)
@@ -305,22 +282,28 @@
 
 
 
-static int pico_generic_port_in_use(uint16_t proto, uint16_t port, struct pico_sockport *sp, void *addr)
+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 (pico_port_in_use_by_nat(proto, port)) {
-        return 1;
-    }
+    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;
+        if (pico_port_in_use_ipv4(sp, addr)) {
+            return 1;
+        }
     }
 
 #endif
 
 #ifdef PICO_SUPPORT_IPV6
-    if (pico_port_in_use_ipv6(sp, addr)) {
-        return 1;
+    if (net == &pico_proto_ipv6)
+    {
+        if (pico_port_in_use_ipv6(sp, addr)) {
+            return 1;
+        }
     }
 
 #endif
@@ -331,10 +314,9 @@
 int pico_is_port_free(uint16_t proto, uint16_t port, void *addr, void *net)
 {
     struct pico_sockport *sp;
-    (void) net;
     sp = pico_get_sockport(proto, port);
 
-    if (pico_generic_port_in_use(proto, port, sp, addr))
+    if (pico_generic_port_in_use(proto, port, sp, addr, net))
         return 0;
 
     return 1;
@@ -417,7 +399,7 @@
     pico_tree_insert(&sp->socks, s);
     s->state |= PICO_SOCKET_STATE_BOUND;
     PICOTCP_MUTEX_UNLOCK(Mutex);
-#if DEBUG_SOCKET_TREE
+#ifdef DEBUG_SOCKET_TREE
     {
         struct pico_tree_node *index;
         /* RB_FOREACH(s, socket_tree, &sp->socks) { */
@@ -450,6 +432,8 @@
             f_out = pico_dequeue(&sock->q_out);
         }
     }
+    pico_queue_deinit(&sock->q_in);
+    pico_queue_deinit(&sock->q_out);
     pico_socket_tcp_cleanup(sock);
 }
 
@@ -499,7 +483,7 @@
     pico_multicast_delete(s);
     pico_socket_tcp_delete(s);
     s->state = PICO_SOCKET_STATE_CLOSED;
-    pico_timer_add(3000, socket_garbage_collect, s);
+    pico_timer_add((pico_time)10, socket_garbage_collect, s);
     PICOTCP_MUTEX_UNLOCK(Mutex);
     return 0;
 }
@@ -536,12 +520,18 @@
 
 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;
 }
 
@@ -588,12 +578,19 @@
 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;
 
 }
@@ -676,7 +673,15 @@
 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;
@@ -750,14 +755,14 @@
     return pico_socket_write_attempt(s, buf, len);
 }
 
-uint16_t pico_socket_high_port(uint16_t proto)
+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_TCP
+#ifdef PICO_SUPPORT_UDP
         (proto == PICO_PROTO_UDP) ||
 #endif
         0) {
@@ -869,9 +874,7 @@
 
 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) {
-        return 0;
-    } else if (len < 0) {
+    if (len < 0) {
         pico_err = PICO_ERR_EINVAL;
         return -1;
     }
@@ -969,6 +972,8 @@
             pico_err = PICO_ERR_EINVAL;
             return -1;
         }
+
+        pico_socket_alter_state(s, PICO_SOCKET_STATE_BOUND, 0, 0);
     }
 
     return s->local_port;
@@ -1007,8 +1012,10 @@
 
 static void pico_xmit_frame_set_nofrag(struct pico_frame *f)
 {
-#ifdef PICO_SUPPORT_IPFRAG
-    f->frag = short_be(PICO_IPV4_DONTFRAG);
+#ifdef PICO_SUPPORT_IPV4FRAG
+    f->frag = PICO_IPV4_DONTFRAG;
+#else
+    (void)f;
 #endif
 }
 
@@ -1022,13 +1029,14 @@
     }
 }
 
-static int pico_socket_xmit_one(struct pico_socket *s, const void *buf, const int len, void *src, struct pico_remote_endpoint *ep)
+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;
@@ -1040,7 +1048,7 @@
     f->sock = s;
     transport_flags_update(f, s);
     pico_xmit_frame_set_nofrag(f);
-    if (ep) {
+    if (ep && !f->info) {
         f->info = pico_socket_set_info(ep);
         if (!f->info) {
             pico_frame_discard(f);
@@ -1048,6 +1056,19 @@
         }
     }
 
+    if (msginfo) {
+        f->send_ttl = (uint8_t)msginfo->ttl;
+        f->send_tos = (uint8_t)msginfo->tos;
+        f->dev = msginfo->dev;
+    }
+#ifdef PICO_SUPPORT_IPV6
+    if(IS_SOCK_IPV6(s) && ep && pico_ipv6_is_multicast(&ep->remote_addr.ip6.addr[0])) {
+        f->dev = pico_ipv6_link_find(src);
+        if(!f->dev) {
+            return -1;
+        }
+    }
+#endif
     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);
@@ -1056,57 +1077,67 @@
 
 static int pico_socket_xmit_avail_space(struct pico_socket *s);
 
+#ifdef PICO_SUPPORT_IPV4FRAG
 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);
-#ifdef PICO_SUPPORT_IPFRAG
-    f->frag = short_be(PICO_IPV4_MOREFRAG);
-#endif
+    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 */
-#ifdef PICO_SUPPORT_IPFRAG
-    f->frag = short_be((uint16_t)((uint16_t)(total_payload_written + (uint16_t)hdr_offset) >> 3u));
-#endif
+    f->frag = (uint16_t)((total_payload_written + (uint16_t)hdr_offset) >> 3u); /* first fragment had a header offset */
     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));
-#ifdef PICO_SUPPORT_IPFRAG
-        f->frag |= short_be(PICO_IPV4_MOREFRAG);
-#endif
+        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));
-#ifdef PICO_SUPPORT_IPFRAG
-        f->frag &= short_be(PICO_IPV4_FRAG_MASK);
-#endif
+        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)
+/* Implies ep discarding! */
+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;
+    int retval = 0;
     struct pico_frame *f = NULL;
 
     if (space > len) {
-        return pico_socket_xmit_one(s, buf, len, src, ep);
+        retval = pico_socket_xmit_one(s, buf, len, src, ep, msginfo);
+        pico_endpoint_free(ep);
+        return retval;
     }
 
-#ifdef PICO_SUPPORT_IPFRAG
+#ifdef PICO_SUPPORT_IPV6
+    /* Can't fragment IPv6 */
+    if (is_sock_ipv6(s)) {
+        retval =  pico_socket_xmit_one(s, buf, space, src, ep, msginfo);
+        pico_endpoint_free(ep);
+        return retval;
+    }
+
+#endif
+
+#ifdef PICO_SUPPORT_IPV4FRAG
     while(total_payload_written < len) {
         /* Always allocate the max space available: space + offset */
         if (len < space)
             space = len;
 
+        if (space > len - total_payload_written) /* update space for last fragment */
+            space = len - total_payload_written;
+
         f = pico_socket_frame_alloc(s, (uint16_t)(space + hdr_offset));
         if (!f) {
             pico_err = PICO_ERR_ENOMEM;
@@ -1124,12 +1155,15 @@
             }
         }
 
-        if (!total_payload_written == 0) {
+        f->payload_len = (uint16_t) space;
+        if (total_payload_written == 0) {
             /* First fragment: no payload written yet! */
             pico_socket_xmit_first_fragment_setup(f, space, hdr_offset);
+            space += hdr_offset; /* only first fragments contains transport header */
+            hdr_offset = 0;
         } else {
             /* Next fragment */
-            pico_socket_xmit_next_fragment_setup(f, hdr_offset, total_payload_written, len);
+            pico_socket_xmit_next_fragment_setup(f, pico_socket_sendto_transport_offset(s), total_payload_written, len);
         }
 
         memcpy(f->payload, (const uint8_t *)buf + total_payload_written, f->payload_len);
@@ -1152,20 +1186,56 @@
     (void) f;
     (void) hdr_offset;
     (void) total_payload_written;
-    return pico_socket_xmit_one(s, buf, space, src, ep);
+    retval = pico_socket_xmit_one(s, buf, space, src, ep, msginfo);
+    pico_endpoint_free(ep);
+    return retval;
 
 #endif
 }
 
-uint16_t pico_socket_get_mtu(struct pico_socket *s)
+static void get_sock_dev(struct pico_socket *s)
 {
-    (void)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))
-        return PICO_SOCKET6_MTU;
+        mss -= PICO_SIZE_IP6HDR;
+    else
+#endif
+    mss -= PICO_SIZE_IP4HDR;
+    return mss;
+}
 
-#endif
-    return PICO_SOCKET4_MTU;
+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);
 }
 
 
@@ -1176,10 +1246,10 @@
 
 #ifdef PICO_SUPPORT_TCP
     if (PROTO(s) == PICO_PROTO_TCP) {
-        transport_len = pico_tcp_get_socket_mtu(s);
+        transport_len = (uint16_t)pico_tcp_get_socket_mss(s);
     } else
 #endif
-    transport_len = pico_socket_get_mtu(s);
+    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;
@@ -1191,18 +1261,22 @@
 }
 
 
-static int pico_socket_xmit(struct pico_socket *s, const void *buf, const int len, void *src, struct pico_remote_endpoint *ep)
+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)) {
-        return pico_socket_xmit_fragments(s, buf, len, src, ep);
+        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) {
@@ -1210,7 +1284,7 @@
         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);
+        w = pico_socket_xmit_one(s, (const void *)((const uint8_t *)buf + total_payload_written), chunk_len, src, ep, msginfo);
         if (w <= 0) {
             break;
         }
@@ -1233,11 +1307,14 @@
 }
 
 
-int pico_socket_sendto(struct pico_socket *s, const void *buf, const int len, void *dst, uint16_t remote_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;
@@ -1245,16 +1322,33 @@
 
     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)
+    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 */
+}
 
-    return pico_socket_xmit(s, buf, len, src, remote_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)
@@ -1279,7 +1373,8 @@
     return pico_socket_sendto(s, buf, len, &s->remote_addr, s->remote_port);
 }
 
-int pico_socket_recvfrom(struct pico_socket *s, void *buf, int len, void *orig, uint16_t *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;
@@ -1300,7 +1395,13 @@
 
 #ifdef PICO_SUPPORT_UDP
     if (PROTO(s) == PICO_PROTO_UDP) {
-        return pico_udp_recv(s, buf, (uint16_t)len, orig, remote_port);
+        /* 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
@@ -1321,6 +1422,13 @@
     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);
@@ -1351,10 +1459,45 @@
         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) {
@@ -1389,7 +1532,6 @@
         return -1;
     }
 
-
     /* When given port = 0, get a random high port to bind to. */
     if (*port == 0) {
         *port = pico_socket_high_port(PROTO(s));
@@ -1451,6 +1593,7 @@
         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;
@@ -1465,6 +1608,7 @@
         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;
@@ -1491,7 +1635,7 @@
 #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, 0, 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 {
@@ -1663,9 +1807,16 @@
         return -1;
     }
 
+    /* unbound sockets can be deleted immediately */
+    if (!(s->state & PICO_SOCKET_STATE_BOUND))
+    {
+        socket_garbage_collect((pico_time)10, s);
+        return 0;
+    }
+
 #ifdef PICO_SUPPORT_UDP
     if (PROTO(s) == PICO_PROTO_UDP) {
-        if (mode & PICO_SHUT_RDWR)
+        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);
@@ -1674,14 +1825,15 @@
 #endif
 #ifdef PICO_SUPPORT_TCP
     if (PROTO(s) == PICO_PROTO_TCP) {
-        if(mode & PICO_SHUT_RDWR)
+        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)
+        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_tcp_notify_closing(s);
+        } else if (mode & PICO_SHUT_RD)
             pico_socket_alter_state(s, PICO_SOCKET_STATE_SHUT_REMOTE, 0, 0);
 
     }
@@ -1690,15 +1842,17 @@
     return 0;
 }
 
-int pico_socket_close(struct pico_socket *s)
+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);
 }
@@ -1712,6 +1866,7 @@
 
     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); */
@@ -1722,7 +1877,9 @@
         }
 
         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)) {
@@ -1745,6 +1902,7 @@
         }
 
         break;
+#endif /* PICO_SUPPORT_UDP */
 
     default:
         /* Do nothing */
@@ -1793,7 +1951,7 @@
 #define SL_LOOP_MIN 1
 
 #ifdef PICO_SUPPORT_TCP
-static int checkSocketSanity(struct pico_socket *s)
+static int check_socket_sanity(struct pico_socket *s)
 {
 
     /* checking for pending connections */
@@ -1801,13 +1959,6 @@
         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
@@ -1841,7 +1992,8 @@
             while (f && (loop_score > 0)) {
                 pico_proto_udp.push(&pico_proto_udp, f);
                 loop_score -= 1;
-                f = pico_dequeue(&s->q_out);
+                if (loop_score > 0) /* only dequeue if there is still loop_score, otherwise f might get lost */
+                    f = pico_dequeue(&s->q_out);
             }
         }
 
@@ -1892,7 +2044,7 @@
                 break;
             }
 
-            if(checkSocketSanity(s) < 0)
+            if(check_socket_sanity(s) < 0)
             {
                 pico_socket_del(s);
                 index_tcp = NULL; /* forcing the restart of loop */
@@ -1930,6 +2082,35 @@
     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)
 {
--- a/stack/pico_socket_multicast.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/stack/pico_socket_multicast.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,51 +1,62 @@
-#include <stdint.h>
+#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_ipv6.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(...) 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)
- */
+*                         |
+*                    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;
+    struct pico_tree MCASTSources_ipv6;
+    uint16_t proto;
 };
-
+//Parameters
+struct pico_mcast 
+{
+    struct pico_socket *s;
+    struct pico_mreq *mreq;
+    struct pico_mreq_source *mreq_s;
+    union pico_address *address;
+    union pico_link *mcast_link;
+    struct pico_mcast_listen *listen;
+};
 static int mcast_listen_link_cmp(struct pico_mcast_listen *a, struct pico_mcast_listen *b)
 {
-    /* TODO: IPv6 */
-    if (a->mcast_link.ip4.addr < b->mcast_link.ip4.addr)
+
+    if (a->proto < b->proto)
         return -1;
 
-    /* TODO: IPv6 */
-    if (a->mcast_link.ip4.addr > b->mcast_link.ip4.addr)
+    if (a->proto > b->proto)
         return 1;
 
-    return 0;
+    return pico_address_compare(&a->mcast_link, &b->mcast_link, a->proto);
 }
 
-static int mcast_listen_cmp(void *ka, void *kb)
+static int mcast_listen_grp_cmp(struct pico_mcast_listen *a, struct pico_mcast_listen *b)
 {
-    struct pico_mcast_listen *a = ka, *b = kb;
     if (a->mcast_group.ip4.addr < b->mcast_group.ip4.addr)
         return -1;
 
@@ -53,9 +64,41 @@
         return 1;
 
     return mcast_listen_link_cmp(a, b);
-
+}
+#ifdef PICO_SUPPORT_IPV6
+static int mcast_listen_grp_cmp_ipv6(struct pico_mcast_listen *a, struct pico_mcast_listen *b)
+{
+    int tmp = memcmp(&a->mcast_group.ip6, &b->mcast_group.ip6, sizeof(struct pico_ip6));
+    if(!tmp)
+        return mcast_listen_link_cmp(a, b);
+    return tmp;
 }
+#endif
 
+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);
+}
+#ifdef PICO_SUPPORT_IPV6
+static int mcast_listen_cmp_ipv6(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_ipv6(a, b);
+}
+#endif
 static int mcast_sources_cmp(void *ka, void *kb)
 {
     union pico_address *a = ka, *b = kb;
@@ -67,7 +110,13 @@
 
     return 0;
 }
-
+#ifdef PICO_SUPPORT_IPV6
+static int mcast_sources_cmp_ipv6(void *ka, void *kb)
+{
+    union pico_address *a = ka, *b = kb;
+    return memcmp(&a->ip6, &b->ip6, sizeof(struct pico_ip6));
+}
+#endif
 static int mcast_socket_cmp(void *ka, void *kb)
 {
     struct pico_socket *a = ka, *b = kb;
@@ -97,16 +146,71 @@
 /* gather sources to be filtered */
 PICO_TREE_DECLARE(MCASTFilter, mcast_filter_cmp);
 
+static int mcast_filter_cmp_ipv6(void *ka, void *kb)
+{
+    union pico_address *a = ka, *b = kb;
+    return memcmp(&a->ip6, &b->ip6, sizeof(struct pico_ip6));
+}
+/* gather sources to be filtered */
+PICO_TREE_DECLARE(MCASTFilter_ipv6, mcast_filter_cmp_ipv6);
+
+inline static struct pico_tree *mcast_get_src_tree(struct pico_socket *s,struct pico_mcast *mcast) {
+    if( IS_SOCK_IPV4(s)) {
+        mcast->listen->MCASTSources.compare = mcast_sources_cmp;
+        return &mcast->listen->MCASTSources;
+    } 
+#ifdef PICO_SUPPORT_IPV6 
+    else if( IS_SOCK_IPV6(s) ) {
+        mcast->listen->MCASTSources_ipv6.compare = mcast_sources_cmp_ipv6;
+        return &mcast->listen->MCASTSources_ipv6;
+    }
+#endif
+    return NULL;
+}
+inline static struct pico_tree *mcast_get_listen_tree(struct pico_socket *s) {
+    if( IS_SOCK_IPV4(s)) 
+        return s->MCASTListen;
+#ifdef PICO_SUPPORT_IPV6
+    else if( IS_SOCK_IPV6(s) )  
+        return s->MCASTListen_ipv6;
+#endif
+    return NULL;
+}
+inline static void mcast_set_listen_tree_p_null(struct pico_socket *s) {
+    if( IS_SOCK_IPV4(s)) 
+        s->MCASTListen = NULL;
+#ifdef PICO_SUPPORT_IPV6
+    else if( IS_SOCK_IPV6(s) )  
+        s->MCASTListen_ipv6 = NULL;
+#endif
+}
 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);
+    ltest.mcast_link = *lnk;
+    ltest.mcast_group = *grp;
+
+    if(IS_SOCK_IPV4(s)) 
+        return pico_tree_findKey(s->MCASTListen, &ltest);
+#ifdef PICO_SUPPORT_IPV6
+    else if(IS_SOCK_IPV6(s) ) {
+        ltest.proto = PICO_PROTO_IPV6;
+        return pico_tree_findKey(s->MCASTListen_ipv6, &ltest);
+    }
+#endif
+    return NULL;
 }
-
+static union pico_address *pico_mcast_get_link_address(struct pico_socket *s, union pico_link *mcast_link) {
+    if( IS_SOCK_IPV4(s) ) 
+        return (union pico_address *) &mcast_link->ipv4.address;
+#ifdef PICO_SUPPORT_IPV6
+    if( IS_SOCK_IPV6(s)) 
+        return (union pico_address *) &mcast_link->ipv6.address;
+#endif
+    return NULL;
+}
 static uint8_t pico_mcast_filter_excl_excl(struct pico_mcast_listen *listen)
 {
     /* filter = intersection of EXCLUDEs */
@@ -114,12 +218,24 @@
     /* 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);
+    if(!pico_tree_empty(&MCASTFilter)) {
+        pico_tree_foreach_safe(index, &MCASTFilter, _tmp)
+        {
+            source = pico_tree_findKey(&listen->MCASTSources, index->keyValue);
+            if (!source)
+                pico_tree_delete(&MCASTFilter, index->keyValue);
+        }
     }
+#ifdef PICO_SUPPORT_IPV6
+    if(!pico_tree_empty(&MCASTFilter_ipv6)) {
+        pico_tree_foreach_safe(index, &MCASTFilter_ipv6, _tmp)
+        {
+            source = pico_tree_findKey(&listen->MCASTSources_ipv6, index->keyValue);
+            if (!source)
+                pico_tree_delete(&MCASTFilter_ipv6, index->keyValue);
+        }
+    }
+#endif
     return PICO_IP_MULTICAST_EXCLUDE;
 }
 
@@ -130,12 +246,24 @@
     /* 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);
+    if(!pico_tree_empty(&listen->MCASTSources)) {
+        pico_tree_foreach_safe(index, &listen->MCASTSources, _tmp)
+        {
+            source = pico_tree_findKey(&MCASTFilter, index->keyValue);
+            if (source)
+                pico_tree_delete(&MCASTFilter, source);
+        }
     }
+#ifdef PICO_SUPPORT_IPV6
+    if(!pico_tree_empty(&listen->MCASTSources_ipv6)) {
+        pico_tree_foreach_safe(index, &listen->MCASTSources_ipv6, _tmp)
+        {
+            source = pico_tree_findKey(&MCASTFilter_ipv6, index->keyValue);
+            if (source)
+                pico_tree_delete(&MCASTFilter_ipv6, source);
+        }
+    }
+#endif
     return PICO_IP_MULTICAST_EXCLUDE;
 }
 
@@ -143,23 +271,47 @@
 {
     /* 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;
+    struct pico_tree_node *index = NULL, *_tmp = 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);
+    if(!pico_tree_empty(&listen->MCASTSources)) {    
+        pico_tree_foreach_safe(index, &MCASTFilter, _tmp)
+        {
+            source = pico_tree_findKey(&listen->MCASTSources, index->keyValue);
+            if (!source)
+                pico_tree_delete(&MCASTFilter, index->keyValue);
+        }
     }
+#ifdef PICO_SUPPORT_IPV6
+    if(!pico_tree_empty(&listen->MCASTSources_ipv6)) {    
+        pico_tree_foreach_safe(index, &MCASTFilter_ipv6, _tmp)
+        {
+            source = pico_tree_findKey(&listen->MCASTSources_ipv6, index->keyValue);
+            if (!source)
+               pico_tree_delete(&MCASTFilter_ipv6, index->keyValue);
+        }
+    }
+#endif
     /* 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);
+    if(!pico_tree_empty(&listen->MCASTSources)) {
+        pico_tree_foreach_safe(index, &listen->MCASTSources, _tmp)
+        {
+            source = pico_tree_insert(&MCASTFilter, index->keyValue);
+            if (source)
+                pico_tree_delete(&MCASTFilter, source);
+        }
     }
+#ifdef PICO_SUPPORT_IPV6
+    if(!pico_tree_empty(&listen->MCASTSources_ipv6)) {
+        pico_tree_foreach_safe(index, &listen->MCASTSources_ipv6, _tmp)
+        {
+            source = pico_tree_insert(&MCASTFilter_ipv6, index->keyValue);
+            if (source)
+                pico_tree_delete(&MCASTFilter_ipv6, source);
+        }
+    }
+#endif
     return PICO_IP_MULTICAST_EXCLUDE;
 }
 
@@ -169,11 +321,23 @@
     /* 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);
+    
+    if( !pico_tree_empty(&listen->MCASTSources)) {
+        pico_tree_foreach_safe(index, &listen->MCASTSources, _tmp)
+        {
+            source = index->keyValue;
+            pico_tree_insert(&MCASTFilter, source);
+        }
     }
+#ifdef PICO_SUPPORT_IPV6
+    if( !pico_tree_empty(&listen->MCASTSources_ipv6)) {
+        pico_tree_foreach_safe(index, &listen->MCASTSources_ipv6, _tmp)
+        {
+            source = index->keyValue;
+            pico_tree_insert(&MCASTFilter_ipv6, source);
+        }
+    }
+#endif
     return PICO_IP_MULTICAST_INCLUDE;
 }
 
@@ -185,13 +349,13 @@
 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}
+    /* 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}
+    /* INCL + EXCL */ {.call = pico_mcast_filter_incl_excl},
+    /* INCL + INCL */ {.call = pico_mcast_filter_incl_incl}
     }
 };
 
@@ -218,13 +382,21 @@
     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);
+    if(!pico_tree_empty(&MCASTFilter)) {
+        pico_tree_foreach_safe(index, &MCASTFilter, _tmp)
+        {
+            pico_tree_delete(&MCASTFilter, index->keyValue);
+         }
     }
-
+#ifdef PICO_SUPPORT_IPV6
+    if(!pico_tree_empty(&MCASTFilter_ipv6)) {
+        pico_tree_foreach_safe(index, &MCASTFilter_ipv6, _tmp)
+        {
+            pico_tree_delete(&MCASTFilter_ipv6, index->keyValue);
+        }
+    }
+#endif
     /* construct new filter */
     pico_tree_foreach_safe(index, &MCASTSockets, _tmp)
     {
@@ -235,13 +407,12 @@
                 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;
 }
@@ -249,13 +420,33 @@
 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;
+#ifdef PICO_DEBUG_MCAST
+    char tmp_string[PICO_IPV6_STRING];
+#endif
+    if(!pico_tree_empty(&listen->MCASTSources)) {
+        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;
+            }
         }
     }
+#ifdef PICO_SUPPORT_IPV6
+    if(!pico_tree_empty(&listen->MCASTSources_ipv6)) {
+        pico_tree_foreach(index, &listen->MCASTSources_ipv6)
+        {
+            if (memcmp(&src->ip6 , &((union pico_address *)index->keyValue)->ip6, sizeof(struct pico_ip6))) {
+#ifdef PICO_DEBUG_MCAST                
+                pico_ipv6_to_string(tmp_string, src->ip6.addr);
+                so_mcast_dbg("MCAST: IP %s in included socket source list\n", tmp_string);
+#endif                
+                return 0;
+            }
+        }
+    }
+#endif
+    /* XXX IPV6 ADDRESS */
     so_mcast_dbg("MCAST: IP %08X NOT in included socket source list\n", src->ip4.addr);
     return -1;
 
@@ -264,13 +455,33 @@
 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;
+#ifdef PICO_DEBUG_MCAST
+    char tmp_string[PICO_IPV6_STRING];
+#endif
+    if(!pico_tree_empty(&listen->MCASTSources)) {
+        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;
+            }
         }
     }
+#ifdef PICO_SUPPORT_IPV6
+    if(!pico_tree_empty(&listen->MCASTSources_ipv6)) {
+        pico_tree_foreach(index, &listen->MCASTSources_ipv6)
+        {
+            if (memcmp(&src->ip6 , &((union pico_address *)index->keyValue)->ip6, sizeof(struct pico_ip6))) {
+#ifdef PICO_DEBUG_MCAST                
+                pico_ipv6_to_string(tmp_string, src->ip6.addr);
+                so_mcast_dbg("MCAST: IP %s in excluded socket source list\n", tmp_string);
+#endif                
+                return 0;
+            }
+        }
+    }
+#endif
+    /* XXX IPV6 ADDRESS  */
     so_mcast_dbg("MCAST: IP %08X NOT in excluded socket source list\n", src->ip4.addr);
     return 0;
 }
@@ -287,185 +498,319 @@
     return -1;
 }
 
-static struct pico_ipv4_link *pico_socket_mcast_filter_link_get(struct pico_socket *s)
+static void *pico_socket_mcast_filter_link_get(struct pico_socket *s)
 {
     /* check if no multicast enabled on socket */
     if (!s->MCASTListen)
         return NULL;
+    if( IS_SOCK_IPV4(s) ) {
+        if (!s->local_addr.ip4.addr)
+            return pico_ipv4_get_default_mcastlink();
 
-    return pico_ipv4_link_get(&s->local_addr.ip4);
+        return pico_ipv4_link_get(&s->local_addr.ip4);
+    }
+#ifdef PICO_SUPPORT_IPV6
+    else if( IS_SOCK_IPV6(s)) {
+        if (pico_ipv6_is_null_address(&s->local_addr.ip6))
+            return pico_ipv6_get_default_mcastlink();
+
+        return pico_ipv6_link_get(&s->local_addr.ip6);
+    }
+#endif
+    return NULL;
 }
 
 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;
+    void *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(IS_SOCK_IPV4(s))
+        listen = listen_find(s,(union pico_address *) &((struct pico_ipv4_link*)(mcast_link))->address, mcast_group);
+#ifdef PICO_SUPPORT_IPV6
+    else if(IS_SOCK_IPV6(s))    
+        listen = listen_find(s, (union pico_address *)&((struct pico_ipv6_link*)(mcast_link))->address, mcast_group);
+#endif
     if (!listen)
         return -1;
 
     return pico_socket_mcast_source_filtering(listen, src);
 }
 
-static struct pico_ipv4_link *get_mcast_link(union pico_address *a)
-{
+
+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);
 }
-
+#ifdef PICO_SUPPORT_IPV6
+static struct pico_ipv6_link *get_mcast_link_ipv6(union pico_address *a) {
 
-static int pico_socket_setoption_pre_validation(struct pico_ip_mreq *mreq)
-{
-    if (!mreq)
+    if (pico_ipv6_is_null_address(&a->ip6)) {
+        return pico_ipv6_get_default_mcastlink();
+    }
+    return pico_ipv6_link_get(&a->ip6);
+}
+#endif
+
+static int pico_socket_setoption_pre_validation(struct pico_mreq *mreq)
+    {
+    if (!mreq) 
         return -1;
 
-    if (!mreq->mcast_group_addr.addr)
+    if (!mreq->mcast_group_addr.ip4.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)
+#ifdef PICO_SUPPORT_IPV6
+static int pico_socket_setoption_pre_validation_ipv6(struct pico_mreq *mreq)
 {
     if (!mreq)
         return -1;
 
-    if (!mreq->mcast_group_addr.addr)
+    if (pico_ipv6_is_null_address((struct pico_ip6*)&mreq->mcast_group_addr))
         return -1;
 
     return 0;
 }
+#endif
 
-static struct pico_ipv4_link *pico_socket_setoption_validate_s_mreq(struct pico_ip_mreq_source *mreq)
+static struct pico_ipv4_link *pico_socket_setoption_validate_mreq(struct pico_mreq *mreq)
 {
-    if (pico_socket_setoption_pre_validation_s(mreq) < 0)
+    if (pico_socket_setoption_pre_validation(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))
+    if (pico_ipv4_is_unicast(mreq->mcast_group_addr.ip4.addr)) 
         return NULL;
 
     return get_mcast_link((union pico_address *)&mreq->mcast_link_addr);
 }
 
+#ifdef PICO_SUPPORT_IPV6
+static struct pico_ipv6_link *pico_socket_setoption_validate_mreq_ipv6(struct pico_mreq *mreq)
+{
+    if (pico_socket_setoption_pre_validation_ipv6(mreq) < 0)
+        return NULL;
+
+    if (pico_ipv6_is_unicast((struct pico_ip6 *)&mreq->mcast_group_addr))
+        return NULL;
+    return get_mcast_link_ipv6((union pico_address *)&mreq->mcast_link_addr);
+}
+#endif
+
+static int pico_socket_setoption_pre_validation_s(struct pico_mreq_source *mreq)
+{
+    if (!mreq)
+        return -1;
+
+    if (!mreq->mcast_group_addr.ip4.addr)
+        return -1;
+
+    return 0;
+}
+#ifdef PICO_SUPPORT_IPV6
+static int pico_socket_setoption_pre_validation_s_ipv6(struct pico_mreq_source *mreq)
+{
+    if (!mreq)
+        return -1;
+
+    if (pico_ipv6_is_null_address((struct pico_ip6 *)&mreq->mcast_group_addr))
+        return -1;
+
+    return 0;
+}
+#endif
+
+static struct pico_ipv4_link *pico_socket_setoption_validate_s_mreq(struct pico_mreq_source *mreq)
+{
+    if (pico_socket_setoption_pre_validation_s(mreq) < 0)
+        return NULL;
+
+    if (pico_ipv4_is_unicast(mreq->mcast_group_addr.ip4.addr))
+        return NULL;
+
+    if (!pico_ipv4_is_unicast(mreq->mcast_source_addr.ip4.addr))
+        return NULL;
+
+    return get_mcast_link((union pico_address *)&mreq->mcast_link_addr);
+}
+#ifdef PICO_SUPPORT_IPV6
+static struct pico_ipv6_link *pico_socket_setoption_validate_s_mreq_ipv6(struct pico_mreq_source *mreq)
+{
+    if (pico_socket_setoption_pre_validation_s_ipv6(mreq) < 0) {
+        return NULL;
+    }
+    if (pico_ipv6_is_unicast((struct pico_ip6 *)&mreq->mcast_group_addr)){
+        return NULL;
+    }
+    if (!pico_ipv6_is_unicast((struct pico_ip6 *)&mreq->mcast_source_addr)){
+        return NULL;
+    }
+
+    return get_mcast_link_ipv6(&mreq->mcast_link_addr);
+}
+#endif
 
 static struct pico_ipv4_link *setop_multicast_link_search(void *value, int bysource)
 {
 
-    struct pico_ip_mreq *mreq = NULL;
+    struct pico_mreq *mreq = NULL;
     struct pico_ipv4_link *mcast_link = NULL;
-    struct pico_ip_mreq_source *mreq_src = NULL;
+    struct pico_mreq_source *mreq_src = NULL;
     if (!bysource) {
-        mreq = (struct pico_ip_mreq *) value;
+        mreq = (struct pico_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;
+        if (!mcast_link) 
+           return NULL;
+        if (!mreq->mcast_link_addr.ip4.addr)
+            mreq->mcast_link_addr.ip4.addr = mcast_link->address.addr;
     } else {
-        mreq_src = (struct pico_ip_mreq_source *) value;
-        if (!mreq_src)
-            return NULL;
+        mreq_src = (struct pico_mreq_source *)value;
+        if (!mreq_src) {
+             return NULL;
+        }
 
         mcast_link = pico_socket_setoption_validate_s_mreq(mreq_src);
-        if (!mcast_link)
+        if (!mcast_link) {
             return NULL;
+        }
 
-        if (!mreq_src->mcast_link_addr.addr)
-            mreq_src->mcast_link_addr.addr = mcast_link->address.addr;
+        if (!mreq_src->mcast_link_addr.ip4.addr)
+            mreq_src->mcast_link_addr.ip4 = mcast_link->address;
     }
 
     return mcast_link;
 }
+#ifdef PICO_SUPPORT_IPV6
+static struct pico_ipv6_link *setop_multicast_link_search_ipv6(void *value, int bysource)
+{
+    struct pico_mreq *mreq = NULL;
+    struct pico_ipv6_link *mcast_link = NULL;
+    struct pico_mreq_source *mreq_src = NULL;
+    if (!bysource) {
+        mreq = (struct pico_mreq *)value;
+        mcast_link = pico_socket_setoption_validate_mreq_ipv6(mreq);
+        if (!mcast_link) {
+           return NULL;
+        }
+        if (pico_ipv6_is_null_address(&mreq->mcast_link_addr.ip6))
+            mreq->mcast_link_addr.ip6 = mcast_link->address;
+    } else {
+        mreq_src = (struct pico_mreq_source *)value;
+        if (!mreq_src) {
+            return NULL;
+        }
 
+        mcast_link = pico_socket_setoption_validate_s_mreq_ipv6(mreq_src);
+        if (!mcast_link) {
+            return NULL;
+        }
+        if (pico_ipv6_is_null_address(&mreq_src->mcast_link_addr.ip6))
+            mreq_src->mcast_link_addr.ip6 = mcast_link->address;
+    }
+    return mcast_link;
+}
+#endif
 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;
+    if( IS_SOCK_IPV4(s) ) {
+
+        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;
+    } 
+#ifdef PICO_SUPPORT_IPV6
+    else if( IS_SOCK_IPV6(s)){
+        s->MCASTListen_ipv6 = PICO_ZALLOC(sizeof(struct pico_tree));
+        if (!s->MCASTListen_ipv6) {
+            pico_err = PICO_ERR_ENOMEM;
+            return -1;
+        }
+
+        s->MCASTListen_ipv6->root = &LEAF;
+        s->MCASTListen_ipv6->compare = mcast_listen_cmp_ipv6;
+        return 0;
+
     }
-
-    s->MCASTListen->root = &LEAF;
-    s->MCASTListen->compare = mcast_listen_cmp;
-    return 0;
+#endif
+    return -1;
 }
 
 
-static struct pico_ipv4_link *setopt_multicast_check(struct pico_socket *s, void *value, int alloc, int bysource)
+static void *setopt_multicast_check(struct pico_socket *s, void *value, int alloc, int bysource)
 {
-    struct pico_ipv4_link *mcast_link = NULL;
-
+    void *mcast_link = NULL;
+    struct pico_tree *listen_tree = mcast_get_listen_tree(s);
     if (!value) {
         pico_err = PICO_ERR_EINVAL;
         return NULL;
     }
-
-    mcast_link = setop_multicast_link_search(value, bysource);
-
+    if(IS_SOCK_IPV4(s))
+        mcast_link = setop_multicast_link_search(value, bysource);
+#ifdef PICO_SUPPORT_IPV6
+    else if(IS_SOCK_IPV6(s))
+        mcast_link = setop_multicast_link_search_ipv6(value, bysource);
+#endif    
     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)
+    if (!listen_tree) { /* 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) {
+    struct pico_tree *tree, *listen_tree;
+    struct pico_mcast mcast;
+    listen_tree = mcast_get_listen_tree(s);
+    if(listen_tree) {
         pico_tree_delete(&MCASTSockets, s);
-        pico_tree_foreach_safe(index, s->MCASTListen, _tmp)
+        pico_tree_foreach_safe(index, listen_tree, _tmp)
         {
             listen = index->keyValue;
-            pico_tree_foreach_safe(index2, &listen->MCASTSources, _tmp2)
+            mcast.listen = listen;
+            tree = mcast_get_src_tree(s, &mcast);
+            pico_tree_foreach_safe(index2, tree, _tmp2)
             {
                 source = index->keyValue;
-                pico_tree_delete(&listen->MCASTSources, source);
+                pico_tree_delete(tree, 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);
+            if (filter_mode >= 0) {
+                if(IS_SOCK_IPV4(s))
+                    pico_ipv4_mcast_leave(&listen->mcast_link.ip4, &listen->mcast_group.ip4, 1, (uint8_t)filter_mode, &MCASTFilter);
+#ifdef PICO_SUPPORT_IPV6      
+              else if(IS_SOCK_IPV6(s))
+                    pico_ipv6_mcast_leave(&listen->mcast_link.ip6, &listen->mcast_group.ip6, 1, (uint8_t)filter_mode, &MCASTFilter_ipv6);
+#endif
+            }
+            pico_tree_delete(listen_tree, listen);
             PICO_FREE(listen);
         }
-        PICO_FREE(s->MCASTListen);
+        PICO_FREE(listen_tree);
+        mcast_set_listen_tree_p_null(s);
     }
 }
 
@@ -520,304 +865,346 @@
     pico_err = PICO_ERR_EINVAL;
     return -1;
 }
-
+static int mcast_get_param(struct pico_mcast *mcast, struct pico_socket *s, void *value,int alloc, int by_source) {
+    if(by_source)
+        mcast->mreq_s = (struct pico_mreq_source *)value;
+    else    
+        mcast->mreq = (struct pico_mreq *)value;
+    mcast->mcast_link = setopt_multicast_check(s, value, alloc, by_source);
+    if (!mcast->mcast_link)
+        return -1;
+    mcast->address =  pico_mcast_get_link_address(s, mcast->mcast_link);     
+    if(by_source)
+        mcast->listen = listen_find(s, &(mcast->mreq_s)->mcast_link_addr, &mcast->mreq_s->mcast_group_addr);
+    else
+        mcast->listen = listen_find(s, &(mcast->mreq)->mcast_link_addr, &mcast->mreq->mcast_group_addr);
+    return 0;
+}
 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)
+    int filter_mode = 0;
+    struct pico_mcast mcast;
+    struct pico_tree *tree, *listen_tree;
+    if(mcast_get_param(&mcast, s, value, 1,0) < 0)
         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) {
+    
+    if (mcast.listen) {
+        if (mcast.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 {
+        } 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;
-        }
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+    } 
+    mcast.listen = PICO_ZALLOC(sizeof(struct pico_mcast_listen));
+    if (!mcast.listen) {
+        pico_err = PICO_ERR_ENOMEM;
+        return -1;
+    }
+    mcast.listen->filter_mode = PICO_IP_MULTICAST_EXCLUDE;
+    mcast.listen->mcast_link = mcast.mreq->mcast_link_addr;
+    mcast.listen->mcast_group = mcast.mreq->mcast_group_addr;
+    mcast.listen->proto = s->net->proto_number;
 
-        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;
-        pico_tree_insert(s->MCASTListen, listen);
-    }
-
+    tree = mcast_get_src_tree(s, &mcast);
+    listen_tree = mcast_get_listen_tree(s);
+#ifdef PICO_SUPPORT_IPV6
+    if( IS_SOCK_IPV6(s)) 
+        mcast.listen->proto = PICO_PROTO_IPV6;
+#endif
+    tree->root = &LEAF;
+    pico_tree_insert(listen_tree, mcast.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);
+    filter_mode = pico_socket_aggregate_mcastfilters(mcast.address, &mcast.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);
+    if(IS_SOCK_IPV4(s)) 
+        return pico_ipv4_mcast_join((struct pico_ip4*)&mcast.mreq->mcast_link_addr,(struct pico_ip4*) &mcast.mreq->mcast_group_addr, 1, (uint8_t)filter_mode, &MCASTFilter);
+#ifdef PICO_SUPPORT_IPV6   
+    else if(IS_SOCK_IPV6(s)) { 
+        return pico_ipv6_mcast_join((struct pico_ip6*)&mcast.mreq->mcast_link_addr,(struct pico_ip6*)&mcast.mreq->mcast_group_addr, 1, (uint8_t)filter_mode, &MCASTFilter_ipv6);
+    }
+#endif
+    return -1;
 }
 
 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)
+    struct pico_tree_node *_tmp,*index;
+    struct pico_mcast mcast;
+    struct pico_tree *listen_tree,*tree;
+    if(mcast_get_param(&mcast, s, value, 0,0) < 0)
         return -1;
-
-    listen = listen_find(s, (union pico_address *)&mreq->mcast_link_addr, (union pico_address *)&mreq->mcast_group_addr);
-    if (!listen) {
+    if (!mcast.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_FREE(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);
-        }
+    } 
+    tree = mcast_get_src_tree(s,&mcast);
+    listen_tree = mcast_get_listen_tree(s);
+
+    pico_tree_foreach_safe(index, tree, _tmp)
+    {
+        source = index->keyValue;
+        pico_tree_delete(tree, source);
     }
-
-    filter_mode = pico_socket_aggregate_mcastfilters((union pico_address *)&mcast_link->address, (union pico_address *)&mreq->mcast_group_addr);
+    pico_tree_delete(listen_tree, mcast.listen);
+    PICO_FREE(mcast.listen);
+    if (pico_tree_empty(listen_tree)) {
+        PICO_FREE(listen_tree);
+        mcast_set_listen_tree_p_null(s);
+        pico_tree_delete(&MCASTSockets, s);
+    }
+    
+    filter_mode = pico_socket_aggregate_mcastfilters(mcast.address, &mcast.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);
+    if(IS_SOCK_IPV4(s)) 
+        return pico_ipv4_mcast_leave((struct pico_ip4*) &mcast.mreq->mcast_link_addr,(struct pico_ip4 *) &mcast.mreq->mcast_group_addr, 1, (uint8_t)filter_mode, &MCASTFilter);
+#ifdef PICO_SUPPORT_IPV6    
+    else if(IS_SOCK_IPV6(s)) { }
+        return pico_ipv6_mcast_leave((struct pico_ip6*)&mcast.mreq->mcast_link_addr,(struct pico_ip6*)&mcast.mreq->mcast_group_addr, 1, (uint8_t)filter_mode, &MCASTFilter_ipv6);
+#endif
+    return -1;
 }
 
 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);
-
+    union pico_address stest, *source = NULL;
+    struct pico_mcast mcast;
+    if(mcast_get_param(&mcast, s, value, 0,1) < 0)
+        return -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) {
+    if (!mcast.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);
-            PICO_FREE(source);
-        }
+    } 
+    if (mcast.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;
     }
 
-    filter_mode = pico_socket_aggregate_mcastfilters((union pico_address *)&mcast_link->address, (union pico_address *)&mreq->mcast_group_addr);
+    stest = mcast.mreq_s->mcast_source_addr;
+    if( IS_SOCK_IPV4(s))
+        source = pico_tree_findKey(&mcast.listen->MCASTSources, &stest);
+#ifdef PICO_SUPPORT_IPV6    
+    else if( IS_SOCK_IPV6(s))
+        source = pico_tree_findKey(&mcast.listen->MCASTSources_ipv6, &stest);
+#endif
+    if (!source) {
+        so_mcast_dbg("pico_socket_setoption: ERROR address to unblock not in source list\n");
+        pico_err = PICO_ERR_EADDRNOTAVAIL;
+        return -1;
+    } 
+    if( IS_SOCK_IPV4(s) )
+        pico_tree_delete(&mcast.listen->MCASTSources, source);
+#ifdef PICO_SUPPORT_IPV6
+    else if( IS_SOCK_IPV6(s) )
+        pico_tree_delete(&mcast.listen->MCASTSources_ipv6, source);
+#endif
+
+    filter_mode = pico_socket_aggregate_mcastfilters(mcast.address, &mcast.mreq_s->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);
+    if(IS_SOCK_IPV4(s)) 
+        return pico_ipv4_mcast_leave((struct pico_ip4 *)&mcast.mreq_s->mcast_link_addr,(struct pico_ip4*) &mcast.mreq_s->mcast_group_addr, 0, (uint8_t)filter_mode, &MCASTFilter);
+#ifdef PICO_SUPPORT_IPV6    
+    else if(IS_SOCK_IPV6(s)) { }
+        return pico_ipv6_mcast_leave((struct pico_ip6*)&mcast.mreq_s->mcast_link_addr,(struct pico_ip6*)&mcast.mreq_s->mcast_group_addr, 0, (uint8_t)filter_mode, &MCASTFilter_ipv6);
+#endif
+    return -1;
 }
 
 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)
+    union pico_address stest, *source = NULL;
+    struct pico_mcast mcast;
+    if(mcast_get_param(&mcast, s, value, 0,1) < 0)
         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 (!mcast.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;
-        }
+    } 
+    if (mcast.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 = mcast.mreq_s->mcast_source_addr;
+    if( IS_SOCK_IPV4(s))
+        source = pico_tree_findKey(&mcast.listen->MCASTSources, &stest);
+#ifdef PICO_SUPPORT_IPV6    
+    else if( IS_SOCK_IPV6(s))
+        source = pico_tree_findKey(&mcast.listen->MCASTSources_ipv6, &stest);
+#endif
+    if (source) {
+        so_mcast_dbg("pico_socket_setoption: ERROR address to block already in source list\n");
+        pico_err = PICO_ERR_ENOMEM;
+        return -1;
+    } 
+    source = PICO_ZALLOC(sizeof(union pico_address));
+    if (!source) {
+        pico_err = PICO_ERR_ENOMEM;
+        return -1;
+    }
+    *source = mcast.mreq_s->mcast_source_addr;
+    if( IS_SOCK_IPV4(s) ) 
+        pico_tree_insert(&mcast.listen->MCASTSources, source);
+#ifdef PICO_SUPPORT_IPV6     
+     else if( IS_SOCK_IPV6(s) ) 
+        pico_tree_insert(&mcast.listen->MCASTSources_ipv6, source);
+#endif
 
-        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);
+    filter_mode = pico_socket_aggregate_mcastfilters(mcast.address, &mcast.mreq_s->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);
+    if(IS_SOCK_IPV4(s)) 
+        return pico_ipv4_mcast_join((struct pico_ip4 *) &mcast.mreq_s->mcast_link_addr, (struct pico_ip4*)&mcast.mreq_s->mcast_group_addr, 0, (uint8_t)filter_mode, &MCASTFilter);
+#ifdef PICO_SUPPORT_IPV6    
+    else if(IS_SOCK_IPV6(s)) { }
+        return pico_ipv6_mcast_join((struct pico_ip6 *)&mcast.mreq_s->mcast_link_addr,(struct pico_ip6*)&mcast.mreq_s->mcast_group_addr, 0, (uint8_t)filter_mode, &MCASTFilter_ipv6);
+#endif
+    return -1;
 }
 
 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)
+    union pico_address stest, *source = NULL;
+    struct pico_mcast mcast;
+    struct pico_tree *tree,*listen_tree;
+    if(mcast_get_param(&mcast, s, value, 1,1) < 0)
         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) {
+    listen_tree = mcast_get_listen_tree(s);
+    if (mcast.listen) {
+        tree = mcast_get_src_tree(s,&mcast);
+        if (mcast.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);
+        stest = mcast.mreq_s->mcast_source_addr;
+        source = pico_tree_findKey(tree, &stest);
         if (source) {
             so_mcast_dbg("pico_socket_setoption: ERROR source address to allow already in source list\n");
+            pico_err = PICO_ERR_ENOMEM;
+            return -1;
+        } 
+        source = PICO_ZALLOC(sizeof(union pico_address));
+        if (!source) {
             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);
         }
+        *source = mcast.mreq_s->mcast_source_addr;
+        pico_tree_insert(tree, source);
+      
     } else {
-        listen = PICO_ZALLOC(sizeof(struct pico_mcast_listen));
-        if (!listen) {
+        mcast.listen = PICO_ZALLOC(sizeof(struct pico_mcast_listen));
+        if (!mcast.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;
+        tree = mcast_get_src_tree(s,&mcast);
+        mcast.listen->filter_mode = PICO_IP_MULTICAST_INCLUDE;
+        mcast.listen->mcast_link = mcast.mreq_s->mcast_link_addr;
+        mcast.listen->mcast_group = mcast.mreq_s->mcast_group_addr;
+        tree->root = &LEAF;
         source = PICO_ZALLOC(sizeof(union pico_address));
         if (!source) {
-            PICO_FREE(listen);
+            PICO_FREE(mcast.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);
+#ifdef PICO_SUPPORT_IPV6
+        if( IS_SOCK_IPV6(s)) 
+            mcast.listen->proto = PICO_PROTO_IPV6;
+#endif
+        *source = mcast.mreq_s->mcast_source_addr;
+        pico_tree_insert(tree, source);
+        pico_tree_insert(listen_tree, mcast.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);
+    filter_mode = pico_socket_aggregate_mcastfilters(mcast.address, &mcast.mreq_s->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);
+    if(IS_SOCK_IPV4(s)) 
+        return pico_ipv4_mcast_join((struct pico_ip4 *)&mcast.mreq_s->mcast_link_addr, (struct pico_ip4*)&mcast.mreq_s->mcast_group_addr,  (uint8_t)reference_count, (uint8_t)filter_mode, &MCASTFilter);
+#ifdef PICO_SUPPORT_IPV6    
+    else if(IS_SOCK_IPV6(s)) { }
+        return pico_ipv6_mcast_join((struct pico_ip6 *) &mcast.mreq_s->mcast_link_addr,(struct pico_ip6*)&mcast.mreq_s->mcast_group_addr, (uint8_t)reference_count, (uint8_t)filter_mode, &MCASTFilter_ipv6);
+#endif
+    return -1;
 }
 
 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)
+    union pico_address stest, *source = NULL;
+    struct pico_mcast mcast;
+    struct pico_tree *tree,*listen_tree;
+    if(mcast_get_param(&mcast, s, value, 0,1) < 0)
         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) {
+    listen_tree = mcast_get_listen_tree(s);
+    if (!mcast.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);
-            PICO_FREE(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);
-                }
-            }
+    } 
+    if (mcast.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;
+    }
+    tree = mcast_get_src_tree(s, &mcast);
+    stest = mcast.mreq_s->mcast_source_addr;
+    source = pico_tree_findKey(tree, &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;
+    } 
+    pico_tree_delete(tree, source);
+    if (pico_tree_empty(tree)) { /* 1 if empty, 0 otherwise */
+        reference_count = 1;
+        pico_tree_delete(listen_tree, mcast.listen);
+        PICO_FREE(mcast.listen);
+        if (pico_tree_empty(listen_tree)) {
+            PICO_FREE(listen_tree);
+            mcast_set_listen_tree_p_null(s);
+            pico_tree_delete(&MCASTSockets, s);
         }
     }
 
-    filter_mode = pico_socket_aggregate_mcastfilters((union pico_address *)&mcast_link->address, (union pico_address *)&mreq->mcast_group_addr);
+    filter_mode = pico_socket_aggregate_mcastfilters(mcast.address, &mcast.mreq_s->mcast_group_addr);
     if (filter_mode < 0)
         return -1;
+    if(IS_SOCK_IPV4(s)) 
+        return pico_ipv4_mcast_leave((struct pico_ip4 *) &mcast.mreq_s->mcast_link_addr, (struct pico_ip4*)&mcast.mreq_s->mcast_group_addr,  (uint8_t)reference_count, (uint8_t)filter_mode, &MCASTFilter);
+#ifdef PICO_SUPPORT_IPV6    
+    else if(IS_SOCK_IPV6(s)) { }
+        return pico_ipv6_mcast_leave((struct pico_ip6 *)&mcast.mreq_s->mcast_link_addr,(struct pico_ip6*)&mcast.mreq_s->mcast_group_addr, (uint8_t)reference_count, (uint8_t)filter_mode, &MCASTFilter_ipv6);
+#endif
+    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
 {
--- a/stack/pico_stack.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/stack/pico_stack.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    .
@@ -16,6 +16,8 @@
 #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"
@@ -52,7 +54,7 @@
 
 static uint32_t _rand_seed;
 
-void pico_rand_feed(uint32_t feed)
+void WEAK pico_rand_feed(uint32_t feed)
 {
     if (!feed)
         return;
@@ -62,12 +64,26 @@
     _rand_seed ^= ~(feed);
 }
 
-uint32_t pico_rand(void)
+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.
  */
 
@@ -140,9 +156,43 @@
     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 */
-int32_t pico_transport_receive(struct pico_frame *f, uint8_t proto)
+MOCKABLE int32_t pico_transport_receive(struct pico_frame *f, uint8_t proto)
 {
     int32_t ret = -1;
     switch (proto) {
@@ -160,7 +210,7 @@
 #endif
 
 
-#ifdef PICO_SUPPORT_IGMP
+#if defined(PICO_SUPPORT_IGMP) && defined(PICO_SUPPORT_MCAST)
     case PICO_PROTO_IGMP:
         ret = pico_enqueue(pico_proto_igmp.q_in, f);
         break;
@@ -210,7 +260,6 @@
     return (int32_t)f->buffer_len;
 }
 
-
 /* Network layer: interface towards socket for frame sending */
 int32_t pico_network_send(struct pico_frame *f)
 {
@@ -238,15 +287,15 @@
 #endif
 #ifdef PICO_SUPPORT_IPV6
     else if (IS_IPV6(f)) {
-        /* XXX */
+        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.
  */
@@ -256,57 +305,6 @@
  * into the stack.
  */
 
-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) || defined (PICO_SUPPORT_IPV6)
-    if ((hdr->proto == PICO_IDETH_IPV4) || (hdr->proto == PICO_IDETH_IPV6))
-        return pico_network_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 int destination_is_bcast(struct pico_frame *f)
 {
     if (!f)
@@ -348,6 +346,96 @@
     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;
@@ -376,121 +464,181 @@
 }
 #endif
 
-struct pico_eth *pico_ethernet_ipv6_dst(struct pico_frame *f)
+static int pico_ethernet_ipv6_dst(struct pico_frame *f, struct pico_eth *const dstmac)
 {
-    struct pico_eth *dstmac = NULL;
+    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
         };
-        dstmac = pico_ethernet_mcast6_translate(f, pico_mcast6_mac);
+        pico_ethernet_mcast6_translate(f, pico_mcast6_mac);
+        memcpy(dstmac, pico_mcast6_mac, PICO_SIZE_ETH);
+        retval = 0;
     } else {
-        dstmac = pico_nd_get(f);
+        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 dstmac;
+    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;
 
-/* This is called by dev loop in order to ensure correct ethernet addressing.
- * Returns 0 if the destination is unknown, and -1 if the packet is not deliverable
- * due to ethernet addressing (i.e., no arp association was possible.
- *
- * Only IP packets must pass by this. ARP will always use direct dev->send() function, so
- * we assume IP is used.
- */
-
-static int32_t pico_ethsend_local(struct pico_frame *f, struct pico_eth_hdr *hdr, int *ret)
-{
     /* 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");
-        *ret = (int32_t)pico_ethernet_receive(f);
+        (void)pico_ethernet_receive(clone);
         return 1;
     }
 
     return 0;
 }
 
-static int32_t pico_ethsend_bcast(struct pico_frame *f, int *ret)
+/* 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)) {
-        *ret = pico_device_broadcast(f);
+        (void)pico_device_broadcast(f); /* We can discard broadcast even if it's not sent. */
         return 1;
     }
 
     return 0;
 }
 
-static int32_t pico_ethsend_dispatch(struct pico_frame *f, int *ret)
+/* 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)
 {
-    *ret = f->dev->send(f->dev, f->start, (int) f->len);
-    if (*ret <= 0)
-        return 0;
+    int ret = f->dev->send(f->dev, f->start, (int) f->len);
+    if (ret <= 0)
+        return 0; /* Failure to deliver! */
     else {
-        pico_frame_discard(f);
-        return 1;
+        return 1; /* Frame is in flight by now. */
     }
 }
 
-int32_t pico_ethernet_send(struct pico_frame *f)
+
+
+
+/* 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)
 {
-    const struct pico_eth *dstmac = NULL;
-    int32_t ret = -1;
+    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)) {
-        dstmac = pico_ethernet_ipv6_dst(f);
-        if (!dstmac)
-            return 0;
+        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
 
-    else if (IS_BCAST(f) || destination_is_bcast(f))
-        dstmac = (const struct pico_eth *) PICO_ETHADDR_ALL;
+    /* 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
         };
-        dstmac = pico_ethernet_mcast_translate(f, pico_mcast_mac);
+        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 {
-#if (defined PICO_SUPPORT_IPV4) && (defined PICO_SUPPORT_ETH)
-        dstmac = pico_arp_get(f);
-        if (!dstmac)
+        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
-        return 0;
-    }
 
     /* This sets destination and source address, then pushes the packet to the device. */
-    if (dstmac && (f->start > f->buffer) && ((f->start - f->buffer) >= PICO_SIZE_ETHHDR)) {
+    if (dstmac_valid) {
         struct pico_eth_hdr *hdr;
-        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 ((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, &ret) || pico_ethsend_bcast(f, &ret) || pico_ethsend_dispatch(f, &ret)) {
-            return ret;
-        } else {
-            return -1;
+        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
@@ -521,6 +669,54 @@
   #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.
@@ -547,9 +743,10 @@
     f->start = f->buffer;
     f->len = f->buffer_len;
     if (f->len > 8) {
-        uint32_t mid_frame = (f->buffer_len >> 2) << 1;
+        uint32_t rand, mid_frame = (f->buffer_len >> 2) << 1;
         mid_frame -= (mid_frame % 4);
-        pico_rand_feed(*(uint32_t*)(f->buffer + mid_frame));
+        memcpy(&rand, f->buffer + mid_frame, sizeof(uint32_t));
+        pico_rand_feed(rand);
     }
 
     memcpy(f->buffer, buffer, len);
@@ -561,14 +758,14 @@
     return ret;
 }
 
-int32_t pico_stack_recv_zerocopy(struct pico_device *dev, uint8_t *buffer, uint32_t len)
+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);
+    f = pico_frame_alloc_skeleton(len, ext_buffer);
     if (!f)
     {
         dbg("Cannot alloc incoming frame!\n");
@@ -583,6 +780,10 @@
         return -1;
     }
 
+    if (notify_free) {
+        f->notify_free = notify_free;
+    }
+
     f->dev = dev;
     ret = pico_enqueue(dev->q_in, f);
     if (ret <= 0) {
@@ -592,6 +793,21 @@
     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) {
@@ -599,9 +815,10 @@
         return -1;
     } else {
         if (f->len > 8) {
-            uint32_t mid_frame = (f->buffer_len >> 2) << 1;
+            uint32_t rand, mid_frame = (f->buffer_len >> 2) << 1;
             mid_frame -= (mid_frame % 4);
-            pico_rand_feed(*(uint32_t*)(f->buffer + mid_frame));
+            memcpy(&rand, f->buffer + mid_frame, sizeof(uint32_t));
+            pico_rand_feed(rand);
         }
 
         return pico_enqueue(f->dev->q_out, f);
@@ -626,7 +843,32 @@
 
 static heap_pico_timer_ref *Timers;
 
-void pico_check_timers(void)
+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);
@@ -637,7 +879,9 @@
             t->timer(pico_tick, t->arg);
 
         if (t)
+        {
             PICO_FREE(t);
+        }
 
         t = NULL;
         heap_peek(Timers, &tref_unused);
@@ -645,7 +889,7 @@
     }
 }
 
-void pico_timer_cancel(struct pico_timer *t)
+void MOCKABLE pico_timer_cancel(struct pico_timer *t)
 {
     uint32_t i;
     struct pico_timer_ref *tref = Timers->top;
@@ -666,7 +910,7 @@
 #define PROTO_DEF_SCORE   32
 #define PROTO_MIN_SCORE   32
 #define PROTO_MAX_SCORE   128
-#define PROTO_LAT_IND     3   /* latecy indication 0-3 (lower is better latency performance), x1, x2, x4, x8 */
+#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)
@@ -754,41 +998,6 @@
     return 0;
 }
 
-
-
-/*
-
-         .
-       .vS.
-     <aoSo.
-    .XoS22.
-    .S2S22.             ._...              ......            ..._.
-   :=|2S2X2|=++;      <vSX2XX2z+          |vSSSXSSs>.      :iXXZUZXXe=
-   )2SS2SS2S2S2I    =oS2S2S2S2X22;.    _vuXS22S2S2S22i  ._wZZXZZZXZZXZX=
-   )22S2S2S2S2Sl    |S2S2S22S2SSSXc:  .S2SS2S2S22S2SS= .]#XZZZXZXZZZZZZ:
-   )oSS2SS2S2Sol     |2}!"""!32S22S(. uS2S2Se**12oS2e  ]dXZZXX2?YYXXXZ*
-   .:2S2So:..-.      .      :]S2S2e;=X2SS2o     .)oc  ]XZZXZ(     =nX:
-    .S2S22.          ___s_i,.)oS2So(;2SS2So,       `  3XZZZZc,      -
-    .S2SSo.        =oXXXSSS2XoS2S2o( XS2S2XSos;.      ]ZZZZXXXX|=
-    .S2S22.      .)S2S2S22S2S2S2S2o( "X2SS2S2S2Sus,,  +3XZZZZZXZZoos_
-    .S2S22.     .]2S2SS22S222S2SS2o(  ]S22S2S2S222So   :3XXZZZZZZZZXXv
-    .S2S22.     =u2SS2e"~---"{2S2So(   -"12S2S2SSS2Su.   "?SXXXZXZZZZXo
-    .S2SSo.     )SS22z;      :S2S2o(       ={vS2S2S22v      .<vXZZZZZZZ;
-    .S2S2S:     ]oSS2c;      =22S2o(          -"S2SS2n          ~4XXZXZ(
-    .2S2S2i     )2S2S2[.    .)XS2So(  <;.      .2S2S2o :<.       ]XZZZX(
-     nX2S2S,,_s_=3oSS2SoaasuXXS2S2o( .oXoasi_aioSSS22l.]dZoaas_aadXZZXZ'
-     vS2SSSXXX2; )S2S2S2SoS2S2S2S2o( iS2S222XSoSS22So.)nXZZXXXZZXXZZXZo
-     x32S22S2Sn  -+S2S2S2S2So22S2So( 12S2SS2S2SS22S}- )SXXZZZZZZZZZXX!-
-      .)S22222i    .i2S2S2o>;:S2S2o(  .<vSoSoSo2S(;     :nXXXXXZXXX(
-       .-~~~~-        --- .   - -        --~~~--           --^^~~-
-                                  .
-
-
-   ... curious about our source code? We are hiring! mailto:<recruiting@tass.be>
-
-
- */
-
 void pico_stack_tick(void)
 {
     static int score[PROTO_DEF_NR] = {
@@ -860,7 +1069,7 @@
     }
 }
 
-struct pico_timer *pico_timer_add(pico_time expire, void (*timer)(pico_time, void *), void *arg)
+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;
@@ -875,7 +1084,7 @@
     tref.tmr = t;
     heap_insert(Timers, &tref);
     if (Timers->n > PICO_MAX_TIMERS) {
-        dbg("Warning: I have %d timers\n", Timers->n);
+        dbg("Warning: I have %d timers\n", (int)Timers->n);
     }
 
     return t;
@@ -900,7 +1109,7 @@
     pico_protocol_init(&pico_proto_icmp6);
 #endif
 
-#ifdef PICO_SUPPORT_IGMP
+#if defined(PICO_SUPPORT_IGMP) && defined(PICO_SUPPORT_MCAST)
     pico_protocol_init(&pico_proto_igmp);
 #endif
 
@@ -932,6 +1141,14 @@
     /* 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();
--- a/stack/pico_tree.c	Wed Apr 09 17:32:25 2014 +0200
+++ b/stack/pico_tree.c	Mon Sep 28 13:16:18 2015 +0200
@@ -1,5 +1,5 @@
 /*********************************************************************
-   PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+   PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved.
    See LICENSE and COPYING for usage.
 
    Author: Andrei Carp <andrei.carp@tass.be>
@@ -125,7 +125,7 @@
     return pico_tree_insert_implementation(tree, key, USE_PICO_ZALLOC);
 }
 
-void *pico_tree_insert_implementation(struct pico_tree*tree, void *key, uint8_t allocator)
+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;
@@ -134,9 +134,11 @@
     int result = 0;
 
     LocalKey = (IS_NOT_LEAF(tree->root) ? pico_tree_findKey(tree, key) : NULL);
+    
     /* if node already in, bail out */
-    if(LocalKey)
+    if(LocalKey) {
         return LocalKey;
+    }
     else
     {
         if(allocator == USE_PICO_PAGE0_ZALLOC)
@@ -207,7 +209,6 @@
 
 
     found = tree->root;
-
     while(IS_NOT_LEAF(found))
     {
         int result;
@@ -296,28 +297,31 @@
     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)
+    if(!delete) 
         return NULL;
-
+    
     lkey = delete->keyValue;
     nodeColor = pico_tree_delete_check_switch(tree, delete, &temp);
 
-    /* deleted node is black, this will mess up the black path property */
-    if(nodeColor == BLACK)
-        fix_delete_collisions(tree, temp);
+    if_nodecolor_black_fix_collisions(tree, temp, nodeColor);
 
     if(allocator == USE_PICO_ZALLOC)
         PICO_FREE(delete);