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

Fork of PicoTCP by Daniele Lacamera

Files at this revision

API Documentation at this revision

Comitter:
daniele
Date:
Sat Aug 03 08:50:27 2013 +0000
Parent:
50:c3b337c38feb
Commit message:
Branch for CDC-ECM: Work in progress

Changed in this revision

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_dev_mbed.cpp Show annotated file Show diff for this revision Revisions of this file
modules/pico_dev_mbed.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_http_client.c Show annotated file Show diff for this revision Revisions of this file
modules/pico_http_client.h Show annotated file Show diff for this revision Revisions of this file
modules/pico_http_server.c Show annotated file Show diff for this revision Revisions of this file
modules/pico_http_server.h Show annotated file Show diff for this revision Revisions of this file
modules/pico_http_util.c Show annotated file Show diff for this revision Revisions of this file
modules/pico_http_util.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_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_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_simple_http.c Show annotated file Show diff for this revision Revisions of this file
modules/pico_simple_http.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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_dev_loop.c	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,68 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+Authors: Daniele Lacamera
+*********************************************************************/
+
+
+#include "pico_device.h"
+#include "pico_dev_loop.h"
+#include "pico_stack.h"
+
+
+#define LOOP_MTU 1500
+static uint8_t l_buf[LOOP_MTU];
+static int l_bufsize = 0;
+
+
+static int pico_loop_send(struct pico_device *dev, void *buf, int len)
+{
+  if (len > LOOP_MTU)
+    return 0;
+
+  if (l_bufsize == 0) {
+    memcpy(l_buf, buf, len);
+    l_bufsize+=len;
+    return len;
+  }
+  return 0;
+}
+
+static int pico_loop_poll(struct pico_device *dev, int loop_score)
+{
+  if (loop_score <= 0)
+    return 0;
+
+  if (l_bufsize > 0) {
+    pico_stack_recv(dev, l_buf, l_bufsize);
+    l_bufsize = 0;
+    loop_score--;
+  }
+  return loop_score;
+}
+
+/* Public interface: create/destroy. */
+
+void pico_loop_destroy(struct pico_device *dev)
+{
+}
+
+struct pico_device *pico_loop_create(void)
+{
+  struct pico_device *loop = pico_zalloc(sizeof(struct pico_device));
+  if (!loop)
+    return NULL;
+
+  if( 0 != pico_device_init((struct pico_device *)loop, "loop", NULL)) {
+    dbg ("Loop init failed.\n");
+    pico_loop_destroy((struct pico_device *)loop);
+    return NULL;
+  }
+  loop->send = pico_loop_send;
+  loop->poll = pico_loop_poll;
+  loop->destroy = pico_loop_destroy;
+  dbg("Device %s created.\n", loop->name);
+  return (struct pico_device *)loop;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_dev_loop.h	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,15 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+*********************************************************************/
+#ifndef _INCLUDE_PICO_LOOP
+#define _INCLUDE_PICO_LOOP
+#include "pico_config.h"
+#include "pico_device.h"
+
+void pico_loop_destroy(struct pico_device *loop);
+struct pico_device *pico_loop_create(void);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_dev_mbed.cpp	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,118 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+Do not redistribute without a written permission by the Copyright
+holders.
+
+Authors: Toon Peters, Maxime Vincent
+*********************************************************************/
+#include "mbed.h"
+extern "C" {
+#include "pico_device.h"
+#include "pico_dev_mbed.h"
+#include "pico_stack.h"
+#include "ethernet_api.h"
+}
+
+struct pico_device_mbed {
+  struct pico_device dev;
+  int bytes_left_in_frame;
+};
+
+#define ETH_MTU 1514
+uint8_t buf[ETH_MTU];
+
+Serial pc(p9, p10, "Serial port"); // tx, rx
+
+extern "C" {
+
+static int pico_mbed_send(struct pico_device *dev, void *buf, int len)
+{
+  int ret, sent;
+  struct pico_device_mbed *mb = (struct pico_device_mbed *) dev;
+
+  if (len > ETH_MTU)
+    return -1;
+
+  /* Write buf content to dev and return amount written */
+  ret = ethernet_write((const char *)buf, len);
+  sent = ethernet_send();
+
+  pc.printf("ETH> sent %d bytes\r\n",ret);
+  if (len != ret || sent != ret)
+    return -1;
+  else
+    return ret;
+}
+
+static int pico_mbed_poll(struct pico_device *dev, int loop_score)
+{
+  int len;
+  struct pico_device_mbed *mb = (struct pico_device_mbed *) dev;
+  
+  while(loop_score > 0)
+  {
+    /* check for new frame(s) */
+    len = (int) ethernet_receive();
+    
+    /* return if no frame has arrived */
+    if (!len)
+      return loop_score;
+  
+    /* read and process frame */
+    len = ethernet_read((char*)buf, ETH_MTU);
+    pc.printf("ETH> recv %d bytes: %x:%x\r\n", len, buf[0],buf[1]);
+    pico_stack_recv(dev, buf, len);
+    loop_score--;
+  }
+  return loop_score;
+}
+
+/* Public interface: create/destroy. */
+void pico_mbed_destroy(struct pico_device *dev)
+{
+  ethernet_free();
+  pico_device_destroy(dev);
+}
+
+struct pico_device *pico_mbed_create(char *name)
+{
+  std::uint8_t mac[PICO_SIZE_ETH];
+  struct pico_device_mbed *mb = (struct pico_device_mbed*) pico_zalloc(sizeof(struct pico_device_mbed));
+
+  if (!mb)
+    return NULL;
+
+  ethernet_address((char *)mac);
+  pc.printf("ETH> Set MAC address to: %x:%x:%x:%x:%x:%x\r\n", mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]);
+
+  if(0 != pico_device_init((struct pico_device *)mb, name, mac)) {
+    pc.printf ("ETH> Loop init failed.\n");
+    //pico_loop_destroy(mb);
+    return NULL;
+  }
+
+  mb->dev.send = pico_mbed_send;
+  mb->dev.poll = pico_mbed_poll;
+  mb->dev.destroy = pico_mbed_destroy;
+  mb->bytes_left_in_frame = 0;
+
+  if(0 != ethernet_init()) {
+    pc.printf("ETH> Failed to initialize hardware.\r\n");
+    pico_device_destroy((struct pico_device *)mb);
+    return NULL;
+  }
+
+  // future work: make the mac address configurable
+
+  pc.printf("ETH> Device %s created.\r\n", mb->dev.name);
+
+  return (struct pico_device *)mb;
+}
+
+void pico_mbed_get_address(char *mac)
+{
+  ethernet_address(mac);
+}
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_dev_mbed.h	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,21 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+Do not redistribute without a written permission by the Copyright
+holders.
+
+File: pico_dev_mbed.h
+Author: Toon Peters
+*********************************************************************/
+
+#ifndef PICO_DEV_MBED_H
+#define    PICO_DEV_MBED_H
+
+#include "pico_config.h"
+#include "pico_device.h"
+
+void pico_mbed_destroy(struct pico_device *vde);
+struct pico_device *pico_mbed_create(char *name);
+
+#endif    /* PICO_DEV_MBED_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_dhcp_client.c	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,731 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+Authors: Frederik Van Slycken, Kristof Roelants
+*********************************************************************/
+
+#include "pico_dhcp_client.h"
+#include "pico_stack.h"
+#include "pico_config.h"
+#include "pico_device.h"
+#include "pico_ipv4.h"
+#include "pico_socket.h"
+
+#ifdef PICO_SUPPORT_DHCPC
+
+/***********
+ * structs *
+ ***********/
+
+static uint8_t dhcp_client_mutex = 1; /* to serialize client negotations if multiple devices */
+
+struct dhcp_timer_param{
+  uint16_t type;
+  struct pico_dhcp_client_cookie* cli;
+  int valid;
+};
+
+struct pico_dhcp_client_cookie
+{
+  uint32_t xid;
+  uint32_t *xid_user;
+  struct pico_ip4 address;
+  struct pico_ip4 netmask;
+  struct pico_ip4 gateway;
+  struct pico_ip4 nameserver;
+  struct pico_ip4 server_id;
+  uint32_t lease_time;
+  uint32_t T1;
+  uint32_t T2;
+  struct pico_socket* socket;
+  int connected;
+  struct pico_device* device;
+  unsigned long start_time;
+  int attempt;
+  enum dhcp_negotiation_state state;
+  void (*cb)(void* cli, int code);
+  struct dhcp_timer_param* timer_param_1;
+  struct dhcp_timer_param* timer_param_2;
+  struct dhcp_timer_param* timer_param_lease;
+  struct dhcp_timer_param* timer_param_retransmit;
+  int link_added;
+};
+
+static int dhcp_cookies_cmp(void *ka, void *kb)
+{
+  struct pico_dhcp_client_cookie *a = ka, *b = kb;
+  if (a->xid < b->xid)
+    return -1; 
+  else if (a->xid > b->xid)
+    return 1;
+  else
+    return 0;
+} 
+PICO_TREE_DECLARE(DHCPCookies, dhcp_cookies_cmp);
+
+/*************************
+ * function declarations *
+ *************************/
+static void pico_dhcp_state_machine(int type, struct pico_dhcp_client_cookie* cli, uint8_t* data, int len);
+static void pico_dhcp_reinitiate_negotiation(unsigned long now, void *arg);
+
+//cb
+static void pico_dhcp_wakeup(uint16_t ev, struct pico_socket *s);
+static void dhcp_timer_cb(unsigned long tick, void* param);
+
+//util
+static void pico_dhcp_retry(struct pico_dhcp_client_cookie *client);
+static int dhclient_send(struct pico_dhcp_client_cookie *cli, uint8_t msg_type);
+static int pico_dhcp_verify_and_identify_type(uint8_t* data, int len, struct pico_dhcp_client_cookie *cli);
+static int init_cookie(struct pico_dhcp_client_cookie* cli);
+static struct pico_dhcp_client_cookie* get_cookie_by_xid(uint32_t xid);
+static uint32_t get_xid(uint8_t* data);
+
+//fsm functions
+static int recv_offer(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
+static int recv_ack(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
+static int renew(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
+static int reset(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
+static int retransmit(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
+
+//fsm implementation
+static void pico_dhcp_state_machine(int type, struct pico_dhcp_client_cookie* cli, uint8_t* data, int len);
+
+/***************
+ * entry point *
+ ***************/
+
+static uint32_t pico_dhcp_execute_init(struct pico_dhcp_client_cookie *cli)
+{
+  if (!dhcp_client_mutex) {
+    pico_timer_add(3000, pico_dhcp_reinitiate_negotiation, cli);
+    return 0;
+  }
+  dhcp_client_mutex--;
+
+  if (init_cookie(cli) < 0)
+    return -1;
+
+  dbg("DHCPC: cookie with xid %u\n", cli->xid);
+  
+  if (pico_tree_insert(&DHCPCookies, cli)) {
+    pico_err = PICO_ERR_EAGAIN;
+    if(cli->cb != NULL) {
+      cli->cb(cli, PICO_DHCP_ERROR);
+    }
+    pico_free(cli);
+    return -1; /* Element key already exists */
+  }
+
+  if (dhclient_send(cli, PICO_DHCP_MSG_DISCOVER) < 0)
+    return -1;
+
+  return 0;
+}
+
+/* returns a pointer to the client cookie. The user should pass this pointer every time he calls a dhcp-function. This is so that we can (one day) support dhcp on multiple interfaces */
+int pico_dhcp_initiate_negotiation(struct pico_device *device, void (*callback)(void *cli, int code), uint32_t *xid)
+{
+  struct pico_dhcp_client_cookie *cli;
+  
+  if(!device || !callback || !xid){
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+  cli = pico_zalloc(sizeof(struct pico_dhcp_client_cookie));
+  if(!cli){
+    pico_err = PICO_ERR_ENOMEM;
+    return -1;
+  }
+
+  cli->device = device;
+  cli->cb = callback;
+  cli->xid_user = xid;
+  *(cli->xid_user) = 0;
+
+  return pico_dhcp_execute_init(cli);
+}
+
+static void pico_dhcp_reinitiate_negotiation(unsigned long now, void *arg)
+{
+  struct pico_dhcp_client_cookie *cli = (struct pico_dhcp_client_cookie *) arg;
+
+  pico_dhcp_execute_init(cli);
+
+  return;
+}
+
+/********************
+ * access functions *
+ ********************/
+
+struct pico_ip4 pico_dhcp_get_address(void* cli)
+{
+  return ((struct pico_dhcp_client_cookie*)cli)->address;
+}
+
+struct pico_ip4 pico_dhcp_get_gateway(void* cli)
+{
+  return ((struct pico_dhcp_client_cookie*)cli)->gateway;
+}
+
+struct pico_ip4 pico_dhcp_get_nameserver(void* cli)
+{
+  return ((struct pico_dhcp_client_cookie*)cli)->nameserver;
+}
+
+/*************
+ * callbacks *
+ *************/
+
+static void pico_dhcp_wakeup(uint16_t ev, struct pico_socket *s)
+{
+  uint8_t buf[DHCPC_DATAGRAM_SIZE];
+  int r=0;
+  uint32_t peer;
+  uint16_t port;
+  int type;
+
+  struct pico_dhcp_client_cookie *cli;
+  dbg("DHCPC: called dhcp_wakeup\n");
+  if (ev == PICO_SOCK_EV_RD) {
+    do {
+      r = pico_socket_recvfrom(s, buf, DHCPC_DATAGRAM_SIZE, &peer, &port);
+      cli = get_cookie_by_xid(get_xid(buf));
+      if(cli == NULL)
+        return;
+      if (r > 0 && port == PICO_DHCPD_PORT) {
+        type = pico_dhcp_verify_and_identify_type(buf, r, cli);
+        pico_dhcp_state_machine(type, cli, buf, r);
+      }
+    } while(r>0);
+  }
+}
+
+static void dhcp_timer_cb(unsigned long tick, void* param)
+{
+  struct dhcp_timer_param* param2 = (struct dhcp_timer_param*) param;
+  if(param2->valid == 1){
+    //dbg("called timer cb on active timer type %d\n",param2->type);
+    pico_dhcp_state_machine(param2->type, param2->cli, NULL, 0);
+  }
+  if(param2->cli->timer_param_1 == param){
+    param2->cli->timer_param_1 = NULL;
+  }
+  if(param2->cli->timer_param_2 == param){
+    param2->cli->timer_param_2 = NULL;
+  }
+  if(param2->cli->timer_param_lease == param){
+    param2->cli->timer_param_lease = NULL;
+  }
+  if(param2->cli->timer_param_retransmit == param){
+    param2->cli->timer_param_retransmit = NULL;
+  }
+
+  pico_free(param);
+
+}
+/*****************
+ * fsm functions *
+ *****************/
+
+static int recv_offer(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len)
+{
+  struct pico_dhcphdr *dhdr = (struct pico_dhcphdr *) data;
+  uint8_t *nextopt, opt_data[20], opt_type;
+  int opt_len = 20;
+  uint8_t msg_type = 0xFF;
+  int T1_set = 0;
+  int T2_set = 0;
+
+  cli->address.addr = dhdr->yiaddr;
+
+  opt_type = dhcp_get_next_option(dhdr->options, opt_data, &opt_len, &nextopt);
+  while (opt_type != PICO_DHCPOPT_END) {
+    if (opt_type == PICO_DHCPOPT_MSGTYPE)
+      msg_type = opt_data[0];
+    if ((opt_type == PICO_DHCPOPT_LEASETIME) && (opt_len == 4)){
+      memcpy(&cli->lease_time, opt_data, 4);
+      cli->lease_time = long_be(cli->lease_time);
+    }
+    if ((opt_type == PICO_DHCPOPT_RENEWALTIME) && (opt_len == 4)){
+      memcpy(&cli->T1, opt_data, 4);
+      cli->T1 = long_be(cli->T1);
+      T1_set =1;
+    }
+    if ((opt_type == PICO_DHCPOPT_REBINDINGTIME) && (opt_len == 4)){
+      memcpy(&cli->T2, opt_data, 4);
+      cli->T2 = long_be(cli->T2);
+      T2_set =1;
+    }
+    if ((opt_type == PICO_DHCPOPT_ROUTER) && (opt_len == 4)) //XXX assuming only one router will be advertised...
+      memcpy(&cli->gateway.addr, opt_data, 4);
+    if ((opt_type == PICO_DHCPOPT_DNS) && (opt_len == 4))
+      memcpy(&cli->nameserver.addr, opt_data, 4);
+    if ((opt_type == PICO_DHCPOPT_NETMASK) && (opt_len == 4))
+      memcpy(&cli->netmask.addr, opt_data, 4);
+    if ((opt_type == PICO_DHCPOPT_SERVERID) && (opt_len == 4))
+      memcpy(&cli->server_id.addr, opt_data, 4);
+    if (opt_type == PICO_DHCPOPT_OPTIONOVERLOAD)
+      dbg("DHCPC: WARNING: option overload present (not processed)");
+
+    opt_len = 20;
+    opt_type = dhcp_get_next_option(NULL, opt_data, &opt_len, &nextopt);
+  }
+
+  /* default values for T1 and T2 if necessary */
+  if(T1_set != 1)
+    cli->T1 = cli->lease_time >> 1;
+  if(T2_set != 1)
+    cli->T2 = (cli->lease_time * 875) / 1000;
+
+
+
+  if ((msg_type != PICO_DHCP_MSG_OFFER) || !cli->lease_time || !cli->netmask.addr || !cli->server_id.addr )
+    return 0;
+
+
+  dhclient_send(cli, PICO_DHCP_MSG_REQUEST);
+  cli->state = DHCPSTATE_REQUEST;
+  return 1;
+}
+
+static int recv_ack(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len)
+{
+  struct pico_ip4 address = {0};
+
+  if(cli->link_added == 0){
+    /* close the socket bound on address 0.0.0.0 */
+    pico_socket_close(cli->socket);
+    cli->socket = NULL;
+    pico_ipv4_link_del(cli->device, address);
+    pico_ipv4_link_add(cli->device, cli->address, cli->netmask);
+    cli->link_added = 1;
+  }
+  cli->state = DHCPSTATE_BOUND;
+
+  dbg("DHCPC: T1: %d\n",cli->T1);
+  dbg("DHCPC: T2: %d\n",cli->T2);
+  dbg("DHCPC: lease time: %d\n",cli->lease_time);
+
+  if(cli->timer_param_1)
+    cli->timer_param_1->valid = 0;
+  if(cli->timer_param_2)
+    cli->timer_param_2->valid = 0;
+  if(cli->timer_param_lease)
+    cli->timer_param_lease->valid = 0;
+  if(cli->timer_param_retransmit)
+    cli->timer_param_retransmit->valid = 0;
+
+
+  cli->timer_param_1 = pico_zalloc(sizeof(struct dhcp_timer_param));
+  if(!cli->timer_param_1){
+    if(cli->cb != NULL){
+      pico_err = PICO_ERR_ENOMEM;
+      cli->cb(cli, PICO_DHCP_ERROR);
+    }
+    return 0;
+  }
+  cli->timer_param_2 = pico_zalloc(sizeof(struct dhcp_timer_param));
+  if(!cli->timer_param_2){
+    if(cli->cb != NULL){
+      pico_err = PICO_ERR_ENOMEM;
+      cli->cb(cli, PICO_DHCP_ERROR);
+    }
+    return 0;
+  }
+  cli->timer_param_lease = pico_zalloc(sizeof(struct dhcp_timer_param));
+  if(!cli->timer_param_lease){
+    if(cli->cb != NULL){
+      pico_err = PICO_ERR_ENOMEM;
+      cli->cb(cli, PICO_DHCP_ERROR);
+    }
+    return 0;
+  }
+  cli->timer_param_1->valid = 1;
+  cli->timer_param_2->valid = 1;
+  cli->timer_param_lease->valid = 1;
+
+  cli->timer_param_1->cli = cli;
+  cli->timer_param_2->cli = cli;
+  cli->timer_param_lease->cli = cli;
+
+  cli->timer_param_1->type = PICO_DHCP_EVENT_T1;
+  cli->timer_param_2->type = PICO_DHCP_EVENT_T2;
+  cli->timer_param_lease->type = PICO_DHCP_EVENT_LEASE;
+  //add timer
+  pico_timer_add(cli->T1*1000, dhcp_timer_cb, cli->timer_param_1);
+  pico_timer_add(cli->T2*1000, dhcp_timer_cb, cli->timer_param_2);
+  pico_timer_add(cli->lease_time*1000, dhcp_timer_cb, cli->timer_param_lease);
+
+  *(cli->xid_user) = cli->xid;
+  if(cli->cb != NULL)
+    cli->cb(cli, PICO_DHCP_SUCCESS);
+  else
+    dbg("DHCPC: no callback\n");
+
+  dhcp_client_mutex++;
+  cli->state = DHCPSTATE_BOUND;
+  return 0;
+}
+
+static int renew(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len)
+{
+  uint16_t port = PICO_DHCP_CLIENT_PORT;
+
+  /* open and bind to currently acquired address */
+  if (!cli->socket){
+    cli->socket = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcp_wakeup);
+    if (!cli->socket) {
+      dbg("DHCPC: error opening socket on renew: %s\n", strerror(pico_err));
+      if(cli->cb != NULL)
+        cli->cb(cli, PICO_DHCP_ERROR);
+      return -1;
+    }
+    if (pico_socket_bind(cli->socket, &cli->address, &port) != 0){
+      dbg("DHCPC: error binding socket on renew: %s\n", strerror(pico_err));
+      pico_socket_close(cli->socket);
+      if(cli->cb != NULL)
+        cli->cb(cli, PICO_DHCP_ERROR);
+      return -1;
+    }
+  }
+  cli->state = DHCPSTATE_RENEWING;
+  dhclient_send(cli, PICO_DHCP_MSG_REQUEST);
+
+  return 0;
+}
+
+static int reset(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len)
+{
+  if(cli->cb != NULL)
+    cli->cb(cli, PICO_DHCP_RESET);
+  //reset pretty much everything
+
+  if(cli->timer_param_1)
+    cli->timer_param_1->valid = 0;
+  if(cli->timer_param_2)
+    cli->timer_param_2->valid = 0;
+  if(cli->timer_param_lease)
+    cli->timer_param_lease->valid = 0;
+  if(cli->timer_param_retransmit)
+    cli->timer_param_retransmit->valid = 0;
+
+  pico_socket_close(cli->socket);
+  pico_ipv4_link_del(cli->device, cli->address);
+
+  //initiate negotiations again
+  init_cookie(cli);
+  pico_dhcp_retry(cli);
+  dhclient_send(cli, PICO_DHCP_MSG_DISCOVER);
+
+  return 0;
+
+}
+
+static int retransmit(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len)
+{
+  pico_dhcp_retry(cli);
+
+  if(cli->state == DHCPSTATE_DISCOVER)
+    dhclient_send(cli, PICO_DHCP_MSG_DISCOVER);
+  else if(cli->state == DHCPSTATE_RENEWING)
+    dhclient_send(cli, PICO_DHCP_MSG_REQUEST);
+  else
+    dbg("DHCPC: WARNING: should not get here in state %d!\n", cli->state);
+
+  return 0;
+
+}
+
+/**********************
+ * fsm implementation *
+ **********************/
+
+struct dhcp_action_entry {
+  uint16_t tcpstate;
+  int (*offer)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
+  int (*ack)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
+  int (*nak)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
+  int (*timer1)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
+  int (*timer_lease)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
+  int (*timer_retransmit)(struct pico_dhcp_client_cookie *cli, uint8_t *data, int len);
+};
+
+static struct dhcp_action_entry dhcp_fsm[] = {
+    /* State             offer       ack       nak     timer1  timer_lease timer_retransmit*/
+  { DHCPSTATE_DISCOVER,  recv_offer, NULL,     NULL,   NULL,   reset,      retransmit},
+  { DHCPSTATE_OFFER,     NULL,       NULL,     NULL,   NULL,   reset,      NULL},
+  { DHCPSTATE_REQUEST,   NULL,       recv_ack, reset,  NULL,   reset,      retransmit},
+  { DHCPSTATE_BOUND,     NULL,       NULL,     reset,  renew,  reset,      NULL},
+  { DHCPSTATE_RENEWING,  NULL,       recv_ack, reset,  NULL,   reset,      retransmit},
+};
+
+
+static void pico_dhcp_state_machine(int type, struct pico_dhcp_client_cookie* cli, uint8_t* data, int len)
+{
+  dbg("DHCPC: received incoming event of type %d\n", type);
+  switch(type){
+    case PICO_DHCP_MSG_OFFER:
+      if(dhcp_fsm[cli->state].offer != NULL)
+        dhcp_fsm[cli->state].offer(cli, data, len);
+      break;
+    case PICO_DHCP_MSG_ACK:
+      if(dhcp_fsm[cli->state].ack != NULL){
+        dhcp_fsm[cli->state].ack(cli, data, len);
+      }
+      break;
+    case PICO_DHCP_MSG_NAK:
+      if(dhcp_fsm[cli->state].nak!= NULL){
+        dhcp_fsm[cli->state].nak(cli, data, len);
+      }
+      break;
+    case PICO_DHCP_EVENT_T1:
+      if(dhcp_fsm[cli->state].timer1!= NULL){
+        dhcp_fsm[cli->state].timer1(cli, NULL, 0);
+      }
+      break;
+    case PICO_DHCP_EVENT_LEASE:
+      if(dhcp_fsm[cli->state].timer_lease!= NULL){
+        dhcp_fsm[cli->state].timer_lease(cli, NULL, 0);
+      }
+      break;
+    case PICO_DHCP_EVENT_RETRANSMIT:
+      if(dhcp_fsm[cli->state].timer_retransmit!= NULL){
+        dhcp_fsm[cli->state].timer_retransmit(cli, NULL, 0);
+      }
+      break;
+    default:
+      dbg("DHCPC: event not supported yet!!\n");
+      break;
+  }
+}
+
+
+/*********************
+ * utility functions *
+ *********************/
+
+static void pico_dhcp_retry(struct pico_dhcp_client_cookie *cli)
+{
+  const int MAX_RETRY = 3;
+  uint32_t new_xid;
+  if (++cli->attempt > MAX_RETRY) {
+    cli->start_time = pico_tick;
+    cli->attempt = 0;
+     new_xid = pico_rand();
+    while(get_cookie_by_xid(new_xid) != NULL){
+      new_xid = pico_rand();
+    }
+    cli->xid = new_xid;
+    cli->state = DHCPSTATE_DISCOVER;
+  }
+}
+
+static int dhclient_send(struct pico_dhcp_client_cookie *cli, uint8_t msg_type)
+{
+  uint8_t buf_out[DHCPC_DATAGRAM_SIZE] = {0};
+  struct pico_dhcphdr *dh_out = (struct pico_dhcphdr *) buf_out;
+  int sent = 0;
+  int i = 0;
+  struct pico_ip4 destination;
+  uint16_t port = PICO_DHCPD_PORT;
+  if(cli->state == DHCPSTATE_BOUND || cli->state == DHCPSTATE_RENEWING){
+    destination.addr = cli->server_id.addr;
+  }else{
+    destination.addr = long_be(0xFFFFFFFF);
+  }
+
+  if(cli->device->eth == NULL){
+    pico_err = PICO_ERR_EOPNOTSUPP;
+    if(cli->cb != NULL){
+      cli->cb(cli, PICO_DHCP_ERROR);
+    }
+    return -1;
+  }
+  memcpy(dh_out->hwaddr, &cli->device->eth->mac, PICO_HLEN_ETHER);
+  dh_out->op = PICO_DHCP_OP_REQUEST;
+  dh_out->htype = PICO_HTYPE_ETHER;
+  dh_out->hlen = PICO_HLEN_ETHER;
+  dh_out->xid = cli->xid;
+  dh_out->secs = (msg_type == PICO_DHCP_MSG_REQUEST)?0:short_be((pico_tick - cli->start_time)/1000);
+  dh_out->dhcp_magic = PICO_DHCPD_MAGIC_COOKIE;
+  if (cli->state == DHCPSTATE_RENEWING)
+    dh_out->ciaddr = cli->address.addr;
+
+  /* Option: msg type, len 1 */
+  dh_out->options[i++] = PICO_DHCPOPT_MSGTYPE;
+  dh_out->options[i++] = 1;
+  dh_out->options[i++] = msg_type;
+
+  if (( msg_type == PICO_DHCP_MSG_REQUEST) && ( cli->state != DHCPSTATE_RENEWING )) {
+    dh_out->options[i++] = PICO_DHCPOPT_REQIP;
+    dh_out->options[i++] = 4;
+    dh_out->options[i++] = (long_be(cli->address.addr) & 0xFF000000) >> 24;
+    dh_out->options[i++] = (long_be(cli->address.addr) & 0xFF0000) >> 16;
+    dh_out->options[i++] = (long_be(cli->address.addr) & 0xFF00) >> 8;
+    dh_out->options[i++] = (long_be(cli->address.addr) & 0xFF);
+    dh_out->options[i++] = PICO_DHCPOPT_SERVERID;
+    dh_out->options[i++] = 4;
+    dh_out->options[i++] = (long_be(cli->server_id.addr) & 0xFF000000) >> 24;
+    dh_out->options[i++] = (long_be(cli->server_id.addr) & 0xFF0000) >> 16;
+    dh_out->options[i++] = (long_be(cli->server_id.addr) & 0xFF00) >> 8;
+    dh_out->options[i++] = (long_be(cli->server_id.addr) & 0xFF);
+  }
+
+  /* Option: req list, len 4 */
+  dh_out->options[i++] = PICO_DHCPOPT_PARMLIST;
+  dh_out->options[i++] = 7;
+  dh_out->options[i++] = PICO_DHCPOPT_NETMASK;
+  dh_out->options[i++] = PICO_DHCPOPT_BCAST;
+  dh_out->options[i++] = PICO_DHCPOPT_TIME;
+  dh_out->options[i++] = PICO_DHCPOPT_ROUTER;
+  dh_out->options[i++] = PICO_DHCPOPT_HOSTNAME;
+  dh_out->options[i++] = PICO_DHCPOPT_RENEWALTIME;
+  dh_out->options[i++] = PICO_DHCPOPT_REBINDINGTIME;
+
+  /* Option : max message size */
+  if( msg_type == PICO_DHCP_MSG_REQUEST || msg_type == PICO_DHCP_MSG_DISCOVER){
+    uint16_t dds = DHCPC_DATAGRAM_SIZE;
+    dh_out->options[i++] = PICO_DHCPOPT_MAXMSGSIZE;
+    dh_out->options[i++] = 2;
+    dh_out->options[i++] = (dds & 0xFF00) >> 8;
+    dh_out->options[i++] = (dds & 0xFF);
+  }
+
+
+
+  dh_out->options[i] = PICO_DHCPOPT_END;
+  sent = pico_socket_sendto(cli->socket, buf_out, DHCPC_DATAGRAM_SIZE, &destination, port);
+  if (sent < 0) {
+    dbg("DHCPC: sendto failed: %s\n", strerror(pico_err));
+    if(cli->cb != NULL)
+      cli->cb(cli, PICO_DHCP_ERROR);
+  }
+
+
+  //resend-timer :
+  if(cli->timer_param_retransmit != NULL)
+    cli->timer_param_retransmit->valid=0;
+
+  cli->timer_param_retransmit = pico_zalloc(sizeof(struct dhcp_timer_param));
+  if(!cli->timer_param_retransmit){
+    if(cli->cb != NULL)
+      pico_err = PICO_ERR_ENOMEM;
+      cli->cb(cli, PICO_DHCP_ERROR);
+    return -1;
+  }
+  cli->timer_param_retransmit->valid = 1;
+  cli->timer_param_retransmit->cli = cli;
+  cli->timer_param_retransmit->type = PICO_DHCP_EVENT_RETRANSMIT;
+  pico_timer_add(4000, dhcp_timer_cb, cli->timer_param_retransmit);
+
+  return 0;
+}
+
+//identifies type & does some preprocessing : checking if everything is valid
+static int pico_dhcp_verify_and_identify_type(uint8_t* data, int len, struct pico_dhcp_client_cookie *cli)
+{
+  struct pico_dhcphdr *dhdr = (struct pico_dhcphdr *) data;
+  uint8_t *nextopt, opt_data[20], opt_type;
+  int opt_len = 20;
+
+  if (dhdr->xid != cli->xid)
+    return 0;
+
+  if (!is_options_valid(dhdr->options, len - sizeof(struct pico_dhcphdr)))
+    return 0;
+
+  if( dhdr->dhcp_magic != PICO_DHCPD_MAGIC_COOKIE)
+    return 0;
+
+  opt_type = dhcp_get_next_option(dhdr->options, opt_data, &opt_len, &nextopt);
+  while (opt_type != PICO_DHCPOPT_END) {
+    /* parse interesting options here */
+    if (opt_type == PICO_DHCPOPT_MSGTYPE) {
+      return *opt_data;
+    }
+    opt_len = 20;
+    opt_type = dhcp_get_next_option(NULL, opt_data, &opt_len, &nextopt);
+  }
+  return 0;
+
+}
+
+static int init_cookie(struct pico_dhcp_client_cookie* cli)
+{
+  uint8_t n = 3;
+  uint16_t port = PICO_DHCP_CLIENT_PORT;
+  struct pico_ip4 address, netmask;
+
+  address.addr = long_be(0x00000000);
+  netmask.addr = long_be(0x00000000);
+
+  cli->state = DHCPSTATE_DISCOVER;
+  cli->start_time = pico_tick;
+  cli->attempt = 0;
+
+  cli->socket = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcp_wakeup);
+  if (!cli->socket) {
+    dbg("DHCPC: error opening socket: %s\n", strerror(pico_err));
+    if(cli->cb != NULL)
+      cli->cb(cli, PICO_DHCP_ERROR);
+    return -1;
+  }
+  if (pico_socket_bind(cli->socket, &address, &port) != 0){
+    dbg("DHCPC: error binding socket: %s\n", strerror(pico_err));
+    pico_socket_close(cli->socket);
+    if(cli->cb != NULL)
+      cli->cb(cli, PICO_DHCP_ERROR);
+    return -1;
+  }
+  cli->socket->dev = cli->device;
+
+  if(pico_ipv4_link_add(cli->device, address, netmask) != 0){
+    dbg("DHCPC: error adding link: %s\n", strerror(pico_err));
+    if(cli->cb != NULL)
+      cli->cb(cli, PICO_DHCP_ERROR);
+    return -1;
+  }
+
+  /* attempt to generate a correct xid 3 times, then fail */
+  do {
+    cli->xid = pico_rand();
+  } while (!cli->xid && --n);
+  if (!cli->xid) {
+    if(cli->cb != NULL)
+      cli->cb(cli, PICO_DHCP_ERROR);
+    return -1;
+  }
+
+  return 0;
+}
+
+static struct pico_dhcp_client_cookie *get_cookie_by_xid(uint32_t xid)
+{
+  struct pico_dhcp_client_cookie test = { }, *cookie = NULL;
+
+  if (!xid)
+    return NULL;
+
+  test.xid = xid;
+  cookie = pico_tree_findKey(&DHCPCookies, &test);
+  if (!cookie)
+    return NULL;
+  else
+    return cookie;
+}
+
+void *pico_dhcp_get_identifier(uint32_t xid)
+{
+  return (void *) get_cookie_by_xid(xid);
+}
+
+static uint32_t get_xid(uint8_t* data)
+{
+  struct pico_dhcphdr *dhdr = (struct pico_dhcphdr *) data;
+  return dhdr->xid;
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_dhcp_client.h	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,36 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+.
+
+*********************************************************************/
+#ifndef _INCLUDE_PICO_DHCP_CLIENT
+#define _INCLUDE_PICO_DHCP_CLIENT
+
+
+#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_nameserver(void* cli);
+
+/* possible codes for the callback */
+#define PICO_DHCP_SUCCESS 0
+#define PICO_DHCP_ERROR   1
+#define PICO_DHCP_RESET   2
+
+/* DHCP EVENT TYPE 
+ * these come after the message types, used for the state machine*/
+#define PICO_DHCP_EVENT_T1                   9
+#define PICO_DHCP_EVENT_T2                   10
+#define PICO_DHCP_EVENT_LEASE                11
+#define PICO_DHCP_EVENT_RETRANSMIT           12
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_dhcp_common.c	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,67 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+.
+
+Authors: Frederik Van Slycken
+*********************************************************************/
+
+#include "pico_config.h"
+#include "pico_stack.h"
+#include "pico_dhcp_common.h"
+
+#if defined (PICO_SUPPORT_DHCPC) || defined (PICO_SUPPORT_DHCPD)
+//this function should only be used after you checked if the options are valid! otherwise it could read from bad memory!
+uint8_t dhcp_get_next_option(uint8_t *begin, uint8_t *data, int *len, uint8_t **nextopt)
+{
+    uint8_t *p;
+    uint8_t type;
+    uint8_t opt_len;
+
+    if (!begin)
+        p = *nextopt;
+    else
+        p = begin;
+
+    type = *p;
+    *nextopt = ++p;
+    if ((type == PICO_DHCPOPT_END) || (type == PICO_DHCPOPT_PAD)) {
+        memset(data, 0, *len);
+        len = 0;
+        return type;
+    }
+    opt_len = *p;
+    p++;
+    if (*len > opt_len)
+        *len = opt_len;
+    memcpy(data, p, *len);
+    *nextopt = p + opt_len;
+    return type;
+}
+
+int is_options_valid(uint8_t *opt_buffer, int len)
+{
+    uint8_t *p = opt_buffer;
+    while (len > 0) {
+        if (*p == PICO_DHCPOPT_END)
+            return 1;
+        else if (*p == PICO_DHCPOPT_PAD) {
+            p++;
+            len--;
+        } else {
+            uint8_t opt_len;
+            p++;
+            len--;
+            if(len > 0) {
+                opt_len = *p;
+                p += opt_len + 1;
+                len -= opt_len;
+            }else
+                return 0;
+        }
+    }
+    return 0;
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_dhcp_common.h	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,102 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+.
+
+*********************************************************************/
+#ifndef _INCLUDE_PICO_DHCP_COMMON
+#define _INCLUDE_PICO_DHCP_COMMON
+
+
+#include <stdint.h>
+
+//minimum size is 576, cfr RFC
+#define DHCPC_DATAGRAM_SIZE 576
+#define DHCPD_DATAGRAM_SIZE 576
+
+
+#define PICO_DHCPD_PORT (short_be(67))
+#define PICO_DHCP_CLIENT_PORT (short_be(68))
+
+#define PICO_DHCP_OP_REQUEST 1
+#define PICO_DHCP_OP_REPLY   2
+
+#define PICO_HTYPE_ETHER 1
+#define PICO_HLEN_ETHER  6
+
+#define PICO_DHCPD_MAGIC_COOKIE (long_be(0x63825363))
+
+/* DHCP OPTIONS, RFC2132 */
+#define PICO_DHCPOPT_PAD                     0x00
+#define PICO_DHCPOPT_NETMASK                 0x01
+#define PICO_DHCPOPT_TIME                    0x02
+#define PICO_DHCPOPT_ROUTER                  0x03
+#define PICO_DHCPOPT_DNS                     0x06
+#define PICO_DHCPOPT_HOSTNAME                0x0c
+#define PICO_DHCPOPT_DOMAINNAME              0x0f
+#define PICO_DHCPOPT_MTU                     0x1a
+#define PICO_DHCPOPT_BCAST                   0x1c
+#define PICO_DHCPOPT_NETBIOSNS               0x2c
+#define PICO_DHCPOPT_NETBIOSSCOPE            0x2f
+
+#define PICO_DHCPOPT_REQIP                   0x32
+#define PICO_DHCPOPT_LEASETIME               0x33
+#define PICO_DHCPOPT_OPTIONOVERLOAD          0x34
+#define PICO_DHCPOPT_MSGTYPE                 0x35
+#define PICO_DHCPOPT_SERVERID                0x36
+#define PICO_DHCPOPT_PARMLIST                0x37
+#define PICO_DHCPOPT_MAXMSGSIZE              0x39
+#define PICO_DHCPOPT_RENEWALTIME             0x3a
+#define PICO_DHCPOPT_REBINDINGTIME           0x3b
+#define PICO_DHCPOPT_DOMAINSEARCH            0x77
+#define PICO_DHCPOPT_STATICROUTE             0x79
+#define PICO_DHCPOPT_END                     0xFF
+
+/* DHCP MESSAGE TYPE */
+#define PICO_DHCP_MSG_DISCOVER               1
+#define PICO_DHCP_MSG_OFFER                  2
+#define PICO_DHCP_MSG_REQUEST                3
+#define PICO_DHCP_MSG_DECLINE                4
+#define PICO_DHCP_MSG_ACK                    5
+#define PICO_DHCP_MSG_NAK                    6
+#define PICO_DHCP_MSG_RELEASE                7
+#define PICO_DHCP_MSG_INFORM                 8
+
+
+enum dhcp_negotiation_state {
+        DHCPSTATE_DISCOVER = 0,
+        DHCPSTATE_OFFER,
+        DHCPSTATE_REQUEST,
+        DHCPSTATE_BOUND,
+        DHCPSTATE_RENEWING
+};
+
+
+struct __attribute__((packed)) pico_dhcphdr
+{
+    uint8_t op;
+    uint8_t htype;
+    uint8_t hlen;
+    uint8_t hops; //zero
+    uint32_t xid; //store this in the request
+    uint16_t secs; // ignore
+    uint16_t flags;
+    uint32_t ciaddr; // client address - if asking for renewal
+    uint32_t yiaddr; // your address (client)
+    uint32_t siaddr; // dhcp offered address
+    uint32_t giaddr; // relay agent, bootp.
+    uint8_t hwaddr[6];
+    uint8_t hwaddr_padding[10];
+    char    hostname[64];
+    char    bootp_filename[128];
+    uint32_t dhcp_magic;
+    uint8_t options[0];
+};
+
+
+//common functions for client and server
+
+uint8_t dhcp_get_next_option(uint8_t *begin, uint8_t *data, int *len, uint8_t **nextopt);
+int is_options_valid(uint8_t *opt_buffer, int len); 
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_dhcp_server.c	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,339 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+
+Authors: Frederik Van Slycken, Kristof Roelants
+*********************************************************************/
+
+#ifdef PICO_SUPPORT_DHCPD
+
+#include "pico_dhcp_server.h"
+#include "pico_stack.h"
+#include "pico_config.h"
+#include "pico_addressing.h"
+#include "pico_socket.h"
+#include "pico_arp.h"
+#include <stdlib.h>
+
+# define dhcpd_dbg(...) do{}while(0)
+//# define dhcpd_dbg dbg
+
+#define dhcpd_make_offer(x) dhcpd_make_reply(x, PICO_DHCP_MSG_OFFER)
+#define dhcpd_make_ack(x) dhcpd_make_reply(x, PICO_DHCP_MSG_ACK)
+#define ip_inrange(x) ((long_be(x) >= long_be(dn->settings->pool_start)) && (long_be(x) <= long_be(dn->settings->pool_end)))
+
+static int dhcp_settings_cmp(void *ka, void *kb)
+{
+  struct pico_dhcpd_settings *a = ka, *b = kb;
+  if (a->dev < b->dev)
+    return -1; 
+  else if (a->dev > b->dev)
+    return 1;
+  else
+    return 0;
+} 
+PICO_TREE_DECLARE(DHCPSettings, dhcp_settings_cmp);
+
+static int dhcp_negotiations_cmp(void *ka, void *kb)
+{
+  struct pico_dhcp_negotiation *a = ka, *b = kb;
+  if (a->xid < b->xid)
+    return -1; 
+  else if (a->xid > b->xid)
+    return 1;
+  else
+    return 0;
+} 
+PICO_TREE_DECLARE(DHCPNegotiations, dhcp_negotiations_cmp);
+
+static struct pico_dhcp_negotiation *get_negotiation_by_xid(uint32_t xid)
+{
+  struct pico_dhcp_negotiation test = { }, *neg = NULL;
+
+  test.xid = xid;
+  neg = pico_tree_findKey(&DHCPNegotiations, &test);
+  if (!neg)
+    return NULL;
+  else
+    return neg;
+}
+
+static void dhcpd_make_reply(struct pico_dhcp_negotiation *dn, uint8_t reply_type)
+{
+  uint8_t buf_out[DHCPD_DATAGRAM_SIZE] = {0};
+  struct pico_dhcphdr *dh_out = (struct pico_dhcphdr *) buf_out;
+  struct pico_ip4 destination = { };
+  uint32_t bcast = dn->settings->my_ip.addr | ~(dn->settings->netmask.addr);
+  uint32_t dns_server = OPENDNS;
+  uint16_t port = PICO_DHCP_CLIENT_PORT;
+  int sent = 0;
+
+  memcpy(dh_out->hwaddr, dn->eth.addr, PICO_HLEN_ETHER);
+  dh_out->op = PICO_DHCP_OP_REPLY;
+  dh_out->htype = PICO_HTYPE_ETHER;
+  dh_out->hlen = PICO_HLEN_ETHER;
+  dh_out->xid = dn->xid;
+  dh_out->yiaddr = dn->ipv4.addr;
+  dh_out->siaddr = dn->settings->my_ip.addr;
+  dh_out->dhcp_magic = PICO_DHCPD_MAGIC_COOKIE;
+
+  /* Option: msg type, len 1 */
+  dh_out->options[0] = PICO_DHCPOPT_MSGTYPE;
+  dh_out->options[1] = 1;
+  dh_out->options[2] = reply_type;
+
+  /* Option: server id, len 4 */
+  dh_out->options[3] = PICO_DHCPOPT_SERVERID;
+  dh_out->options[4] = 4;
+  memcpy(dh_out->options + 5, &dn->settings->my_ip.addr, 4);
+
+  /* Option: Lease time, len 4 */
+  dh_out->options[9] = PICO_DHCPOPT_LEASETIME;
+  dh_out->options[10] = 4;
+  memcpy(dh_out->options + 11, &dn->settings->lease_time, 4);
+
+  /* Option: Netmask, len 4 */
+  dh_out->options[15] = PICO_DHCPOPT_NETMASK;
+  dh_out->options[16] = 4;
+  memcpy(dh_out->options + 17, &dn->settings->netmask.addr, 4);
+
+  /* Option: Router, len 4 */
+  dh_out->options[21] = PICO_DHCPOPT_ROUTER;
+  dh_out->options[22] = 4;
+  memcpy(dh_out->options + 23, &dn->settings->my_ip.addr, 4);
+
+  /* Option: Broadcast, len 4 */
+  dh_out->options[27] = PICO_DHCPOPT_BCAST;
+  dh_out->options[28] = 4;
+  memcpy(dh_out->options + 29, &bcast, 4);
+
+  /* Option: DNS, len 4 */
+  dh_out->options[33] = PICO_DHCPOPT_DNS;
+  dh_out->options[34] = 4;
+  memcpy(dh_out->options + 35, &dns_server, 4);
+
+  dh_out->options[40] = PICO_DHCPOPT_END;
+
+  destination.addr = dh_out->yiaddr;
+
+  sent = pico_socket_sendto(dn->settings->s, buf_out, DHCPD_DATAGRAM_SIZE, &destination, port);
+  if (sent < 0) {
+    dhcpd_dbg("DHCPD: sendto failed with code %d!\n", pico_err);
+  }
+}
+
+static void dhcp_recv(struct pico_socket *s, uint8_t *buffer, int len)
+{
+  struct pico_dhcphdr *dhdr = (struct pico_dhcphdr *) buffer;
+  struct pico_dhcp_negotiation *dn = get_negotiation_by_xid(dhdr->xid);
+  struct pico_ip4* ipv4 = NULL;
+  struct pico_dhcpd_settings test, *settings = NULL;
+  uint8_t *nextopt, opt_data[20], opt_type;
+  int opt_len = 20;
+  uint8_t msg_type;
+  uint32_t msg_reqIP = 0;
+  uint32_t msg_servID = 0;
+
+  if (!is_options_valid(dhdr->options, len - sizeof(struct pico_dhcphdr))) {
+    dhcpd_dbg("DHCPD WARNING: invalid options in dhcp message\n");
+    return;
+  }
+
+  if (!dn) {
+    dn = pico_zalloc(sizeof(struct pico_dhcp_negotiation));
+    if (!dn) {
+      pico_err = PICO_ERR_ENOMEM;
+      return;
+    }
+    dn->xid = dhdr->xid;
+    dn->state = DHCPSTATE_DISCOVER;
+    memcpy(dn->eth.addr, dhdr->hwaddr, PICO_HLEN_ETHER);
+
+    test.dev = pico_ipv4_link_find(&s->local_addr.ip4);
+    settings = pico_tree_findKey(&DHCPSettings, &test);
+    if (settings) {
+      dn->settings = settings;
+    } else {
+      dhcpd_dbg("DHCPD WARNING: received DHCP message on unconfigured link %s\n", test.dev->name);
+      pico_free(dn);
+      return;
+    }
+
+    ipv4 = pico_arp_reverse_lookup(&dn->eth);
+    if (!ipv4) {
+      dn->ipv4.addr = settings->pool_next;
+      pico_arp_create_entry(dn->eth.addr, dn->ipv4, settings->dev);
+      settings->pool_next = long_be(long_be(settings->pool_next) + 1);
+    } else {
+      dn->ipv4.addr = ipv4->addr;
+    }
+
+    if (pico_tree_insert(&DHCPNegotiations, dn)) {
+      dhcpd_dbg("DHCPD WARNING: tried creating new negotation for existing xid %u\n", dn->xid);
+      pico_free(dn);
+      return; /* Element key already exists */
+    }
+  }
+ 
+  if (!ip_inrange(dn->ipv4.addr))
+    return;
+
+  opt_type = dhcp_get_next_option(dhdr->options, opt_data, &opt_len, &nextopt);
+  while (opt_type != PICO_DHCPOPT_END) {
+    /* parse interesting options here */
+      //dhcpd_dbg("DHCPD sever: opt_type %x,  opt_data[0]%d\n", opt_type, opt_data[0]);
+    switch(opt_type){
+      case PICO_DHCPOPT_MSGTYPE:
+        msg_type = opt_data[0];
+        break;
+      case PICO_DHCPOPT_REQIP:
+        //dhcpd_dbg("DHCPD sever: opt_type %x,  opt_len%d\n", opt_type, opt_len);
+        if( opt_len == 4)
+        {
+          msg_reqIP =  ( opt_data[0] << 24 );
+          msg_reqIP |= ( opt_data[1] << 16 );
+          msg_reqIP |= ( opt_data[2] << 8  );
+          msg_reqIP |= ( opt_data[3]       );
+         //dhcpd_dbg("DHCPD sever: msg_reqIP %x, opt_data[0] %x,[1] %x,[2] %x,[3] %x\n", msg_reqIP, opt_data[0],opt_data[1],opt_data[2],opt_data[3]);
+        };
+        break;
+      case PICO_DHCPOPT_SERVERID:
+        //dhcpd_dbg("DHCPD sever: opt_type %x,  opt_len%d\n", opt_type, opt_len);
+        if( opt_len == 4)
+        {
+          msg_servID =  ( opt_data[0] << 24 );
+          msg_servID |= ( opt_data[1] << 16 );
+          msg_servID |= ( opt_data[2] << 8  );
+          msg_servID |= ( opt_data[3]       );
+          //dhcpd_dbg("DHCPD sever: msg_servID %x, opt_data[0] %x,[1] %x,[2] %x,[3] %x\n", msg_servID, opt_data[0],opt_data[1],opt_data[2],opt_data[3]);
+        };
+        break;        
+      default:
+        break;
+    }
+        
+    opt_len = 20;
+    opt_type = dhcp_get_next_option(NULL, opt_data, &opt_len, &nextopt);
+  }
+    
+  //dhcpd_dbg("DHCPD sever: msg_type %d, dn->state %d\n", msg_type, dn->state);
+  //dhcpd_dbg("DHCPD sever: msg_reqIP %x, dn->msg_servID %x\n", msg_reqIP, msg_servID);
+  //dhcpd_dbg("DHCPD sever: dhdr->ciaddr %x, dhdr->yiaddr %x, dn->ipv4.addr %x\n", dhdr->ciaddr,dhdr->yiaddr,dn->ipv4.addr);
+
+  if (msg_type == PICO_DHCP_MSG_DISCOVER)
+  {
+    dhcpd_make_offer(dn);
+    dn->state = DHCPSTATE_OFFER;
+    return;
+  }
+  else if ((msg_type == PICO_DHCP_MSG_REQUEST)&&( dn->state == DHCPSTATE_OFFER))
+  {
+    dhcpd_make_ack(dn);
+    dn->state = DHCPSTATE_BOUND;
+    return;
+  }
+  else if ((msg_type == PICO_DHCP_MSG_REQUEST)&&( dn->state == DHCPSTATE_BOUND))
+  {
+    if( ( msg_servID == 0 )
+      &&( msg_reqIP == 0 )
+      &&( dhdr->ciaddr == dn->ipv4.addr)
+      )
+    { 
+      dhcpd_make_ack(dn);
+      return;
+    }
+  }  
+}
+
+static void pico_dhcpd_wakeup(uint16_t ev, struct pico_socket *s)
+{
+  uint8_t buf[DHCPD_DATAGRAM_SIZE] = { };
+  int r = 0;
+  uint32_t peer = 0;
+  uint16_t port = 0;
+
+  dhcpd_dbg("DHCPD: called dhcpd_wakeup\n");
+  if (ev == PICO_SOCK_EV_RD) {
+    do {
+      r = pico_socket_recvfrom(s, buf, DHCPD_DATAGRAM_SIZE, &peer, &port);
+      if (r > 0 && port == PICO_DHCP_CLIENT_PORT) {
+        dhcp_recv(s, buf, r);
+      }
+    } while(r>0);
+  }
+}
+
+int pico_dhcp_server_initiate(struct pico_dhcpd_settings *setting)
+{
+  struct pico_dhcpd_settings *settings = NULL;
+  struct pico_ipv4_link *link = NULL;
+  uint16_t port = PICO_DHCPD_PORT;
+
+  if (!setting) {
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+
+  if (!setting->my_ip.addr) {
+    pico_err = PICO_ERR_EINVAL;
+    dhcpd_dbg("DHCPD: IP address of interface was not supplied\n");
+    return -1;
+  }
+
+  link = pico_ipv4_link_get(&setting->my_ip);
+  if (!link) {
+    pico_err = PICO_ERR_EINVAL;
+    dhcpd_dbg("DHCPD: no link with IP %X found\n", setting->my_ip.addr);
+    return -1;
+  }
+
+  settings = pico_zalloc(sizeof(struct pico_dhcpd_settings));
+  if (!settings) {
+    pico_err = PICO_ERR_ENOMEM;
+    return -1;
+  }
+  memcpy(settings, setting, sizeof(struct pico_dhcpd_settings));
+
+  settings->dev = link->dev;
+  dhcpd_dbg("DHCPD: configuring DHCP server for link %s\n", link->dev->name);
+  settings->my_ip.addr = link->address.addr;
+  dhcpd_dbg("DHCPD: using server addr %X\n", long_be(settings->my_ip.addr));
+  settings->netmask.addr = link->netmask.addr;
+  dhcpd_dbg("DHCPD: using netmask %X\n", long_be(settings->netmask.addr));
+
+  /* default values if not provided */
+  if (settings->pool_start == 0)
+    settings->pool_start = (settings->my_ip.addr & settings->netmask.addr) | POOL_START;
+  dhcpd_dbg("DHCPD: using pool_start %X\n", long_be(settings->pool_start));
+  if (settings->pool_end == 0)
+    settings->pool_end = (settings->my_ip.addr & settings->netmask.addr) | POOL_END;
+  dhcpd_dbg("DHCPD: using pool_end %x\n", long_be(settings->pool_end));
+  if (settings->lease_time == 0)
+    settings->lease_time = LEASE_TIME;
+  dhcpd_dbg("DHCPD: using lease time %x\n", long_be(settings->lease_time));
+  settings->pool_next = settings->pool_start;
+
+  settings->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcpd_wakeup);
+  if (!settings->s) {
+    dhcpd_dbg("DHCP: could not open client socket\n");
+    pico_free(settings);
+    return -1;
+  }
+  if (pico_socket_bind(settings->s, &settings->my_ip, &port) != 0) {
+    dhcpd_dbg("DHCP: could not bind server socket (%s)\n", strerror(pico_err));
+    pico_free(settings);
+    return -1;
+  }
+  
+  if (pico_tree_insert(&DHCPSettings, settings)) {
+    dhcpd_dbg("DHCPD ERROR: link %s already configured\n", link->dev->name);
+    pico_err = PICO_ERR_EINVAL;
+    pico_free(settings);
+    return -1; /* Element key already exists */
+  }
+  dhcpd_dbg("DHCPD: configured DHCP server for link %s\n", link->dev->name);
+
+  return 0;
+}
+#endif /* PICO_SUPPORT_DHCP */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_dhcp_server.h	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,43 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+*********************************************************************/
+#ifndef _INCLUDE_PICO_DHCP_SERVER
+#define _INCLUDE_PICO_DHCP_SERVER
+
+#include "pico_dhcp_common.h"
+#include "pico_addressing.h"
+
+/* default configuration */ 
+#define OPENDNS (long_be(0xd043dede)) /* OpenDNS DNS server 208.67.222.222 */
+#define POOL_START long_be(0x00000064)
+#define POOL_END long_be(0x000000fe)
+#define LEASE_TIME long_be(0x00000078)
+
+struct pico_dhcpd_settings
+{
+  struct pico_device *dev;
+  struct pico_socket *s;
+  struct pico_ip4 my_ip;
+  struct pico_ip4 netmask;
+  uint32_t pool_start;
+  uint32_t pool_next;
+  uint32_t pool_end;
+  uint32_t lease_time;
+  uint8_t flags; /* unused atm */
+};
+
+struct pico_dhcp_negotiation {
+  struct pico_dhcpd_settings *settings;
+  struct pico_ip4 ipv4;
+  struct pico_eth eth;
+  enum dhcp_negotiation_state state;
+  uint32_t xid;
+  uint32_t assigned_address;
+};
+
+/* required settings field: IP address of the interface to serve, only IPs of this network will be served. */
+int pico_dhcp_server_initiate(struct pico_dhcpd_settings *setting);
+
+#endif /* _INCLUDE_PICO_DHCP_SERVER */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_dns_client.c	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,767 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+.
+  
+Authors: Kristof Roelants
+*********************************************************************/
+#include "pico_config.h"
+#include "pico_stack.h"
+#include "pico_addressing.h"
+#include "pico_socket.h"
+#include "pico_ipv4.h"
+#include "pico_dns_client.h"
+#include "pico_tree.h"
+
+#ifdef PICO_SUPPORT_DNS_CLIENT
+
+#define dns_dbg(...) do{}while(0)
+//#define dns_dbg dbg
+
+/* DNS response length */
+#define PICO_DNS_MAX_RESPONSE_LEN 256
+
+/* DNS client retransmission time (msec) + frequency */
+#define PICO_DNS_CLIENT_RETRANS 4000
+#define PICO_DNS_CLIENT_MAX_RETRANS 3
+
+/* Default nameservers */
+#define PICO_DNS_NS_GOOGLE "8.8.8.8"
+
+/* Nameserver port */
+#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_PTR 12
+
+/* QCLASS values */
+#define PICO_DNS_CLASS_IN 1
+
+/* Compression values */
+#define PICO_DNS_LABEL 0
+#define PICO_DNS_POINTER 3
+
+/* TTL values */
+#define PICO_DNS_MAX_TTL 604800 /* one week */
+
+/* Header flags */
+#define FLAG_QR(hdr, x) ((hdr)->flags = ((hdr)->flags & ~(0x1 << 15)) | (x << 15)) 
+#define FLAG_OPCODE(hdr, x) ((hdr)->flags = ((hdr)->flags & ~(0xF << 11)) | (x << 11)) 
+#define FLAG_AA(hdr, x) ((hdr)->flags = ((hdr)->flags & ~(0x1 << 10)) | (x << 10)) 
+#define FLAG_TC(hdr, x) ((hdr)->flags = ((hdr)->flags & ~(0x1 << 9)) | (x << 9)) 
+#define FLAG_RD(hdr, x) ((hdr)->flags = ((hdr)->flags & ~(0x1 << 8)) | (x << 8)) 
+#define FLAG_RA(hdr, x) ((hdr)->flags = ((hdr)->flags & ~(0x1 << 7)) | (x << 7)) 
+#define FLAG_Z(hdr, x) ((hdr)->flags = ((hdr)->flags & ~(0x7 << 4)) | (x << 4)) 
+#define FLAG_RCODE(hdr, x) ((hdr)->flags = ((hdr)->flags & ~(0xF)) | x) 
+
+#define GET_FLAG_QR(hdr) ((((hdr)->flags) & (1 << 15)) != 0) 
+#define GET_FLAG_OPCODE(hdr) ((((hdr)->flags) & (0xF << 11)) >> 11) 
+#define GET_FLAG_AA(hdr) ((((hdr)->flags) & (1 << 10)) != 0) 
+#define GET_FLAG_TC(hdr) ((((hdr)->flags) & (1 << 9)) != 0) 
+#define GET_FLAG_RD(hdr) ((((hdr)->flags) & (1 << 8)) != 0) 
+#define GET_FLAG_RA(hdr) ((((hdr)->flags) & (1 << 7)) != 0) 
+#define GET_FLAG_Z(hdr) ((((hdr)->flags) & (0x7 << 4)) >> 4) 
+#define GET_FLAG_RCODE(hdr) (((hdr)->flags) & (0x0F)) 
+
+/* RFC 1025 section 4. MESSAGES */
+struct __attribute__((packed)) dns_message_hdr
+{
+  uint16_t id;
+  uint16_t flags;
+  uint16_t qdcount;
+  uint16_t ancount;
+  uint16_t nscount;
+  uint16_t arcount;
+};
+
+struct __attribute__((packed)) dns_query_suffix
+{
+  /* NAME - domain name to which this resource record pertains */
+  uint16_t qtype;
+  uint16_t qclass;
+};
+
+struct __attribute__((packed)) dns_answer_suffix
+{
+  /* NAME - domain name to which this resource record pertains */
+  uint16_t qtype;
+  uint16_t qclass;
+  uint32_t ttl;
+  uint16_t rdlength;
+  /* RDATA - variable length string of octets that describes the resource */
+};
+
+struct pico_dns_ns
+{
+  struct pico_ip4 ns; /* Nameserver */
+};
+
+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 -1; 
+  else if (a->ns.addr > b->ns.addr)
+    return 1;
+  else
+    return 0;
+} 
+    
+PICO_TREE_DECLARE(NSTable,dns_ns_cmp);
+
+int pico_dns_client_nameserver(struct pico_ip4 *ns, uint8_t flag)
+{
+  struct pico_dns_ns test, *key = NULL;
+
+  if (!ns) {
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+
+  switch (flag)
+  {
+    case PICO_DNS_NS_ADD:
+      key = pico_zalloc(sizeof(struct pico_dns_ns));
+      if (!key) {
+        pico_err = PICO_ERR_ENOMEM;
+        return -1;
+      }
+      key->ns = *ns;
+
+      if(pico_tree_insert(&NSTable,key)){
+        dns_dbg("DNS WARNING: nameserver %08X already added\n",ns->addr);
+        pico_err = PICO_ERR_EINVAL;
+        pico_free(key);
+        return -1; /* Element key already exists */
+      }
+      dns_dbg("DNS: nameserver %08X added\n", ns->addr);
+      /* If default NS found, remove it */
+      pico_string_to_ipv4(PICO_DNS_NS_GOOGLE, &test.ns.addr);
+      if (ns->addr != test.ns.addr) {
+
+          key = pico_tree_findKey(&NSTable,&test);
+        if (key) {
+            if(pico_tree_delete(&NSTable,key)) {
+            dns_dbg("DNS: default nameserver %08X removed\n", test.ns.addr);
+            pico_free(key);
+          } else {
+            pico_err = PICO_ERR_EAGAIN;
+            return -1;
+          }
+        }
+      }
+      break;
+
+    case PICO_DNS_NS_DEL:
+      test.ns = *ns;
+
+      key = pico_tree_findKey(&NSTable,&test);
+      if (!key) {
+        dns_dbg("DNS WARNING: nameserver %08X not found\n", ns->addr);
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+      }
+      /* RB_REMOVE returns pointer to removed element, NULL to indicate error */
+
+            if(pico_tree_delete(&NSTable,key)) {
+        dns_dbg("DNS: nameserver %08X removed\n",key->ns.addr);
+        pico_free(key);
+      } else {
+        pico_err = PICO_ERR_EAGAIN;
+        return -1;
+      }
+      /* If no NS left, add default NS */
+      if(pico_tree_first(&NSTable) == NULL){
+        dns_dbg("DNS: add default nameserver\n");
+        return pico_dns_client_init();
+      }
+      break;
+
+    default:
+      pico_err = PICO_ERR_EINVAL;
+      return -1;
+  }
+  return 0;
+}
+
+int pico_dns_client_init()
+{
+  struct pico_ip4 default_ns;
+  if (pico_string_to_ipv4(PICO_DNS_NS_GOOGLE, &default_ns.addr) != 0) {
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+  return pico_dns_client_nameserver(&default_ns, PICO_DNS_NS_ADD);
+}
+
+struct pico_dns_key
+{
+  char *q_hdr;
+  uint16_t len;
+  uint16_t id;
+  uint16_t qtype;
+  uint16_t qclass;
+  uint8_t retrans;
+  struct pico_dns_ns q_ns;
+  struct pico_socket *s;
+  void (*callback)(char *, void *);
+  void *arg;
+};
+
+static int dns_cmp(void *ka, void *kb)
+{
+    struct pico_dns_key *a = ka,*b = kb;
+  if (a->id < b->id)
+    return -1; 
+  else if (a->id > b->id)
+    return 1;
+  else
+    return 0;
+} 
+    
+PICO_TREE_DECLARE(DNSTable,dns_cmp);
+
+static int pico_dns_client_strlen(const char *url)
+{
+  uint16_t len = 0;
+  int p;
+
+  if (!url)
+    return -1;
+
+  while ((p = *url++) != 0) {
+    len++;
+  }
+  return len;
+}
+
+/* Replace '.' by the label length */
+static int pico_dns_client_label(char *ptr)
+{
+  char *l;
+  uint8_t lbl_len = 0;
+  int p;
+
+  if (!ptr)
+    return -1;
+
+  l = ptr++;
+  while ((p = *ptr++) != 0){
+    if (p == '.') {
+      *l = lbl_len;
+      l = ptr - 1;
+      lbl_len = 0;
+    } else {
+      lbl_len++;
+    }
+  }
+  *l = lbl_len;
+  return 0;
+}
+
+/* Replace the label length by '.' */
+static int pico_dns_client_reverse_label(char *ptr)
+{
+  char *l;
+  int p;
+
+  if(!ptr)
+    return -1;
+
+  l = ptr;
+  while ((p = *ptr++) != 0){
+    ptr += p;
+    *l = '.';
+    l = ptr;
+  }
+  return 0;
+}
+
+/* Seek the end of a string */
+static char *pico_dns_client_seek(char *ptr)
+{
+  int p;
+
+  if (!ptr)
+    return NULL;
+
+  while ((p = *ptr++) != 0);
+
+  return ptr++;
+}
+
+static inline void pico_dns_client_construct_hdr(struct dns_message_hdr *hdr, uint16_t id)
+{
+  hdr->id = short_be(id);
+  FLAG_QR(hdr, PICO_DNS_QR_QUERY); 
+  FLAG_OPCODE(hdr, PICO_DNS_OPCODE_QUERY); 
+  FLAG_AA(hdr, PICO_DNS_AA_NO_AUTHORITY); 
+  FLAG_TC(hdr, PICO_DNS_TC_NO_TRUNCATION); 
+  FLAG_RD(hdr, PICO_DNS_RD_IS_DESIRED); 
+  FLAG_RA(hdr, PICO_DNS_RA_NO_SUPPORT); 
+  FLAG_Z(hdr, 0); 
+  FLAG_RCODE(hdr, PICO_DNS_RCODE_NO_ERROR); 
+  hdr->flags = short_be(hdr->flags);
+  hdr->qdcount = short_be(1);
+  hdr->ancount = short_be(0);
+  hdr->nscount = short_be(0);
+  hdr->arcount = short_be(0);
+}
+
+static inline void pico_dns_client_hdr_ntoh(struct dns_message_hdr *hdr)
+{
+  hdr->id = short_be(hdr->id);
+  hdr->flags = short_be(hdr->flags);
+  hdr->qdcount = short_be(hdr->qdcount);
+  hdr->ancount = short_be(hdr->ancount);
+  hdr->nscount = short_be(hdr->nscount);
+  hdr->arcount = short_be(hdr->arcount);
+}
+
+
+static int pico_dns_client_mirror(char *ptr)
+{
+  unsigned char buf[4] = {0};
+  char *m;
+  int cnt = 0;
+  int p, i;
+
+  if (!ptr)
+    return -1;
+
+  m = ptr;
+  while ((p = *ptr++) != 0)
+  {
+    if (pico_is_digit(p)) {
+      buf[cnt] = (10 * buf[cnt]) + (p - '0');
+    } else if (p == '.') {
+        cnt++;
+    } else {
+      return -1;
+    }
+  }
+
+  /* Handle short notation */
+  if(cnt == 1){
+    buf[3] = buf[1];
+    buf[1] = 0;
+    buf[2] = 0;
+  }else if (cnt == 2){
+    buf[3] = buf[2];
+    buf[2] = 0;
+  }else if(cnt != 3){
+    /* String could not be parsed, return error */
+    return -1;
+  }
+
+  ptr = m;
+  for(i = 3; i >= 0; i--)
+  {
+    if(buf[i] > 99){
+      *ptr++ = '0' + (buf[i] / 100);
+      *ptr++ = '0' + ((buf[i] % 100) / 10);
+      *ptr++ = '0' + ((buf[i] % 100) % 10);
+    }else if(buf[i] > 9){
+      *ptr++ = '0' + (buf[i] / 10);
+      *ptr++ = '0' + (buf[i] % 10);
+    }else{
+      *ptr++ = '0' + buf[i];
+    }
+    if(i > 0)
+      *ptr++ = '.';
+  }
+
+  return 0;
+}
+
+static struct pico_dns_key *pico_dns_client_idcheck(uint16_t id)
+{
+  struct pico_dns_key test;
+
+  test.id = id;
+  return pico_tree_findKey(&DNSTable,&test);
+}
+
+static void pico_dns_client_callback(uint16_t ev, struct pico_socket *s);
+
+static int pico_dns_client_send(struct pico_dns_key *key)
+{
+  struct pico_socket *s;
+  int w = 0;
+
+  dns_dbg("DNS: sending query to %08X\n", key->q_ns.ns.addr);
+  s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dns_client_callback);
+  if (!s)
+    return -1; 
+  key->s = s;
+  if (pico_socket_connect(s, &key->q_ns.ns, short_be(PICO_DNS_NS_PORT)) != 0)
+    return -1;
+  w = pico_socket_send(s, key->q_hdr, key->len);
+  if (w <= 0)
+    return -1;
+
+  return 0;
+}
+
+static void pico_dns_client_retransmission(unsigned long now, void *arg)
+{
+  struct pico_dns_key *key = (struct pico_dns_key *)arg;
+  struct pico_dns_ns *q_ns = NULL;
+
+  if (!key->retrans) {
+    dns_dbg("DNS: no retransmission!\n");
+    pico_free(key->q_hdr);
+
+    if(pico_tree_delete(&DNSTable,key))
+      pico_free(key);
+  }
+  else if (key->retrans <= PICO_DNS_CLIENT_MAX_RETRANS) {
+    key->retrans++;
+    dns_dbg("DNS: retransmission! (%u attempts)\n", key->retrans);
+        // ugly hack
+    q_ns = pico_tree_next(pico_tree_findNode(&NSTable,&key->q_ns))->keyValue;
+    if (q_ns)
+      key->q_ns = *q_ns; 
+    else
+        key->q_ns = *((struct pico_dns_ns *)pico_tree_first(&NSTable));
+    pico_dns_client_send(key);
+    pico_timer_add(PICO_DNS_CLIENT_RETRANS, pico_dns_client_retransmission, key);
+  } else {
+    dns_dbg("DNS ERROR: no reply from nameservers! (%u attempts)\n", key->retrans);
+    pico_socket_close(key->s);
+    pico_err = PICO_ERR_EIO;
+    key->callback(NULL, key->arg);
+    pico_free(key->q_hdr);
+    /* RB_REMOVE returns pointer to removed element, NULL to indicate error */
+
+    if(pico_tree_delete(&DNSTable,key))
+      pico_free(key);
+  }
+}
+
+static void pico_dns_client_callback(uint16_t ev, struct pico_socket *s)
+{
+  char *q_qname, *q_suf, *a_hdr, *a_qname, *a_suf, *a_rdata;
+  struct dns_message_hdr *hdr;
+  struct dns_query_suffix query_suf;
+  struct dns_answer_suffix answer_suf;
+  struct pico_dns_key test, *key;
+  char *answer;
+  char dns_answer[PICO_DNS_MAX_RESPONSE_LEN] = {0};
+  uint8_t valid_suffix = 0;
+  uint16_t compression = 0;
+  int i = 0, r = 0;
+
+  if (ev & PICO_SOCK_EV_RD) {
+    r = pico_socket_read(s, dns_answer, PICO_DNS_MAX_RESPONSE_LEN);
+    pico_socket_close(s);
+    if (r == PICO_DNS_MAX_RESPONSE_LEN || r < (int)sizeof(struct dns_message_hdr)) {
+      dns_dbg("DNS ERROR: received incorrect number(%d) of bytes\n", r);
+      return;
+    }
+
+    /* Check header validity */
+    a_hdr = dns_answer;
+    hdr = (struct dns_message_hdr *) a_hdr;
+    pico_dns_client_hdr_ntoh(hdr);
+    if (GET_FLAG_QR(hdr) != PICO_DNS_QR_RESPONSE || GET_FLAG_OPCODE(hdr) != PICO_DNS_OPCODE_QUERY 
+        || GET_FLAG_TC(hdr) == PICO_DNS_TC_IS_TRUNCATED || GET_FLAG_RCODE(hdr) != PICO_DNS_RCODE_NO_ERROR) {
+      dns_dbg("DNS ERROR: OPCODE %d | TC %d | RCODE %d\n", GET_FLAG_OPCODE(hdr), GET_FLAG_TC(hdr), GET_FLAG_RCODE(hdr));
+      return;
+    }
+
+    if (hdr->ancount < 1 || r < (int)(sizeof(struct dns_message_hdr) + hdr->qdcount * sizeof(struct dns_query_suffix)
+            + hdr->ancount * sizeof(struct dns_answer_suffix))) {
+      dns_dbg("DNS ERROR: ancount < 1 OR received number(%d) of bytes too low\n", r);
+      return;
+    }
+
+    /* Find DNS key */
+    test.id = hdr->id;
+
+    key = pico_tree_findKey(&DNSTable,&test);
+    if (!key) {
+      dns_dbg("DNS WARNING: key with id %u not found\n", hdr->id);
+      return;
+    }
+    key->retrans = 0;
+
+    /* Check query suffix validity */
+    q_qname = a_hdr + sizeof(struct dns_message_hdr);
+    q_suf = pico_dns_client_seek(q_qname);
+    query_suf = *(struct dns_query_suffix *) q_suf;
+    if (short_be(query_suf.qtype) != key->qtype || short_be(query_suf.qclass) != key->qclass) {
+      dns_dbg("DNS ERROR: received qtype (%u) or qclass (%u) incorrect\n", short_be(query_suf.qtype), short_be(query_suf.qclass));
+      return;
+    }
+
+    /* Seek answer suffix */
+    a_qname = q_suf + sizeof(struct dns_query_suffix);
+    a_suf = a_qname;
+    while(i++ < hdr->ancount) {
+      uint16_t comp_h = short_from(a_suf);
+      compression = short_be(comp_h);
+      switch (compression >> 14)
+      {
+        case PICO_DNS_POINTER:
+          while (compression >> 14 == PICO_DNS_POINTER) {
+            dns_dbg("DNS: pointer\n");
+            a_suf += sizeof(uint16_t);
+            comp_h = short_from(a_suf);
+            compression = short_be(comp_h);
+          }
+          break;
+
+        case PICO_DNS_LABEL:
+          dns_dbg("DNS: label\n");
+          a_suf = pico_dns_client_seek(a_qname);
+          break;
+
+        default:
+          dns_dbg("DNS ERROR: incorrect compression (%u) value\n", compression);
+          return;
+      }
+
+      /* Check answer suffix validity */
+      answer_suf = *(struct dns_answer_suffix *)a_suf;
+      if (short_be(answer_suf.qtype) != key->qtype || short_be(answer_suf.qclass) != key->qclass) {
+        dns_dbg("DNS WARNING: received qtype (%u) or qclass (%u) incorrect\n", short_be(answer_suf.qtype), short_be(answer_suf.qclass));
+        a_suf = a_suf + sizeof(struct dns_answer_suffix) + short_be(answer_suf.rdlength);
+        continue;
+      }
+
+      if (short_be(answer_suf.ttl) > PICO_DNS_MAX_TTL) {
+        dns_dbg("DNS WARNING: received TTL (%u) > MAX (%u)\n", short_be(answer_suf.ttl), PICO_DNS_MAX_TTL);
+        a_suf = a_suf + sizeof(struct dns_answer_suffix) + short_be(answer_suf.rdlength);
+        continue;
+      }
+
+      valid_suffix = 1;
+      break;
+    }
+
+    if (!valid_suffix) {
+       dns_dbg("DNS ERROR: invalid dns answer suffix\n");
+       return;
+    }
+
+    a_rdata = a_suf + sizeof(struct dns_answer_suffix);
+    if (key->qtype == PICO_DNS_TYPE_A) {
+      uint32_t ip_h = long_from(a_rdata);
+      dns_dbg("DNS: length %u | ip %08X\n", short_be(answer_suf.rdlength), long_be(ip_h));
+      answer = pico_zalloc(16);
+      pico_ipv4_to_string(answer, ip_h);
+      key->callback(answer, key->arg);
+    } else if (key->qtype == PICO_DNS_TYPE_PTR) {
+      pico_dns_client_reverse_label((char *) a_rdata);
+      dns_dbg("DNS: length %u | name %s\n", short_be(answer_suf.rdlength), (char *)a_rdata + 1);
+      answer = pico_zalloc(answer_suf.rdlength - 1);
+      memcpy(answer, (char *)a_rdata + 1, short_be(answer_suf.rdlength) - 1);
+      key->callback(answer, key->arg);
+    } else {
+      dns_dbg("DNS ERROR: incorrect qtype (%u)\n", key->qtype);
+      return;
+    }
+  }
+
+  if (ev == PICO_SOCK_EV_ERR) {
+    dns_dbg("DNS: socket error received\n");
+  }
+}
+
+int pico_dns_client_getaddr(const char *url, void (*callback)(char *, void *), void *arg)
+{
+  char *q_hdr, *q_qname, *q_suf;
+  struct dns_message_hdr *hdr;
+  struct dns_query_suffix query_suf;
+  struct pico_dns_key *key;
+  uint16_t url_len = 0;
+  uint16_t id = 0;
+
+  if (!url || !callback) {
+    dns_dbg("DNS ERROR: NULL parameters\n");
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+
+  url_len = pico_dns_client_strlen(url);
+  /* 2 extra bytes for url_len to account for 2 extra label length octets */
+  q_hdr = pico_zalloc(sizeof(struct dns_message_hdr) + (1 + url_len + 1) + sizeof(struct dns_query_suffix));
+  if (!q_hdr) {
+    pico_err = PICO_ERR_ENOMEM;
+    return -1;
+  }
+  q_qname = q_hdr + sizeof(struct dns_message_hdr);
+  q_suf = q_qname + (1 + url_len + 1);
+
+  /* Construct query header */
+  hdr = (struct dns_message_hdr *) q_hdr;
+  do {
+    id = (uint16_t) (pico_rand() & 0xFFFFU);
+    dns_dbg("DNS: generated id %u\n", id);
+  } while (pico_dns_client_idcheck(id));
+  pico_dns_client_construct_hdr(hdr, id);
+  /* Add and manipulate domain name */
+  memcpy(q_qname + 1, url, url_len + 1);
+  pico_dns_client_label(q_qname);
+  /* Add type and class of query */
+  query_suf.qtype = short_be(PICO_DNS_TYPE_A);
+  query_suf.qclass = short_be(PICO_DNS_CLASS_IN);
+  memcpy(q_suf, &query_suf, sizeof(struct dns_query_suffix));
+  /* Create RB entry */
+  key = pico_zalloc(sizeof(struct pico_dns_key));
+  if (!key) {
+    pico_free(q_hdr);
+    pico_err = PICO_ERR_ENOMEM;
+    return -1;
+  }
+  key->q_hdr = q_hdr;
+  key->len = sizeof(struct dns_message_hdr) + (1 + url_len + 1) + sizeof(struct dns_query_suffix);
+  key->id = id;
+  key->qtype = PICO_DNS_TYPE_A;
+  key->qclass = PICO_DNS_CLASS_IN;
+  key->retrans = 1;
+
+  key->q_ns = *((struct pico_dns_ns *)pico_tree_first(&NSTable));
+  key->s = NULL;
+  key->callback = callback;
+  key->arg = arg;
+  /* Send query */
+  if (pico_dns_client_send(key) < 0) {
+    pico_free(q_hdr);
+    if (key->s)
+      pico_socket_close(key->s);
+    pico_free(key);
+    pico_err = PICO_ERR_EAGAIN;
+    return -1;
+  }
+  /* Insert RB entry */
+
+  if(pico_tree_insert(&DNSTable,key)) {
+    pico_free(q_hdr);
+    pico_free(key);
+    pico_err = PICO_ERR_EAGAIN;
+    return -1; /* Element key already exists */
+  }
+
+  pico_timer_add(PICO_DNS_CLIENT_RETRANS, pico_dns_client_retransmission, key);
+  return 0;
+}
+
+int pico_dns_client_getname(const char *ip, void (*callback)(char *, void *), void *arg)
+{
+  char *q_hdr, *q_qname, *q_suf;
+  struct dns_message_hdr *hdr;
+  struct dns_query_suffix query_suf;
+  struct pico_dns_key *key;
+  uint16_t ip_len = 0;
+  uint16_t arpa_len = 0;
+  uint16_t id = 0;
+
+  if (!ip || !callback) {
+    dns_dbg("DNS ERROR: NULL parameters\n");
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+
+  ip_len = pico_dns_client_strlen(ip);
+  arpa_len = pico_dns_client_strlen(".in-addr.arpa");
+  /* 2 extra bytes for ip_len and arpa_len to account for 2 extra length octets */
+  q_hdr = pico_zalloc(sizeof(struct dns_message_hdr) + (1 + ip_len + arpa_len + 1) + sizeof(struct dns_query_suffix));
+  if (!q_hdr) {
+    pico_err = PICO_ERR_ENOMEM;
+    return -1;
+  }
+  q_qname = q_hdr + sizeof(struct dns_message_hdr);
+  q_suf = q_qname + (1 + ip_len + arpa_len + 1);
+
+  /* Construct query header */
+  hdr = (struct dns_message_hdr *)q_hdr;
+  do {
+    id = (uint16_t) (pico_rand() & 0xFFFFU);
+    dns_dbg("DNS: generated id %u\n", id);
+  } while (pico_dns_client_idcheck(id));
+  pico_dns_client_construct_hdr(hdr, id);
+  /* Add and manipulate domain name */
+  memcpy(q_qname + 1, ip, ip_len + 1);
+  pico_dns_client_mirror(q_qname + 1);
+  memcpy(q_qname + 1 + ip_len, ".in-addr.arpa", arpa_len);
+  pico_dns_client_label(q_qname);
+  /* Add type and class of query */
+  query_suf.qtype = short_be(PICO_DNS_TYPE_PTR);
+  query_suf.qclass = short_be(PICO_DNS_CLASS_IN);
+  memcpy(q_suf, &query_suf, sizeof(struct dns_query_suffix));
+  /* Create RB entry */
+  key = pico_zalloc(sizeof(struct pico_dns_key));
+  if (!key) {
+    pico_free(q_hdr);
+    pico_err = PICO_ERR_ENOMEM;
+    return -1;
+  }
+  key->q_hdr = q_hdr;
+  key->len = sizeof(struct dns_message_hdr) + (1 + ip_len + arpa_len + 1) + sizeof(struct dns_query_suffix);
+  key->id = id;
+  key->qtype = PICO_DNS_TYPE_PTR;
+  key->qclass = PICO_DNS_CLASS_IN;
+  key->retrans = 1;
+  key->q_ns = *((struct pico_dns_ns *)pico_tree_first(&NSTable));
+  key->s = NULL;
+  key->callback = callback;
+  key->arg = arg;
+  /* Send query */
+  if (pico_dns_client_send(key) < 0) {
+    pico_free(q_hdr);
+    if (key->s)
+      pico_socket_close(key->s);
+    pico_free(key);
+    pico_err = PICO_ERR_EAGAIN;
+    return -1;
+  }
+  /* Insert RB entry */
+
+  if(pico_tree_insert(&DNSTable,key)) {
+    pico_free(q_hdr);
+    pico_free(key);
+    pico_err = PICO_ERR_EAGAIN;
+    return -1; /* Element key already exists */
+  }
+
+  pico_timer_add(PICO_DNS_CLIENT_RETRANS, pico_dns_client_retransmission, key);
+  return 0;
+}
+
+#ifdef PICO_DNS_CLIENT_MAIN
+int main(int argc, char *argv[])
+{
+  dns_dbg(">>>>> DNS GET ADDR\n");
+  pico_dns_client_getaddr("www.google.be");
+  dns_dbg(">>>>> DNS GET NAME\n");
+  pico_dns_client_getname("173.194.67.94");
+
+  return 0;
+}
+#endif /* PICO_DNS_CLIENT_MAIN */
+#endif /* PICO_SUPPORT_DNS_CLIENT */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_dns_client.h	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,23 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+.
+  
+Authors: Kristof Roelants
+*********************************************************************/
+
+#ifndef _INCLUDE_PICO_DNS_CLIENT
+#define _INCLUDE_PICO_DNS_CLIENT
+
+#define PICO_DNS_NS_DEL 0
+#define PICO_DNS_NS_ADD 1
+#include <stdint.h>
+
+int pico_dns_client_init();
+/* flag is PICO_DNS_NS_DEL or PICO_DNS_NS_ADD */
+int pico_dns_client_nameserver(struct pico_ip4 *ns, uint8_t flag);
+int pico_dns_client_getaddr(const char *url, void (*callback)(char *ip, void *arg), void *arg);
+int pico_dns_client_getname(const char *ip, void (*callback)(char *url, void *arg), void *arg);
+
+#endif /* _INCLUDE_PICO_DNS_CLIENT */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_http_client.c	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,701 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+Author: Andrei Carp <andrei.carp@tass.be>
+*********************************************************************/
+#include <string.h>
+#include <stdint.h>
+#include "pico_tree.h"
+#include "pico_config.h"
+#include "pico_socket.h"
+#include "pico_tcp.h"
+#include "pico_dns_client.h"
+#include "pico_http_client.h"
+#include "pico_ipv4.h"
+#include "pico_stack.h"
+
+/*
+ * This is the size of the following header
+ *
+ * GET <resource> HTTP/1.1<CRLF>
+ * Host: <host>:<port><CRLF>
+ * User-Agent: picoTCP<CRLF>
+ * Connection: close<CRLF>
+ * <CRLF>
+ *
+ * where <resource>,<host> and <port> will be added later.
+ */
+
+#ifdef PICO_SUPPORT_HTTP_CLIENT
+
+#define HTTP_GET_BASIC_SIZE   63u
+#define HTTP_HEADER_LINE_SIZE 50u
+#define RESPONSE_INDEX                9u
+
+#define HTTP_CHUNK_ERROR    0xFFFFFFFFu
+
+#ifdef dbg
+    #undef dbg
+    #define dbg(...) do{}while(0);
+#endif
+
+#define consumeChar(c)                             (pico_socket_read(client->sck,&c,1u))
+#define isLocation(line)                         (memcmp(line,"Location",8u) == 0)
+#define isContentLength(line)             (memcmp(line,"Content-Length",14u) == 0u)
+#define isTransferEncoding(line)        (memcmp(line,"Transfer-Encoding",17u) == 0u)
+#define isChunked(line)                            (memcmp(line," chunked",8u) == 0u)
+#define isNotHTTPv1(line)                        (memcmp(line,"HTTP/1.",7u))
+#define is_hex_digit(x) ( ('0' <= x && x <= '9') || ('a' <= x && x <= 'f') )
+#define hex_digit_to_dec(x) ( ('0' <= x && x <= '9') ? x-'0' : ( ('a' <= x && x <= 'f') ? x-'a' + 10 : -1) )
+
+struct pico_http_client
+{
+    uint16_t connectionID;
+    uint8_t state;
+    struct pico_socket * sck;
+    void (*wakeup)(uint16_t ev, uint16_t conn);
+    struct pico_ip4 ip;
+    struct pico_http_uri * uriKey;
+    struct pico_http_header * header;
+};
+
+// HTTP Client internal states
+#define HTTP_READING_HEADER      0
+#define HTTP_READING_BODY                 1
+#define HTTP_READING_CHUNK_VALUE 2
+#define HTTP_READING_CHUNK_TRAIL 3
+
+
+static int compareClients(void * ka, void * kb)
+{
+    return ((struct pico_http_client *)ka)->connectionID - ((struct pico_http_client *)kb)->connectionID;
+}
+
+PICO_TREE_DECLARE(pico_client_list,compareClients);
+
+// Local functions
+int parseHeaderFromServer(struct pico_http_client * client, struct pico_http_header * header);
+int readChunkLine(struct pico_http_client * client);
+
+void tcpCallback(uint16_t ev, struct pico_socket *s)
+{
+
+    struct pico_http_client * client = NULL;
+    struct pico_tree_node * index;
+
+    // find httpClient
+    pico_tree_foreach(index,&pico_client_list)
+    {
+        if( ((struct pico_http_client *)index->keyValue)->sck == s )
+        {
+            client = (struct pico_http_client *)index->keyValue;
+            break;
+        }
+    }
+
+    if(!client)
+    {
+        dbg("Client not found...Something went wrong !\n");
+        return;
+    }
+
+    if(ev & PICO_SOCK_EV_CONN)
+        client->wakeup(EV_HTTP_CON,client->connectionID);
+
+    if(ev & PICO_SOCK_EV_RD)
+    {
+
+        // read the header, if not read
+        if(client->state == HTTP_READING_HEADER)
+        {
+            // wait for header
+            client->header = pico_zalloc(sizeof(struct pico_http_header));
+            if(!client->header)
+            {
+                pico_err = PICO_ERR_ENOMEM;
+                return;
+            }
+
+            // wait for header
+            if(parseHeaderFromServer(client,client->header) < 0)
+            {
+                client->wakeup(EV_HTTP_ERROR,client->connectionID);
+            }
+            else
+            {
+                // call wakeup
+                if(client->header->responseCode != HTTP_CONTINUE)
+                {
+                    client->wakeup(
+                            client->header->responseCode == HTTP_OK ?
+                            EV_HTTP_REQ | EV_HTTP_BODY : // data comes for sure only when 200 is received
+                            EV_HTTP_REQ
+                            ,client->connectionID);
+                }
+            }
+        }
+        else
+        {
+            // just let the user know that data has arrived, if chunked data comes, will be treated in the
+            // read api.
+            client->wakeup(EV_HTTP_BODY,client->connectionID);
+        }
+    }
+
+    if(ev & PICO_SOCK_EV_ERR)
+    {
+        client->wakeup(EV_HTTP_ERROR,client->connectionID);
+    }
+
+    if( (ev & PICO_SOCK_EV_CLOSE) || (ev & PICO_SOCK_EV_FIN) )
+    {
+        client->wakeup(EV_HTTP_CLOSE,client->connectionID);
+    }
+
+}
+
+// used for getting a response from DNS servers
+static void dnsCallback(char *ip, void * ptr)
+{
+    struct pico_http_client * client = (struct pico_http_client *)ptr;
+
+    if(!client)
+    {
+        dbg("Who made the request ?!\n");
+        return;
+    }
+
+    if(ip)
+    {
+        client->wakeup(EV_HTTP_DNS,client->connectionID);
+
+        // add the ip address to the client, and start a tcp connection socket
+        pico_string_to_ipv4(ip,&client->ip.addr);
+        pico_free(ip);
+        client->sck = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_TCP, &tcpCallback);
+        if(!client->sck)
+        {
+            client->wakeup(EV_HTTP_ERROR,client->connectionID);
+            return;
+        }
+
+        if(pico_socket_connect(client->sck,&client->ip,short_be(client->uriKey->port)) < 0)
+        {
+            client->wakeup(EV_HTTP_ERROR,client->connectionID);
+            return;
+        }
+
+    }
+    else
+    {
+        // wakeup client and let know error occured
+        client->wakeup(EV_HTTP_ERROR,client->connectionID);
+
+        // close the client (free used heap)
+        pico_http_client_close(client->connectionID);
+    }
+}
+
+/*
+ * API used for opening a new HTTP Client.
+ *
+ * The accepted uri's are [http://]hostname[:port]/resource
+ * no relative uri's are accepted.
+ *
+ * The function returns a connection ID >= 0 if successful
+ * -1 if an error occured.
+ */
+int pico_http_client_open(char * uri, void (*wakeup)(uint16_t ev, uint16_t conn))
+{
+    struct pico_http_client * client;
+
+    if(!wakeup)
+    {
+        pico_err = PICO_ERR_EINVAL;
+        return HTTP_RETURN_ERROR;
+    }
+
+    client = pico_zalloc(sizeof(struct pico_http_client));
+    if(!client)
+    {
+        // memory error
+        pico_err = PICO_ERR_ENOMEM;
+        return HTTP_RETURN_ERROR;
+    }
+
+    client->wakeup = wakeup;
+    client->connectionID = (uint16_t)pico_rand() & 0x7FFFu; // negative values mean error, still not good generation
+
+    client->uriKey = pico_zalloc(sizeof(struct pico_http_uri));
+
+    if(!client->uriKey)
+    {
+        pico_err = PICO_ERR_ENOMEM;
+        pico_free(client);
+        return HTTP_RETURN_ERROR;
+    }
+
+    pico_processURI(uri,client->uriKey);
+
+    if(pico_tree_insert(&pico_client_list,client))
+    {
+        // already in
+        pico_err = PICO_ERR_EEXIST;
+        pico_free(client->uriKey);
+        pico_free(client);
+        return HTTP_RETURN_ERROR;
+    }
+
+    // dns query
+    dbg("Querying : %s \n",client->uriKey->host);
+    pico_dns_client_getaddr(client->uriKey->host, dnsCallback,client);
+
+    // return the connection ID
+    return client->connectionID;
+}
+
+/*
+ * API for sending a header to the client.
+ *
+ * if hdr == HTTP_HEADER_RAW , then the parameter header
+ * is sent as it is to client.
+ *
+ * if hdr == HTTP_HEADER_DEFAULT, then the parameter header
+ * is ignored and the library will build the response header
+ * based on the uri passed when opening the client.
+ *
+ */
+int pico_http_client_sendHeader(uint16_t conn, char * header, int hdr)
+{
+    struct pico_http_client search = {.connectionID = conn};
+    struct pico_http_client * http = pico_tree_findKey(&pico_client_list,&search);
+    int length ;
+    if(!http)
+    {
+        dbg("Client not found !\n");
+        return HTTP_RETURN_ERROR;
+    }
+
+    // the api gives the possibility to the user to build the GET header
+    // based on the uri passed when opening the client, less headache for the user
+    if(hdr == HTTP_HEADER_DEFAULT)
+    {
+        header = pico_http_client_buildHeader(http->uriKey);
+
+        if(!header)
+        {
+            pico_err = PICO_ERR_ENOMEM;
+            return HTTP_RETURN_ERROR;
+        }
+    }
+
+    length = pico_socket_write(http->sck,(void *)header,strlen(header)+1);
+
+    if(hdr == HTTP_HEADER_DEFAULT)
+        pico_free(header);
+
+    return length;
+}
+
+/*
+ * API for reading received data.
+ *
+ * This api hides from the user if the transfer-encoding
+ * was chunked or a full length was provided, in case of
+ * a chunked transfer encoding will "de-chunk" the data
+ * and pass it to the user.
+ */
+int pico_http_client_readData(uint16_t conn, char * data, uint16_t size)
+{
+    struct pico_http_client dummy = {.connectionID = conn};
+    struct pico_http_client * client = pico_tree_findKey(&pico_client_list,&dummy);
+
+    if(!client)
+    {
+        dbg("Wrong connection id !\n");
+        pico_err = PICO_ERR_EINVAL;
+        return HTTP_RETURN_ERROR;
+    }
+
+    // for the moment just read the data, do not care if it's chunked or not
+    if(client->header->transferCoding == HTTP_TRANSFER_FULL)
+        return pico_socket_read(client->sck,(void *)data,size);
+    else
+    {
+        int lenRead = 0;
+
+        // read the chunk line
+        if(readChunkLine(client) == HTTP_RETURN_ERROR)
+        {
+            dbg("Probably the chunk is malformed or parsed wrong...\n");
+            client->wakeup(EV_HTTP_ERROR,client->connectionID);
+            return HTTP_RETURN_ERROR;
+        }
+
+        // nothing to read, no use to try
+        if(client->state != HTTP_READING_BODY)
+        {
+            pico_err = PICO_ERR_EAGAIN;
+            return HTTP_RETURN_OK;
+        }
+
+        // check if we need more than one chunk
+        if(size >= client->header->contentLengthOrChunk)
+        {
+            // read the rest of the chunk, if chunk is done, proceed to the next chunk
+            while(lenRead <= size)
+            {
+                int tmpLenRead = 0;
+
+                if(client->state == HTTP_READING_BODY)
+                {
+
+                    // if needed truncate the data
+                    tmpLenRead = pico_socket_read(client->sck,data + lenRead,
+                    client->header->contentLengthOrChunk < size-lenRead ? client->header->contentLengthOrChunk : size-lenRead);
+
+                    if(tmpLenRead > 0)
+                    {
+                        client->header->contentLengthOrChunk -= tmpLenRead;
+                    }
+                    else if(tmpLenRead < 0)
+                    {
+                        // error on reading
+                        dbg(">>> Error returned pico_socket_read\n");
+                        pico_err = PICO_ERR_EBUSY;
+                        // return how much data was read until now
+                        return lenRead;
+                    }
+                }
+
+                lenRead += tmpLenRead;
+                if(readChunkLine(client) == HTTP_RETURN_ERROR)
+                {
+                    dbg("Probably the chunk is malformed or parsed wrong...\n");
+                    client->wakeup(EV_HTTP_ERROR,client->connectionID);
+                    return HTTP_RETURN_ERROR;
+                }
+
+                if(client->state != HTTP_READING_BODY || !tmpLenRead)  break;
+
+            }
+        }
+        else
+        {
+            // read the data from the chunk
+            lenRead = pico_socket_read(client->sck,(void *)data,size);
+
+            if(lenRead)
+                client->header->contentLengthOrChunk -= lenRead;
+        }
+
+        return lenRead;
+    }
+}
+
+/*
+ * API for reading received data.
+ *
+ * Reads out the header struct received from server.
+ */
+struct pico_http_header * pico_http_client_readHeader(uint16_t conn)
+{
+    struct pico_http_client dummy = {.connectionID = conn};
+    struct pico_http_client * client = pico_tree_findKey(&pico_client_list,&dummy);
+
+    if(client)
+    {
+        return client->header;
+    }
+    else
+    {
+        // not found
+        dbg("Wrong connection id !\n");
+        pico_err = PICO_ERR_EINVAL;
+        return NULL;
+    }
+}
+
+/*
+ * API for reading received data.
+ *
+ * Reads out the uri struct after was processed.
+ */
+struct pico_http_uri * pico_http_client_readUriData(uint16_t conn)
+{
+    struct pico_http_client dummy = {.connectionID = conn};
+    struct pico_http_client * client = pico_tree_findKey(&pico_client_list,&dummy);
+    //
+    if(client)
+        return client->uriKey;
+    else
+    {
+        // not found
+        dbg("Wrong connection id !\n");
+        pico_err = PICO_ERR_EINVAL;
+        return NULL;
+    }
+}
+
+/*
+ * API for reading received data.
+ *
+ * Close the client.
+ */
+int pico_http_client_close(uint16_t conn)
+{
+    struct pico_http_client * toBeRemoved = NULL;
+    struct pico_http_client dummy = {};
+    dummy.connectionID = conn;
+
+    dbg("Closing the client...\n");
+    toBeRemoved = pico_tree_delete(&pico_client_list,&dummy);
+    if(!toBeRemoved)
+    {
+        dbg("Warning ! Element not found ...");
+        return HTTP_RETURN_ERROR;
+    }
+
+    // close socket
+    if(toBeRemoved->sck)
+    pico_socket_close(toBeRemoved->sck);
+
+
+    if(toBeRemoved->header)
+    {
+        // free space used
+            if(toBeRemoved->header->location)
+                pico_free(toBeRemoved->header->location);
+
+        pico_free(toBeRemoved->header);
+    }
+
+    if(toBeRemoved->uriKey)
+    {
+        if(toBeRemoved->uriKey->host)
+            pico_free(toBeRemoved->uriKey->host);
+
+        if(toBeRemoved->uriKey->resource)
+            pico_free(toBeRemoved->uriKey->resource);
+        pico_free(toBeRemoved->uriKey);
+    }
+    pico_free(toBeRemoved);
+
+    return 0;
+}
+
+/*
+ * API for reading received data.
+ *
+ * Builds a GET header based on the fields on the uri.
+ */
+char * pico_http_client_buildHeader(const struct pico_http_uri * uriData)
+{
+    char * header;
+    char port[6u]; // 6 = max length of a uint16 + \0
+
+    uint16_t headerSize = HTTP_GET_BASIC_SIZE;
+
+    if(!uriData->host || !uriData->resource || !uriData->port)
+    {
+        pico_err = PICO_ERR_EINVAL;
+        return NULL;
+    }
+
+    //
+    headerSize += strlen(uriData->host) + strlen(uriData->resource) + pico_itoa(uriData->port,port) + 4u; // 3 = size(CRLF + \0)
+    header = pico_zalloc(headerSize);
+
+    if(!header)
+    {
+        // not enought memory
+        pico_err = PICO_ERR_ENOMEM;
+        return NULL;
+    }
+
+    // build the actual header
+    strcpy(header,"GET ");
+    strcat(header,uriData->resource);
+    strcat(header," HTTP/1.1\r\n");
+    strcat(header,"Host: ");
+    strcat(header,uriData->host);
+    strcat(header,":");
+    strcat(header,port);
+    strcat(header,"\r\n");
+    strcat(header,"User-Agent: picoTCP\r\nConnection: close\r\n\r\n"); //?
+
+    return header;
+}
+
+int parseHeaderFromServer(struct pico_http_client * client, struct pico_http_header * header)
+{
+    char line[HTTP_HEADER_LINE_SIZE];
+    char c;
+    int index = 0;
+
+    // read the first line of the header
+    while(consumeChar(c)>0 && c!='\r')
+    {
+        if(index < HTTP_HEADER_LINE_SIZE) // truncate if too long
+            line[index++] = c;
+    }
+
+    consumeChar(c); // consume \n
+
+    // check the integrity of the response
+    // make sure we have enough characters to include the response code
+    // make sure the server response starts with HTTP/1.
+    if(index < RESPONSE_INDEX+2 || isNotHTTPv1(line))
+    {
+        // wrong format of the the response
+        pico_err = PICO_ERR_EINVAL;
+        return HTTP_RETURN_ERROR;
+    }
+
+    // extract response code
+    header->responseCode = (line[RESPONSE_INDEX] - '0') * 100u +
+                                                 (line[RESPONSE_INDEX+1] - '0') * 10u +
+                                                 (line[RESPONSE_INDEX+2] - '0');
+
+
+    if(header->responseCode/100u > 5u)
+    {
+        // invalid response type
+        header->responseCode = 0;
+        return HTTP_RETURN_ERROR;
+    }
+
+    dbg("Server response : %d \n",header->responseCode);
+
+    // parse the rest of the header
+    while(consumeChar(c)>0)
+    {
+        if(c==':')
+        {
+            // check for interesting fields
+
+            // Location:
+            if(isLocation(line))
+            {
+                index = 0;
+                while(consumeChar(c)>0 && c!='\r')
+                {
+                    line[index++] = c;
+                }
+
+                // allocate space for the field
+                header->location = pico_zalloc(index+1u);
+                if(!header->location)
+                {
+                    pico_err = PICO_ERR_ENOMEM;
+                    return HTTP_RETURN_ERROR;
+                }
+
+                memcpy(header->location,line,index);
+
+            }// Content-Length:
+            else if(isContentLength(line))
+            {
+                header->contentLengthOrChunk = 0u;
+                header->transferCoding = HTTP_TRANSFER_FULL;
+                // consume the first space
+                consumeChar(c);
+                while(consumeChar(c)>0 && c!='\r')
+                {
+                    header->contentLengthOrChunk = header->contentLengthOrChunk*10u + (c-'0');
+                }
+
+            }// Transfer-Encoding: chunked
+            else if(isTransferEncoding(line))
+            {
+                index = 0;
+                while(consumeChar(c)>0 && c!='\r')
+                {
+                    line[index++] = c;
+                }
+
+                if(isChunked(line))
+                {
+                    header->contentLengthOrChunk = 0u;
+                    header->transferCoding = HTTP_TRANSFER_CHUNKED;
+                }
+
+            }// just ignore the line
+            else
+            {
+                while(consumeChar(c)>0 && c!='\r');
+            }
+
+            // consume the next one
+            consumeChar(c);
+            // reset the index
+            index = 0u;
+        }
+        else if(c=='\r' && !index)
+        {
+                // consume the \n
+                consumeChar(c);
+                break;
+        }
+        else
+        {
+            line[index++] = c;
+        }
+    }
+
+    if(header->transferCoding == HTTP_TRANSFER_CHUNKED)
+    {
+        // read the first chunk
+        header->contentLengthOrChunk = 0;
+
+        client->state = HTTP_READING_CHUNK_VALUE;
+        readChunkLine(client);
+
+    }
+    else
+        client->state = HTTP_READING_BODY;
+
+    dbg("End of header\n");
+    return HTTP_RETURN_OK;
+
+}
+
+// an async read of the chunk part, since in theory a chunk can be split in 2 packets
+int readChunkLine(struct pico_http_client * client)
+{
+    char c = 0;
+
+    if(client->header->contentLengthOrChunk==0 && client->state == HTTP_READING_BODY)
+    {
+        client->state = HTTP_READING_CHUNK_VALUE;
+    }
+
+    if(client->state == HTTP_READING_CHUNK_VALUE)
+    {
+        while(consumeChar(c)>0 && c!='\r' && c!=';')
+        {
+            if(is_hex_digit(c))
+                client->header->contentLengthOrChunk = (client->header->contentLengthOrChunk << 4u) + hex_digit_to_dec(c);
+            else
+            {
+                pico_err = PICO_ERR_EINVAL;
+                // something went wrong
+                return HTTP_RETURN_ERROR;
+            }
+        }
+
+        if(c=='\r' || c==';') client->state = HTTP_READING_CHUNK_TRAIL;
+    }
+
+    if(client->state == HTTP_READING_CHUNK_TRAIL)
+    {
+
+        while(consumeChar(c)>0 && c!='\n');
+
+        if(c=='\n') client->state = HTTP_READING_BODY;
+    }
+
+    return HTTP_RETURN_OK;
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_http_client.h	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,49 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+Author: Andrei Carp <andrei.carp@tass.be>
+*********************************************************************/
+
+
+#ifndef PICO_HTTP_CLIENT_H_
+#define PICO_HTTP_CLIENT_H_
+
+#include "pico_http_util.h"
+
+/*
+ * Transfer encodings
+ */
+#define HTTP_TRANSFER_CHUNKED  1u
+#define HTTP_TRANSFER_FULL         0u
+
+/*
+ * Parameters for the send header function
+ */
+#define HTTP_HEADER_RAW                    0u
+#define HTTP_HEADER_DEFAULT            1u
+
+/*
+ * Data types
+ */
+
+struct pico_http_header
+{
+    uint16_t responseCode;                     // http response
+    char * location;                                     // if redirect is reported
+    uint32_t contentLengthOrChunk;    // size of the message
+    uint8_t transferCoding;                   // chunked or full
+
+};
+
+int pico_http_client_open(char * uri, void (*wakeup)(uint16_t ev, uint16_t conn));
+int pico_http_client_sendHeader(uint16_t conn, char * header, int hdr);
+
+struct pico_http_header * pico_http_client_readHeader(uint16_t conn);
+struct pico_http_uri * pico_http_client_readUriData(uint16_t conn);
+char * pico_http_client_buildHeader(const struct pico_http_uri * uriData);
+
+int pico_http_client_readData(uint16_t conn, char * data, uint16_t size);
+int pico_http_client_close(uint16_t conn);
+
+#endif /* PICO_HTTP_CLIENT_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_http_server.c	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,636 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+Author: Andrei Carp <andrei.carp@tass.be>
+*********************************************************************/
+
+#include "pico_stack.h"
+#include "pico_http_server.h"
+#include "pico_tcp.h"
+#include "pico_tree.h"
+#include "pico_socket.h"
+
+#ifdef PICO_SUPPORT_HTTP_SERVER
+
+#define BACKLOG                              10
+
+#define HTTP_SERVER_CLOSED         0
+#define HTTP_SERVER_LISTEN         1
+
+#define HTTP_HEADER_MAX_LINE     256u
+
+#define consumeChar(c) (pico_socket_read(client->sck,&c,1u))
+
+static char returnOkHeader[] =
+"HTTP/1.1 200 OK\r\n\
+Host: localhost\r\n\
+Transfer-Encoding: chunked\r\n\
+Connection: close\r\n\
+\r\n";
+
+static char returnFailHeader[] =
+"HTTP/1.1 404 Not Found\r\n\
+Host: localhost\r\n\
+Connection: close\r\n\
+\r\n\
+<html><body>The resource you requested cannot be found !</body></html>";
+
+static char errorHeader[] =
+"HTTP/1.1 400 Bad Request\r\n\
+Host: localhost\r\n\
+Connection: close\r\n\
+\r\n\
+<html><body>There was a problem with your request !</body></html>";
+
+struct httpServer
+{
+    uint16_t state;
+    struct pico_socket * sck;
+    uint16_t port;
+    void (*wakeup)(uint16_t ev, uint16_t param);
+    uint8_t accepted;
+};
+
+struct httpClient
+{
+    uint16_t connectionID;
+    struct pico_socket * sck;
+    void * buffer;
+    uint16_t bufferSize;
+    uint16_t bufferSent;
+    char * resource;
+    uint16_t state;
+};
+
+/* Local states for clients */
+#define HTTP_WAIT_HDR                0
+#define HTTP_WAIT_EOF_HDR        1
+#define HTTP_EOF_HDR                2
+#define HTTP_WAIT_RESPONSE  3
+#define HTTP_WAIT_DATA            4
+#define HTTP_SENDING_DATA        5
+#define HTTP_ERROR                    6
+#define HTTP_CLOSED                    7
+
+static struct httpServer server = {};
+
+/*
+ * Private functions
+ */
+static int parseRequest(struct httpClient * client);
+static int readRemainingHeader(struct httpClient * client);
+static void sendData(struct httpClient * client);
+static inline int readData(struct httpClient * client); // used only in a place
+static inline struct httpClient * findClient(uint16_t conn);
+
+static int compareClients(void * ka, void * kb)
+{
+    return ((struct httpClient *)ka)->connectionID - ((struct httpClient *)kb)->connectionID;
+}
+
+PICO_TREE_DECLARE(pico_http_clients,compareClients);
+
+void httpServerCbk(uint16_t ev, struct pico_socket *s)
+{
+    struct pico_tree_node * index;
+    struct httpClient * client = NULL;
+  uint8_t serverEvent = FALSE;
+
+  // determine the client for the socket
+  if( s == server.sck)
+  {
+        serverEvent = TRUE;
+  }
+  else
+  {
+        pico_tree_foreach(index,&pico_http_clients)
+        {
+            client = index->keyValue;
+            if(client->sck == s) break;
+            client = NULL;
+        }
+  }
+
+    if(!client && !serverEvent)
+    {
+        return;
+    }
+
+    if (ev & PICO_SOCK_EV_RD)
+    {
+
+        if(readData(client) == HTTP_RETURN_ERROR)
+        {
+            // send out error
+            client->state = HTTP_ERROR;
+            pico_socket_write(client->sck,errorHeader,sizeof(errorHeader)-1);
+            server.wakeup(EV_HTTP_ERROR,client->connectionID);
+        }
+    }
+
+    if(ev & PICO_SOCK_EV_WR)
+    {
+        if(client->state == HTTP_SENDING_DATA)
+        {
+            sendData(client);
+        }
+    }
+
+    if(ev & PICO_SOCK_EV_CONN)
+    {
+        server.accepted = FALSE;
+        server.wakeup(EV_HTTP_CON,HTTP_SERVER_ID);
+        if(!server.accepted)
+        {
+            pico_socket_close(s); // reject socket
+        }
+    }
+
+    if( (ev & PICO_SOCK_EV_CLOSE) || (ev & PICO_SOCK_EV_FIN) )
+    {
+        server.wakeup(EV_HTTP_CLOSE,(serverEvent ? HTTP_SERVER_ID : client->connectionID));
+    }
+
+    if(ev & PICO_SOCK_EV_ERR)
+    {
+        server.wakeup(EV_HTTP_ERROR,(serverEvent ? HTTP_SERVER_ID : client->connectionID));
+    }
+}
+
+/*
+ * API for starting the server. If 0 is passed as a port, the port 80
+ * will be used.
+ */
+int pico_http_server_start(uint16_t port, void (*wakeup)(uint16_t ev, uint16_t conn))
+{
+    struct pico_ip4 anything = {};
+
+    server.port = port ? short_be(port) : short_be(80u);
+
+    if(!wakeup)
+    {
+        pico_err = PICO_ERR_EINVAL;
+        return HTTP_RETURN_ERROR;
+    }
+
+    server.sck = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_TCP, &httpServerCbk);
+
+    if(!server.sck)
+    {
+        pico_err = PICO_ERR_EFAULT;
+        return HTTP_RETURN_ERROR;
+    }
+
+    if(pico_socket_bind(server.sck , &anything, &server.port)!=0)
+    {
+        pico_err = PICO_ERR_EADDRNOTAVAIL;
+        return HTTP_RETURN_ERROR;
+    }
+
+    if (pico_socket_listen(server.sck, BACKLOG) != 0)
+    {
+        pico_err = PICO_ERR_EADDRINUSE;
+        return HTTP_RETURN_ERROR;
+    }
+    server.wakeup = wakeup;
+    server.state = HTTP_SERVER_LISTEN;
+    return HTTP_RETURN_OK;
+}
+
+/*
+ * API for accepting new connections. This function should be
+ * called when the event EV_HTTP_CON is triggered, if not called
+ * when noticed the connection will be considered rejected and the
+ * socket will be dropped.
+ *
+ * Returns the ID of the new connection or a negative value if error.
+ */
+int pico_http_server_accept(void)
+{
+  struct pico_ip4 orig;
+  struct httpClient * client;
+  uint16_t port;
+
+  client = pico_zalloc(sizeof(struct httpClient));
+  if(!client)
+  {
+        pico_err = PICO_ERR_ENOMEM;
+      return HTTP_RETURN_ERROR;
+  }
+
+    client->sck = pico_socket_accept(server.sck,&orig,&port);
+
+    if(!client->sck)
+    {
+        pico_err = PICO_ERR_ENOMEM;
+        pico_free(client);
+        return HTTP_RETURN_ERROR;
+    }
+
+    server.accepted = TRUE;
+    // buffer used for async sending
+    client->state = HTTP_WAIT_HDR;
+    client->buffer = NULL;
+    client->bufferSize = 0;
+    client->connectionID = pico_rand() & 0x7FFF;
+
+    //add element to the tree, if duplicate because the rand
+    //regenerate
+    while(pico_tree_insert(&pico_http_clients,client)!=NULL)
+        client->connectionID = pico_rand() & 0x7FFF;
+
+    return client->connectionID;
+}
+
+/*
+ * Function used for getting the resource asked by the
+ * client. It is useful after the request header (EV_HTTP_REQ)
+ * from client was received, otherwise NULL is returned.
+ */
+char * pico_http_getResource(uint16_t conn)
+{
+    struct httpClient * client = findClient(conn);
+
+    if(!client)
+        return NULL;
+    else
+        return client->resource;
+}
+
+/*
+ * After the resource was asked by the client (EV_HTTP_REQ)
+ * before doing anything else, the server has to let know
+ * the client if the resource can be provided or not.
+ *
+ * This is controlled via the code parameter which can
+ * have two values :
+ *
+ * HTTP_RESOURCE_FOUND or HTTP_RESOURCE_NOT_FOUND
+ *
+ * If a resource is reported not found the 404 header will be sent and the connection
+ * will be closed , otherwise the 200 header is sent and the user should
+ * immediately submit data.
+ *
+ */
+int pico_http_respond(uint16_t conn, uint16_t code)
+{
+    struct httpClient * client = findClient(conn);
+
+    if(!client)
+    {
+        dbg("Client not found !\n");
+        return HTTP_RETURN_ERROR;
+    }
+
+    if(client->state == HTTP_WAIT_RESPONSE)
+    {
+        if(code == HTTP_RESOURCE_FOUND)
+        {
+            client->state = HTTP_WAIT_DATA;
+            return pico_socket_write(client->sck,returnOkHeader,sizeof(returnOkHeader)-1);//remove \0
+        }
+        else
+        {
+            int length;
+
+            length = pico_socket_write(client->sck,returnFailHeader,sizeof(returnFailHeader)-1);//remove \0
+            pico_socket_close(client->sck);
+            client->state = HTTP_CLOSED;
+            return length;
+
+        }
+    }
+    else
+    {
+        dbg("Bad state for the client \n");
+        return HTTP_RETURN_ERROR;
+    }
+
+}
+
+/*
+ * API used to submit data to the client.
+ * Server sends data only using Transfer-Encoding: chunked.
+ *
+ * With this function the user will submit a data chunk to
+ * be sent.
+ * The function will send the chunk size in hex and the rest will
+ * be sent using WR event from sockets.
+ * After each transmision EV_HTTP_PROGRESS is called and at the
+ * end of the chunk EV_HTTP_SENT is called.
+ *
+ * To let the client know this is the last chunk, the user
+ * should pass a NULL buffer.
+ */
+int pico_http_submitData(uint16_t conn, void * buffer, int len)
+{
+
+    struct httpClient * client = findClient(conn);
+    char chunkStr[10];
+    int chunkCount;
+
+    if(client->state != HTTP_WAIT_DATA)
+    {
+        dbg("Client is in a different state than accepted\n");
+        return HTTP_RETURN_ERROR;
+    }
+
+    if(client->buffer)
+    {
+        dbg("Already a buffer submited\n");
+        return HTTP_RETURN_ERROR;
+    }
+
+    if(!client)
+    {
+        dbg("Wrong connection ID\n");
+        return HTTP_RETURN_ERROR;
+    }
+
+    if(!buffer)
+    {
+        len = 0;
+    }
+
+    if(len > 0)
+    {
+        client->buffer = pico_zalloc(len);
+        if(!client->buffer)
+        {
+            pico_err = PICO_ERR_ENOMEM;
+            return HTTP_RETURN_ERROR;
+        }
+        // taking over the buffer
+        memcpy(client->buffer,buffer,len);
+    }
+    else
+        client->buffer = NULL;
+
+
+    client->bufferSize = len;
+    client->bufferSent = 0;
+
+    // create the chunk size and send it
+    if(len > 0)
+    {
+        client->state = HTTP_SENDING_DATA;
+        chunkCount = pico_itoaHex(client->bufferSize,chunkStr);
+        chunkStr[chunkCount++] = '\r';
+        chunkStr[chunkCount++] = '\n';
+        pico_socket_write(client->sck,chunkStr,chunkCount);
+    }
+    else if(len == 0)
+    {
+        dbg("->\n");
+        // end of transmision
+        pico_socket_write(client->sck,"0\r\n\r\n",5u);
+        // nothing left, close the client
+        pico_socket_close(client->sck);
+        client->state = HTTP_CLOSED;
+    }
+
+    return HTTP_RETURN_OK;
+}
+
+/*
+ * When EV_HTTP_PROGRESS is triggered you can use this
+ * function to check the state of the chunk.
+ */
+
+int pico_http_getProgress(uint16_t conn, uint16_t * sent, uint16_t *total)
+{
+    struct httpClient * client = findClient(conn);
+
+    if(!client)
+    {
+        dbg("Wrong connection id !\n");
+        return HTTP_RETURN_ERROR;
+    }
+
+    *sent = client->bufferSent;
+    *total = client->bufferSize;
+
+    return HTTP_RETURN_OK;
+}
+
+/*
+ * This API can be used to close either a client
+ * or the server ( if you pass HTTP_SERVER_ID as a connection ID).
+ */
+int pico_http_close(uint16_t conn)
+{
+    // close the server
+    if(conn == HTTP_SERVER_ID)
+    {
+        if(server.state == HTTP_SERVER_LISTEN)
+        {
+            struct pico_tree_node * index, * tmp;
+            // close the server
+            pico_socket_close(server.sck);
+            server.sck = NULL;
+
+            // destroy the tree
+            pico_tree_foreach_safe(index,&pico_http_clients,tmp)
+            {
+                struct httpClient * client = index->keyValue;
+
+                if(client->resource)
+                    pico_free(client->resource);
+
+                pico_socket_close(client->sck);
+                pico_tree_delete(&pico_http_clients,client);
+            }
+
+            server.state = HTTP_SERVER_CLOSED;
+            return HTTP_RETURN_OK;
+        }
+        else // nothing to close
+            return HTTP_RETURN_ERROR;
+    } // close a connection in this case
+    else
+    {
+
+        struct httpClient * client = findClient(conn);
+
+        if(!client)
+        {
+            dbg("Client not found..\n");
+            return HTTP_RETURN_ERROR;
+        }
+
+        pico_tree_delete(&pico_http_clients,client);
+
+        if(client->resource)
+            pico_free(client->resource);
+
+        if(client->buffer)
+            pico_free(client->buffer);
+
+        if(client->state != HTTP_CLOSED || !client->sck)
+            pico_socket_close(client->sck);
+
+        pico_free(client);
+        return HTTP_RETURN_OK;
+    }
+}
+
+// check the integrity of the request
+int parseRequest(struct httpClient * client)
+{
+    char c;
+    //read first line
+    consumeChar(c);
+    if(c == 'G')
+    { // possible GET
+
+        char line[HTTP_HEADER_MAX_LINE];
+        int index = 0;
+
+        line[index] = c;
+
+        // consume the full line
+        while(consumeChar(c)>0) // read char by char only the first line
+        {
+            line[++index] = c;
+            if(c == '\n')
+                break;
+
+                if(index >= HTTP_HEADER_MAX_LINE)
+            {
+                dbg("Size exceeded \n");
+                return HTTP_RETURN_ERROR;
+            }
+        }
+
+        // extract the function and the resource
+        if(memcmp(line,"GET",3u) || line[3u]!=' ' || index < 10u || line[index] !='\n')
+        {
+            dbg("Wrong command or wrong ending\n");
+            return HTTP_RETURN_ERROR;
+        }
+
+        // start reading the resource
+        index = 4u; // go after ' '
+        while(line[index]!=' ')
+        {
+            if(line[index]=='\n') // no terminator ' '
+            {
+                dbg("No terminator...\n");
+                return HTTP_RETURN_ERROR;
+            }
+
+            index++;
+        }
+
+        client->resource = pico_zalloc(index - 3u);// allocate without the GET in front + 1 which is \0
+
+        if(!client)
+        {
+            pico_err = PICO_ERR_ENOMEM;
+            return HTTP_RETURN_ERROR;
+        }
+
+        // copy the resource
+        memcpy(client->resource,line+4u,index-4u);// copy without the \0 which was already set by pico_zalloc
+
+        client->state = HTTP_WAIT_EOF_HDR;
+        return HTTP_RETURN_OK;
+
+    }
+
+    return HTTP_RETURN_ERROR;
+}
+
+
+
+int readRemainingHeader(struct httpClient * client)
+{
+    char line[100];
+    int count = 0;
+    int len;
+
+    while( (len = pico_socket_read(client->sck,line,100u)) > 0)
+    {
+        char c;
+        int index = 0;
+        // parse the response
+        while(index < len)
+        {
+            c = line[index++];
+            if(c!='\r' && c!='\n')
+                count++;
+            if(c=='\n')
+            {
+                if(!count)
+                {
+                    client->state = HTTP_EOF_HDR;
+                    dbg("End of header !\n");
+                    break;
+                }
+                count = 0;
+
+            }
+        }
+    }
+
+    return HTTP_RETURN_OK;
+}
+
+void sendData(struct httpClient * client)
+{
+    int length;
+    while( client->bufferSent < client->bufferSize &&
+    (length = pico_socket_write(client->sck,client->buffer+client->bufferSent,client->bufferSize-client->bufferSent)) > 0 )
+    {
+        client->bufferSent += length;
+        server.wakeup(EV_HTTP_PROGRESS,client->connectionID);
+    }
+
+    if(client->bufferSent == client->bufferSize && client->bufferSize)
+    {
+        //send chunk trail
+        if(pico_socket_write(client->sck,"\r\n",2) > 0)
+        {
+            client->state = HTTP_WAIT_DATA;
+            //free the buffer
+            pico_free(client->buffer);
+            client->buffer = NULL;
+            server.wakeup(EV_HTTP_SENT,client->connectionID);
+        }
+    }
+
+}
+
+int readData(struct httpClient * client)
+{
+    if(client->state == HTTP_WAIT_HDR)
+    {
+        if(parseRequest(client)<0 || readRemainingHeader(client)<0)
+        {
+            return HTTP_RETURN_ERROR;
+        }
+    } // continue with this in case the header comes line by line not a big chunk
+    else if(client->state == HTTP_WAIT_EOF_HDR)
+    {
+        if(readRemainingHeader(client)<0 )
+            return HTTP_RETURN_ERROR;
+    }
+
+    if(client->state == HTTP_EOF_HDR)
+    {
+        client->state = HTTP_WAIT_RESPONSE;
+        pico_socket_shutdown(client->sck,PICO_SHUT_RD);
+        server.wakeup(EV_HTTP_REQ,client->connectionID);
+    }
+
+    return HTTP_RETURN_OK;
+}
+
+struct httpClient * findClient(uint16_t conn)
+{
+    struct httpClient dummy = {.connectionID = conn};
+
+    return pico_tree_findKey(&pico_http_clients,&dummy);
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_http_server.h	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,40 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+Author: Andrei Carp <andrei.carp@tass.be>
+*********************************************************************/
+
+#ifndef PICO_HTTP_SERVER_H_
+#define PICO_HTTP_SERVER_H_
+
+#include <stdint.h>
+#include "pico_http_util.h"
+
+// Response codes
+#define HTTP_RESOURCE_FOUND                0
+#define HTTP_RESOURCE_NOT_FOUND        1
+
+// Generic id for the server
+#define HTTP_SERVER_ID                    0
+
+/*
+ * Server functions
+ */
+int pico_http_server_start(uint16_t port, void (*wakeup)(uint16_t ev, uint16_t conn));
+int pico_http_server_accept(void);
+
+/*
+ * Client functions
+ */
+char * pico_http_getResource(uint16_t conn);
+int      pico_http_getProgress(uint16_t conn, uint16_t * sent, uint16_t *total);
+
+/*
+ * Handshake and data functions
+ */
+int      pico_http_respond(uint16_t conn, uint16_t code);
+int      pico_http_submitData(uint16_t conn, void * buffer, int len);
+int      pico_http_close(uint16_t conn);
+
+#endif /* PICO_HTTP_SERVER_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_http_util.c	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,186 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+Author: Andrei Carp <andrei.carp@tass.be>
+*********************************************************************/
+
+#include <stdint.h>
+#include "pico_config.h"
+#include "pico_stack.h"
+#include "pico_protocol.h"
+#include "pico_http_util.h"
+
+#define TRUE    1
+#define FALSE 0
+
+#define HTTP_PROTO_TOK        "http://"
+#define HTTP_PROTO_LEN        7u
+
+#if defined PICO_SUPPORT_HTTP_CLIENT || defined PICO_SUPPORT_HTTP_SERVER
+
+int pico_itoaHex(uint16_t port, char * ptr)
+{
+    int size = 0;
+    int index;
+
+    // transform to from number to string [ in backwards ]
+    while(port)
+    {
+        ptr[size] = ((port & 0xF) < 10) ? ((port & 0xF) + '0') : ((port & 0xF) - 10 + 'a');
+        port = port>>4u; //divide by 16
+        size++;
+    }
+
+    // invert positions
+    for(index=0 ;index < size>>1u ;index++)
+    {
+        char c = ptr[index];
+        ptr[index] = ptr[size-index-1];
+        ptr[size-index-1] = c;
+    }
+    ptr[size] = '\0';
+    return size;
+}
+
+int pico_itoa(uint16_t port, char * ptr)
+{
+    int size = 0;
+    int index;
+
+    // transform to from number to string [ in backwards ]
+    while(port)
+    {
+        ptr[size] = port%10 + '0';
+        port = port/10;
+        size++;
+    }
+
+    // invert positions
+    for(index=0 ;index < size>>1u ;index++)
+    {
+        char c = ptr[index];
+        ptr[index] = ptr[size-index-1];
+        ptr[size-index-1] = c;
+    }
+    ptr[size] = '\0';
+    return size;
+}
+
+
+int pico_processURI(const char * uri, struct pico_http_uri * urikey)
+{
+
+    uint16_t lastIndex = 0, index;
+
+    if(!uri || !urikey || uri[0] == '/')
+    {
+        pico_err = PICO_ERR_EINVAL;
+        goto error;
+    }
+
+    // detect protocol => search for  "://"
+    if(memcmp(uri,HTTP_PROTO_TOK,HTTP_PROTO_LEN) == 0) // could be optimized
+    { // protocol identified, it is http
+        urikey->protoHttp = TRUE;
+        lastIndex = HTTP_PROTO_LEN;
+    }
+    else
+    {
+        if(strstr(uri,"://")) // different protocol specified
+        {
+            urikey->protoHttp = FALSE;
+            goto error;
+        }
+        // no protocol specified, assuming by default it's http
+        urikey->protoHttp = TRUE;
+    }
+
+    // detect hostname
+    index = lastIndex;
+    while(uri[index] && uri[index]!='/' && uri[index]!=':') index++;
+
+    if(index == lastIndex)
+    {
+        // wrong format
+        urikey->host = urikey->resource = NULL;
+        urikey->port = urikey->protoHttp = 0u;
+
+        goto error;
+    }
+    else
+    {
+        // extract host
+        urikey->host = (char *)pico_zalloc(index-lastIndex+1);
+
+        if(!urikey->host)
+        {
+            // no memory
+            goto error;
+        }
+        memcpy(urikey->host,uri+lastIndex,index-lastIndex);
+    }
+
+    if(!uri[index])
+    {
+        // nothing specified
+        urikey->port = 80u;
+        urikey->resource = pico_zalloc(2u);
+        urikey->resource[0] = '/';
+        return HTTP_RETURN_OK;
+    }
+    else if(uri[index] == '/')
+    {
+        urikey->port = 80u;
+    }
+    else if(uri[index] == ':')
+    {
+        urikey->port = 0u;
+        index++;
+        while(uri[index] && uri[index]!='/')
+        {
+            // should check if every component is a digit
+            urikey->port = urikey->port*10 + (uri[index] - '0');
+            index++;
+        }
+    }
+
+  // extract resource
+    if(!uri[index])
+    {
+        urikey->resource = pico_zalloc(2u);
+        urikey->resource[0] = '/';
+    }
+    else
+    {
+        lastIndex = index;
+        while(uri[index] && uri[index]!='?' && uri[index]!='&' && uri[index]!='#') index++;
+        urikey->resource = (char *)pico_zalloc(index-lastIndex+1);
+
+        if(!urikey->resource)
+        {
+            // no memory
+            pico_err = PICO_ERR_ENOMEM;
+            goto error;
+        }
+
+        memcpy(urikey->resource,uri+lastIndex,index-lastIndex);
+    }
+
+    return HTTP_RETURN_OK;
+
+    error :
+    if(urikey->resource)
+    {
+        pico_free(urikey->resource);
+        urikey->resource = NULL;
+    }
+    if(urikey->host)
+    {
+        pico_free(urikey->host);
+        urikey->host = NULL;
+    }
+
+    return HTTP_RETURN_ERROR;
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_http_util.h	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,117 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+Author: Andrei Carp <andrei.carp@tass.be>
+*********************************************************************/
+
+#ifndef PICO_HTTP_UTIL_H_
+#define PICO_HTTP_UTIL_H_
+
+/* Informational reponses */
+#define HTTP_CONTINUE                         100u
+#define HTTP_SWITCHING_PROTOCOLS  101u
+#define HTTP_PROCESSING                      102u
+
+/* Success */
+#define HTTP_OK                                        200u
+#define HTTP_CREATED                            201u
+#define HTTP_ACCEPTED                            202u
+#define HTTP_NON_AUTH_INFO                203u
+#define HTTP_NO_CONTENT                        204u
+#define HTTP_RESET_CONTENT                205u
+#define HTTP_PARTIAL_CONTENT            206u
+#define HTTP_MULTI_STATUS                    207u
+#define HTTP_ALREADY_REPORTED            208u
+#define HTTP_LOW_SPACE                        250u
+#define HTTP_IM_SPACE                            226u
+
+/* Redirection */
+#define HTTP_MULTI_CHOICE                    300u
+#define HTTP_MOVED_PERMANENT            301u
+#define HTTP_FOUND                                302u
+#define HTTP_SEE_OTHER                        303u
+#define HTTP_NOT_MODIFIED                    304u
+#define HTTP_USE_PROXY                        305u
+#define HTTP_SWITCH_PROXY                    306u
+#define HTTP_TEMP_REDIRECT                307u
+#define HTTP_PERM_REDIRECT                308u
+
+/* Client error */
+#define HTTP_BAD_REQUEST                    400u
+#define HTTP_UNAUTH                                401u
+#define HTTP_PAYMENT_REQ                    402u
+#define HTTP_FORBIDDEN                        403u
+#define HTTP_NOT_FOUND                        404u
+#define HTTP_METH_NOT_ALLOWED            405u
+#define HTTP_NOT_ACCEPTABLE                406u
+#define HTTP_PROXY_AUTH_REQ                407u
+#define HTTP_REQ_TIMEOUT                    408u
+#define HTTP_CONFLICT                            409u
+#define HTTP_GONE                                    410u
+#define HTTP_LEN_REQ                            411u
+#define HTTP_PRECONDITION_FAIL        412u
+#define HTTP_REQ_ENT_LARGE                413u
+#define HTTP_URI_TOO_LONG                    414u
+#define HTTP_UNSUPORTED_MEDIA            415u
+#define HTTP_REQ_RANGE_NOK                416u
+#define HTTP_EXPECT_FAILED                417u
+#define HTTP_TEAPOT                                418u
+#define HTTP_UNPROC_ENTITY                422u
+#define HTTP_LOCKED                                423u
+#define HTTP_METHOD_FAIL                    424u
+#define HTTP_UNORDERED                        425u
+#define HTTP_UPGRADE_REQ                    426u
+#define HTTP_PRECOND_REQ                    428u
+#define HTTP_TOO_MANY_REQ                    429u
+#define HTTP_HEDER_FIELD_LARGE        431u
+
+/* Server error */
+#define HTTP_INTERNAL_SERVER_ERR    500u
+#define HTTP_NOT_IMPLEMENTED            501u
+#define HTTP_BAD_GATEWAY                    502u
+#define HTTP_SERVICE_UNAVAILABLE    503u
+#define HTTP_GATEWAY_TIMEOUT            504u
+#define HTTP_NOT_SUPPORTED                505u
+#define HTTP_SERV_LOW_STORAGE            507u
+#define HTTP_LOOP_DETECTED                508u
+#define HTTP_NOT_EXTENDED                    510u
+#define HTTP_NETWORK_AUTH                    511u
+#define HTTP_PERMISSION_DENIED        550u
+
+/* Returns used  */
+#define HTTP_RETURN_ERROR    -1
+#define HTTP_RETURN_OK                0
+
+/* List of events - shared between client and server */
+#define EV_HTTP_CON            1u
+#define EV_HTTP_REQ       2u
+#define EV_HTTP_PROGRESS  4u
+#define EV_HTTP_SENT          8u
+#define EV_HTTP_CLOSE     16u
+#define EV_HTTP_ERROR     32u
+#define EV_HTTP_BODY            64u
+#define EV_HTTP_DNS                128u
+
+#ifndef TRUE
+    #define TRUE    1
+#endif
+
+#ifndef FALSE
+    #define FALSE 0
+#endif
+
+struct pico_http_uri
+{
+    uint8_t protoHttp; // is the protocol Http ?
+    char * host;             // hostname
+    uint16_t port;         // port if specified
+    char * resource;     // resource , ignoring the other possible parameters
+};
+
+// used for chunks
+int pico_itoaHex(uint16_t port, char * ptr);
+int pico_itoa(uint16_t port, char * ptr);
+int pico_processURI(const char * uri, struct pico_http_uri * urikey);
+
+#endif /* PICO_HTTP_UTIL_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_icmp4.c	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,307 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+.
+
+Authors: Daniele Lacamera
+*********************************************************************/
+
+
+#include "pico_icmp4.h"
+#include "pico_config.h"
+#include "pico_ipv4.h"
+#include "pico_eth.h"
+#include "pico_device.h"
+#include "pico_stack.h"
+#include "pico_tree.h"
+
+/* Queues */
+static struct pico_queue icmp_in = {};
+static struct pico_queue icmp_out = {};
+
+
+/* Functions */
+
+static int pico_icmp4_checksum(struct pico_frame *f)
+{
+  struct pico_icmp4_hdr *hdr = (struct pico_icmp4_hdr *) f->transport_hdr;
+  if (!hdr) {
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+  hdr->crc = 0;
+  hdr->crc = short_be(pico_checksum(hdr, f->transport_len));
+  return 0;
+}
+
+#ifdef PICO_SUPPORT_PING
+static void ping_recv_reply(struct pico_frame *f);
+#endif
+
+static int pico_icmp4_process_in(struct pico_protocol *self, struct pico_frame *f)
+{
+  struct pico_icmp4_hdr *hdr = (struct pico_icmp4_hdr *) f->transport_hdr;
+  if (hdr->type == PICO_ICMP_ECHO) {
+    hdr->type = PICO_ICMP_ECHOREPLY;
+    /* outgoing frames require a f->len without the ethernet header len */
+    if (f->dev->eth)
+      f->len -= PICO_SIZE_ETHHDR;
+    pico_icmp4_checksum(f);
+    pico_ipv4_rebound(f);
+  } else if (hdr->type == PICO_ICMP_UNREACH) {
+    f->net_hdr = f->transport_hdr + PICO_ICMPHDR_UN_SIZE;
+    pico_ipv4_unreachable(f, hdr->code);
+  } else if (hdr->type == PICO_ICMP_ECHOREPLY) {
+#ifdef PICO_SUPPORT_PING
+    ping_recv_reply(f);
+#endif
+    pico_frame_discard(f);
+  } else {
+    pico_frame_discard(f);
+  }
+  return 0;
+}
+
+static int pico_icmp4_process_out(struct pico_protocol *self, struct pico_frame *f)
+{
+  dbg("Called %s\n", __FUNCTION__);
+  return 0;
+}
+
+/* Interface: protocol definition */
+struct pico_protocol pico_proto_icmp4 = {
+  .name = "icmp4",
+  .proto_number = PICO_PROTO_ICMP4,
+  .layer = PICO_LAYER_TRANSPORT,
+  .process_in = pico_icmp4_process_in,
+  .process_out = pico_icmp4_process_out,
+  .q_in = &icmp_in,
+  .q_out = &icmp_out,
+};
+
+static int pico_icmp4_notify(struct pico_frame *f, uint8_t type, uint8_t code)
+{
+  struct pico_frame *reply;
+  struct pico_icmp4_hdr *hdr;
+  struct pico_ipv4_hdr *info;
+  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);
+  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->payload = reply->transport_hdr + PICO_ICMPHDR_UN_SIZE;
+  memcpy(reply->payload, f->net_hdr, 8 + sizeof(struct pico_ipv4_hdr));
+  pico_icmp4_checksum(reply);
+  pico_ipv4_frame_push(reply, &info->src, PICO_PROTO_ICMP4);
+  return 0;
+}
+
+int pico_icmp4_port_unreachable(struct pico_frame *f)
+{
+  /*Parameter check executed in pico_icmp4_notify*/
+  return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_PORT);
+}
+
+int pico_icmp4_proto_unreachable(struct pico_frame *f)
+{
+  /*Parameter check executed in pico_icmp4_notify*/
+  return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_PROTOCOL);
+}
+
+int pico_icmp4_dest_unreachable(struct pico_frame *f)
+{
+  /*Parameter check executed in pico_icmp4_notify*/
+  return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_HOST);
+}
+
+int pico_icmp4_ttl_expired(struct pico_frame *f)
+{
+  /*Parameter check executed in pico_icmp4_notify*/
+  return pico_icmp4_notify(f, PICO_ICMP_TIME_EXCEEDED, PICO_ICMP_TIMXCEED_INTRANS);
+}
+
+int pico_icmp4_packet_filtered(struct pico_frame *f)
+{
+  /*Parameter check executed in pico_icmp4_notify*/
+  /*Packet Filtered: type 3, code 13 (Communication Administratively Prohibited)*/
+  return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_FILTER_PROHIB);
+}
+
+/***********************/
+/* Ping implementation */
+/***********************/
+/***********************/
+/***********************/
+/***********************/
+
+
+#ifdef PICO_SUPPORT_PING
+
+
+struct pico_icmp4_ping_cookie
+{
+  struct pico_ip4 dst;
+  uint16_t err;
+  uint16_t id;
+  uint16_t seq;
+  uint16_t size;
+  int count;
+  unsigned long timestamp;
+  int interval;
+  int timeout;
+  void (*cb)(struct pico_icmp4_stats*);
+
+};
+
+static int cookie_compare(void *ka, void *kb)
+{
+    struct pico_icmp4_ping_cookie *a = ka, *b = kb;
+  if (a->id < b->id)
+    return -1;
+  if (a->id > b->id)
+    return 1;
+  return (a->seq - b->seq);
+}
+
+PICO_TREE_DECLARE(Pings,cookie_compare);
+
+static int pico_icmp4_send_echo(struct pico_icmp4_ping_cookie *cookie)
+{
+  struct pico_frame *echo = pico_proto_ipv4.alloc(&pico_proto_ipv4, PICO_ICMPHDR_UN_SIZE + cookie->size);
+  struct pico_icmp4_hdr *hdr;
+
+  hdr = (struct pico_icmp4_hdr *) echo->transport_hdr;
+
+  hdr->type = PICO_ICMP_ECHO;
+  hdr->code = 0;
+  hdr->hun.ih_idseq.idseq_id = short_be(cookie->id);
+  hdr->hun.ih_idseq.idseq_seq = short_be(cookie->seq);
+  echo->transport_len = PICO_ICMPHDR_UN_SIZE + cookie->size;
+  echo->payload = echo->transport_hdr + PICO_ICMPHDR_UN_SIZE;
+  echo->payload_len = cookie->size;
+  /* XXX: Fill payload */
+  pico_icmp4_checksum(echo);
+  pico_ipv4_frame_push(echo, &cookie->dst, PICO_PROTO_ICMP4);
+  return 0;
+}
+
+
+static void ping_timeout(unsigned long now, void *arg)
+{
+  struct pico_icmp4_ping_cookie *cookie = (struct pico_icmp4_ping_cookie *)arg;
+  if(pico_tree_findKey(&Pings,cookie)){
+    if (cookie->err == PICO_PING_ERR_PENDING) {
+      struct pico_icmp4_stats stats;
+      stats.dst = cookie->dst;
+      stats.seq = cookie->seq;
+      stats.time = 0;
+      stats.size = cookie->size;
+      stats.err = PICO_PING_ERR_TIMEOUT;
+      dbg(" ---- Ping timeout!!!\n");
+      cookie->cb(&stats);
+    }
+
+    pico_tree_delete(&Pings,cookie);
+    pico_free(cookie);
+  }
+}
+
+static void next_ping(unsigned long now, void *arg);
+static inline void send_ping(struct pico_icmp4_ping_cookie *cookie)
+{
+  pico_icmp4_send_echo(cookie);
+  cookie->timestamp = pico_tick;
+  pico_timer_add(cookie->timeout, ping_timeout, cookie);
+  if (cookie->seq < cookie->count)
+    pico_timer_add(cookie->interval, next_ping, cookie);
+}
+
+static void next_ping(unsigned long now, void *arg)
+{
+  struct pico_icmp4_ping_cookie *newcookie, *cookie = (struct pico_icmp4_ping_cookie *)arg;
+
+    if(pico_tree_findKey(&Pings,cookie)){
+    if (cookie->seq < cookie->count) {
+      newcookie = pico_zalloc(sizeof(struct pico_icmp4_ping_cookie));
+      if (!newcookie)
+        return;
+      memcpy(newcookie, cookie, sizeof(struct pico_icmp4_ping_cookie));
+      newcookie->seq++;
+
+        pico_tree_insert(&Pings,newcookie);
+      send_ping(newcookie);
+    }
+  }
+}
+
+
+static void ping_recv_reply(struct pico_frame *f)
+{
+  struct pico_icmp4_ping_cookie test, *cookie;
+  struct pico_icmp4_hdr *hdr = (struct pico_icmp4_hdr *) f->transport_hdr;
+  test.id  = short_be(hdr->hun.ih_idseq.idseq_id );
+  test.seq = short_be(hdr->hun.ih_idseq.idseq_seq);
+
+  cookie = pico_tree_findKey(&Pings, &test);
+  if (cookie) {
+    struct pico_icmp4_stats stats;
+    cookie->err = PICO_PING_ERR_REPLIED;
+    stats.dst = cookie->dst;
+    stats.seq = cookie->seq;
+    stats.size = cookie->size;
+    stats.time = pico_tick - cookie->timestamp;
+    stats.err = cookie->err;
+    stats.ttl = ((struct pico_ipv4_hdr *)f->net_hdr)->ttl;
+        if(cookie->cb != NULL)
+        cookie->cb(&stats);
+  } else {
+    dbg("Reply for seq=%d, not found.\n", test.seq);
+  }
+}
+
+int pico_icmp4_ping(char *dst, int count, int interval, int timeout, int size, void (*cb)(struct pico_icmp4_stats *))
+{
+  static uint16_t next_id = 0x91c0;
+  struct pico_icmp4_ping_cookie *cookie;
+
+  if((dst == NULL) || (interval == 0) || (timeout == 0) || (count == 0)){
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+
+  cookie = pico_zalloc(sizeof(struct pico_icmp4_ping_cookie));
+  if (!cookie) {
+    pico_err = PICO_ERR_ENOMEM;
+    return -1;
+  }
+
+  if (pico_string_to_ipv4(dst, &cookie->dst.addr) < 0) {
+    pico_err = PICO_ERR_EINVAL;
+    pico_free(cookie);
+    return -1;
+  }
+  cookie->seq = 1;
+  cookie->id = next_id++;
+  cookie->err = PICO_PING_ERR_PENDING;
+  cookie->size = size;
+  cookie->interval = interval;
+  cookie->timeout = timeout;
+  cookie->cb = cb;
+  cookie->count = count;
+
+  pico_tree_insert(&Pings,cookie);
+  send_ping(cookie);
+
+  return 0;
+
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_icmp4.h	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,149 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+.
+
+*********************************************************************/
+#ifndef _INCLUDE_PICO_ICMP4
+#define _INCLUDE_PICO_ICMP4
+#include "pico_addressing.h"
+#include "pico_protocol.h"
+
+extern struct pico_protocol pico_proto_icmp4;
+
+struct __attribute__((packed)) pico_icmp4_hdr {
+  uint8_t type;
+  uint8_t code;
+  uint16_t crc;
+
+  /* hun */
+  union {
+    uint8_t ih_pptr;
+    struct pico_ip4 ih_gwaddr;
+    struct {
+      uint16_t idseq_id;
+      uint16_t idseq_seq;
+    } ih_idseq;
+    uint32_t ih_void;
+    struct {
+      uint16_t ipm_void;
+      uint16_t ipm_nmtu;
+    } ih_pmtu;
+    struct {
+      uint8_t rta_numgw;
+      uint8_t rta_wpa;
+      uint16_t rta_lifetime;
+    } ih_rta;
+  } hun;
+
+  /* dun */
+  union {
+    struct {
+      uint32_t ts_otime;
+      uint32_t ts_rtime;
+      uint32_t ts_ttime;
+    } id_ts;
+    struct {
+      uint32_t ip_options;
+      uint32_t ip_data_hi;
+      uint32_t ip_data_lo;
+    } id_ip;
+    struct {
+      uint32_t ira_addr;
+      uint32_t ira_pref;
+    } id_ra;
+    uint32_t id_mask;
+    uint8_t  id_data[1];
+  } dun;
+};
+
+#define PICO_ICMPHDR_DRY_SIZE  4
+#define PICO_ICMPHDR_UN_SIZE  8
+
+#define PICO_ICMP_ECHOREPLY    0 
+#define PICO_ICMP_DEST_UNREACH 3 
+#define PICO_ICMP_SOURCE_QUENCH  4
+#define PICO_ICMP_REDIRECT   5
+#define PICO_ICMP_ECHO   8
+#define PICO_ICMP_TIME_EXCEEDED  11
+#define PICO_ICMP_PARAMETERPROB  12
+#define PICO_ICMP_TIMESTAMP    13
+#define PICO_ICMP_TIMESTAMPREPLY 14
+#define PICO_ICMP_INFO_REQUEST 15
+#define PICO_ICMP_INFO_REPLY   16
+#define PICO_ICMP_ADDRESS    17
+#define PICO_ICMP_ADDRESSREPLY 18
+
+
+#define  PICO_ICMP_UNREACH    3  
+#define  PICO_ICMP_SOURCEQUENCH  4  
+#define  PICO_ICMP_ROUTERADVERT  9  
+#define  PICO_ICMP_ROUTERSOLICIT  10  
+#define  PICO_ICMP_TIMXCEED    11  
+#define  PICO_ICMP_PARAMPROB    12  
+#define  PICO_ICMP_TSTAMP    13  
+#define  PICO_ICMP_TSTAMPREPLY  14  
+#define  PICO_ICMP_IREQ    15  
+#define  PICO_ICMP_IREQREPLY    16    
+#define  PICO_ICMP_MASKREQ    17    
+#define  PICO_ICMP_MASKREPLY    18    
+
+#define  PICO_ICMP_MAXTYPE    18
+
+
+#define  PICO_ICMP_UNREACH_NET          0  
+#define  PICO_ICMP_UNREACH_HOST          1  
+#define  PICO_ICMP_UNREACH_PROTOCOL          2  
+#define  PICO_ICMP_UNREACH_PORT          3  
+#define  PICO_ICMP_UNREACH_NEEDFRAG          4  
+#define  PICO_ICMP_UNREACH_SRCFAIL          5  
+#define  PICO_ICMP_UNREACH_NET_UNKNOWN        6  
+#define  PICO_ICMP_UNREACH_HOST_UNKNOWN       7  
+#define  PICO_ICMP_UNREACH_ISOLATED          8  
+#define  PICO_ICMP_UNREACH_NET_PROHIB          9  
+#define  PICO_ICMP_UNREACH_HOST_PROHIB        10  
+#define  PICO_ICMP_UNREACH_TOSNET          11  
+#define  PICO_ICMP_UNREACH_TOSHOST          12  
+#define  PICO_ICMP_UNREACH_FILTER_PROHIB      13  
+#define  PICO_ICMP_UNREACH_HOST_PRECEDENCE    14  
+#define  PICO_ICMP_UNREACH_PRECEDENCE_CUTOFF  15  
+
+
+#define  PICO_ICMP_REDIRECT_NET  0    
+#define  PICO_ICMP_REDIRECT_HOST  1    
+#define  PICO_ICMP_REDIRECT_TOSNET  2    
+#define  PICO_ICMP_REDIRECT_TOSHOST  3    
+
+
+#define  PICO_ICMP_TIMXCEED_INTRANS  0    
+#define  PICO_ICMP_TIMXCEED_REASS  1    
+
+
+#define  PICO_ICMP_PARAMPROB_OPTABSENT 1    
+
+#define PICO_SIZE_ICMP4HDR ((sizeof(struct pico_icmp4_hdr)))
+
+struct pico_icmp4_stats
+{
+  struct pico_ip4 dst;
+  unsigned long size;
+  unsigned long seq;
+  unsigned long time;
+  unsigned long ttl;
+  int err;
+};
+
+int pico_icmp4_port_unreachable(struct pico_frame *f);
+int pico_icmp4_proto_unreachable(struct pico_frame *f);
+int pico_icmp4_dest_unreachable(struct pico_frame *f);
+int pico_icmp4_ttl_expired(struct pico_frame *f);
+int pico_icmp4_packet_filtered(struct pico_frame *f);
+
+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_PENDING 0xFFFF
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_igmp.c	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,1120 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+RFC 1112, 2236, 3376, 3569, 3678, 4607
+
+Authors: Kristof Roelants (IGMPv3), Simon Maes, Brecht Van Cauwenberghe 
+*********************************************************************/
+
+#include "pico_stack.h"
+#include "pico_ipv4.h"
+#include "pico_igmp.h"
+#include "pico_config.h"
+#include "pico_eth.h"
+#include "pico_addressing.h"
+#include "pico_frame.h"
+#include "pico_tree.h"
+#include "pico_device.h"
+#include "pico_socket.h"
+
+#define igmp_dbg(...) do{}while(0)
+//#define igmp_dbg dbg
+
+/* membership states */
+#define IGMP_STATE_NON_MEMBER             (0x0)
+#define IGMP_STATE_DELAYING_MEMBER        (0x1)
+#define IGMP_STATE_IDLE_MEMBER            (0x2)
+
+/* events */ 
+#define IGMP_EVENT_DELETE_GROUP           (0x0)
+#define IGMP_EVENT_CREATE_GROUP           (0x1)
+#define IGMP_EVENT_UPDATE_GROUP           (0x2)
+#define IGMP_EVENT_QUERY_RECV             (0x3)
+#define IGMP_EVENT_REPORT_RECV            (0x4)
+#define IGMP_EVENT_TIMER_EXPIRED          (0x5)
+
+/* message types */
+#define IGMP_TYPE_MEM_QUERY               (0x11)
+#define IGMP_TYPE_MEM_REPORT_V1           (0x12)
+#define IGMP_TYPE_MEM_REPORT_V2           (0x16)
+#define IGMP_TYPE_LEAVE_GROUP             (0x17)
+#define IGMP_TYPE_MEM_REPORT_V3           (0x22)
+
+/* group record types */
+#define IGMP_MODE_IS_INCLUDE              (1)
+#define IGMP_MODE_IS_EXCLUDE              (2)
+#define IGMP_CHANGE_TO_INCLUDE_MODE       (3)
+#define IGMP_CHANGE_TO_EXCLUDE_MODE       (4)
+#define IGMP_ALLOW_NEW_SOURCES            (5)
+#define IGMP_BLOCK_OLD_SOURCES            (6)
+
+/* host flag */
+#define IGMP_HOST_LAST                    (0x1)
+#define IGMP_HOST_NOT_LAST                (0x0)
+
+/* list of timers, counters and their default values */
+#define IGMP_ROBUSTNESS                   (2)
+#define IGMP_QUERY_INTERVAL               (125) /* secs */
+#define IGMP_QUERY_RESPONSE_INTERVAL      (10) /* secs */
+#define IGMP_STARTUP_QUERY_INTERVAL       (IGMPV3_QUERY_INTERVAL / 4)
+#define IGMP_STARTUP_QUERY_COUNT          (IGMPV3_ROBUSTNESS)
+#define IGMP_LAST_MEMBER_QUERY_INTERVAL   (1) /* secs */
+#define IGMP_LAST_MEMBER_QUERY_COUNT      (IGMPV3_ROBUSTNESS)
+#define IGMP_UNSOLICITED_REPORT_INTERVAL  (1) /* secs */
+#define IGMP_DEFAULT_MAX_RESPONSE_TIME    (100)
+
+/* custom timers types */
+#define IGMP_TIMER_GROUP_REPORT           (1)
+#define IGMP_TIMER_V1_QUERIER             (2)
+#define IGMP_TIMER_V2_QUERIER             (3)
+
+/* IGMP groups */
+#define IGMP_ALL_HOST_GROUP               long_be(0xE0000001) /* 224.0.0.1 */
+#define IGMP_ALL_ROUTER_GROUP             long_be(0xE0000002) /* 224.0.0.2 */
+#define IGMPV3_ALL_ROUTER_GROUP           long_be(0xE0000016) /* 224.0.0.22 */
+
+/* misc */
+#define IGMP_TIMER_STOPPED                (1)
+#define IP_OPTION_ROUTER_ALERT_LEN        (4)
+#define IGMP_MAX_GROUPS                   (32) /* max 255 */
+
+struct __attribute__((packed)) igmp_message {
+  uint8_t type;
+  uint8_t max_resp_time;
+  uint16_t crc;
+  uint32_t mcast_group;
+};
+
+struct __attribute__((packed)) igmpv3_query {
+  uint8_t type;
+  uint8_t max_resp_time;
+  uint16_t crc;
+  uint32_t mcast_group;
+  uint8_t rsq;
+  uint8_t qqic;
+  uint16_t sources;
+  uint32_t source_addr[0];
+};
+
+struct __attribute__((packed)) igmpv3_group_record {
+  uint8_t type;
+  uint8_t aux;
+  uint16_t sources;
+  uint32_t mcast_group;
+  uint32_t source_addr[0];
+};
+
+struct __attribute__((packed)) igmpv3_report {
+  uint8_t type;
+  uint8_t res0;
+  uint16_t crc;
+  uint16_t res1;
+  uint16_t groups;
+  struct igmpv3_group_record record[0];
+};
+
+struct igmp_parameters {
+  uint8_t event;
+  uint8_t state;
+  uint8_t last_host;
+  uint8_t filter_mode;
+  uint8_t max_resp_time;
+  struct pico_ip4 mcast_link;
+  struct pico_ip4 mcast_group;
+  struct pico_tree *MCASTFilter;
+  struct pico_frame *f;
+};
+
+struct igmp_timer {
+  uint8_t type;
+  uint8_t stopped;
+  unsigned long start;
+  unsigned long delay;
+  struct pico_ip4 mcast_link;
+  struct pico_ip4 mcast_group;
+  struct pico_frame *f;
+  void (*callback)(struct igmp_timer *t);
+};
+
+/* queues */
+static struct pico_queue igmp_in = {};
+static struct pico_queue igmp_out = {};
+
+/* finite state machine caller */
+static int pico_igmp_process_event(struct igmp_parameters *p);
+
+/* state callback prototype */
+typedef int (*callback)(struct igmp_parameters *);
+
+/* redblack trees */
+static int igmp_timer_cmp(void *ka, void *kb)
+{
+  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;
+}
+PICO_TREE_DECLARE(IGMPTimers, igmp_timer_cmp);
+
+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;
+  if (a->mcast_link.addr < b->mcast_link.addr)
+    return -1;
+  if (a->mcast_link.addr > b->mcast_link.addr)
+    return 1;
+  return 0;
+}
+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;
+}
+PICO_TREE_DECLARE(IGMPAllow, igmp_sources_cmp);
+PICO_TREE_DECLARE(IGMPBlock, igmp_sources_cmp);
+
+static struct igmp_parameters *pico_igmp_find_parameter(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group)
+{
+  struct igmp_parameters test = {0};
+  test.mcast_link.addr = mcast_link->addr;
+  test.mcast_group.addr = mcast_group->addr;
+  return pico_tree_findKey(&IGMPParameters, &test);
+}
+
+static int pico_igmp_delete_parameter(struct igmp_parameters *p)
+{
+  if (pico_tree_delete(&IGMPParameters, p))
+    pico_free(p);
+  else
+    return -1;
+
+  return 0;
+}
+
+static void pico_igmp_timer_expired(unsigned long now, void *arg)
+{
+  struct igmp_timer *t = NULL, *timer = NULL, test = {0};
+
+  t = (struct igmp_timer *)arg;
+  test.type = t->type;
+  test.mcast_link = t->mcast_link;
+  test.mcast_group = t->mcast_group;
+  igmp_dbg("IGMP: timer expired for %08X link %08X type %u, delay %lu\n", t->mcast_group.addr, t->mcast_link.addr, t->type, t->delay);
+  timer = pico_tree_findKey(&IGMPTimers, &test);
+  if (!timer) {
+    return;
+  }
+  if (timer->stopped == IGMP_TIMER_STOPPED) {
+    pico_free(t);
+    return;
+  }
+  if (timer->start + timer->delay < PICO_TIME_MS()) {
+    pico_tree_delete(&IGMPTimers, timer);
+    if (timer->callback)
+      timer->callback(timer);
+    pico_free(timer);
+  } else {
+    igmp_dbg("IGMP: restart timer for %08X, delay %lu, new delay %lu\n", t->mcast_group.addr, t->delay,  (timer->start + timer->delay) - PICO_TIME_MS());
+    pico_timer_add((timer->start + timer->delay) - PICO_TIME_MS(), &pico_igmp_timer_expired, timer);
+  }
+  return;
+}
+
+static int pico_igmp_timer_reset(struct igmp_timer *t)
+{
+  struct igmp_timer *timer = NULL, test = {0};
+
+  igmp_dbg("IGMP: reset timer for %08X, delay %lu\n", t->mcast_group.addr, t->delay);
+  test.type = t->type;
+  test.mcast_link = t->mcast_link;
+  test.mcast_group = t->mcast_group;
+  timer = pico_tree_findKey(&IGMPTimers, &test);
+  if (!timer)
+    return -1;
+
+  *timer = *t;
+  timer->start = PICO_TIME_MS();
+  return 0;
+}
+
+static int pico_igmp_timer_start(struct igmp_timer *t)
+{
+  struct igmp_timer *timer = NULL, test = {0};
+
+  igmp_dbg("IGMP: start timer for %08X link %08X type %u, delay %lu\n", t->mcast_group.addr, t->mcast_link.addr, t->type, t->delay);
+  test.type = t->type;
+  test.mcast_link = t->mcast_link;
+  test.mcast_group = t->mcast_group;
+  timer = pico_tree_findKey(&IGMPTimers, &test);
+  if (timer)
+    return pico_igmp_timer_reset(t);
+
+  timer = pico_zalloc(sizeof(struct igmp_timer));
+  if (!timer) {
+    pico_err = PICO_ERR_ENOMEM;
+    return -1;
+  }
+  *timer = *t;
+  timer->start = PICO_TIME_MS();
+
+  pico_tree_insert(&IGMPTimers, timer);
+  pico_timer_add(timer->delay, &pico_igmp_timer_expired, timer);
+  return 0;
+}
+
+static int pico_igmp_timer_stop(struct igmp_timer *t)
+{
+  struct igmp_timer *timer = NULL, test = {0};
+
+  test.type = t->type;
+  test.mcast_link = t->mcast_link;
+  test.mcast_group = t->mcast_group;
+  timer = pico_tree_findKey(&IGMPTimers, &test);
+  if (!timer)
+    return 0;
+
+  igmp_dbg("IGMP: stop timer for %08X, delay %lu\n", timer->mcast_group.addr, timer->delay);
+  timer->stopped = IGMP_TIMER_STOPPED;
+  return 0;
+}
+
+static int pico_igmp_timer_is_running(struct igmp_timer *t)
+{
+  struct igmp_timer *timer = NULL, test = {0};
+
+  test.type = t->type;
+  test.mcast_link = t->mcast_link;
+  test.mcast_group = t->mcast_group;
+  timer = pico_tree_findKey(&IGMPTimers, &test);
+  if (timer)
+    return 1;
+  return 0;
+}
+
+static struct igmp_timer *pico_igmp_find_timer(uint8_t type, struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group)
+{
+  struct igmp_timer test = {0};
+
+  test.type = type;
+  test.mcast_link = *mcast_link;
+  test.mcast_group = *mcast_group;
+  return pico_tree_findKey(&IGMPTimers, &test);
+}
+
+static void pico_igmp_report_expired(struct igmp_timer *t)
+{
+  struct igmp_parameters *p = NULL;
+
+  p = pico_igmp_find_parameter(&t->mcast_link, &t->mcast_group);
+  if (!p)
+    return;
+
+  p->event = IGMP_EVENT_TIMER_EXPIRED;
+  pico_igmp_process_event(p);
+}
+
+static void pico_igmp_v2querier_expired(struct igmp_timer *t)
+{
+  struct pico_ipv4_link *link = NULL;
+  struct pico_tree_node *index = NULL, *_tmp = NULL;
+
+  link = pico_ipv4_link_by_dev(t->f->dev);
+  if (!link)
+    return;
+
+  /* When changing compatibility mode, cancel all pending response 
+   * and retransmission timers.
+   */
+  pico_tree_foreach_safe(index, &IGMPTimers, _tmp) 
+  {
+    ((struct igmp_timer *)index->keyValue)->stopped = IGMP_TIMER_STOPPED;
+    pico_tree_delete(&IGMPTimers, index->keyValue);
+  }
+  igmp_dbg("IGMP: switch to compatibility mode IGMPv3\n");
+  link->mcast_compatibility = PICO_IGMPV3;
+  return;
+}
+
+static int pico_igmp_is_checksum_valid(struct pico_frame *f)
+{
+  struct pico_ipv4_hdr *hdr = NULL;
+  uint8_t ihl = 24, datalen = 0;
+
+  hdr = (struct pico_ipv4_hdr *)f->net_hdr;
+  ihl = (hdr->vhl & 0x0F) * 4; /* IHL is in 32bit words */
+  datalen = short_be(hdr->len) - ihl;
+
+  if (short_be(pico_checksum(f->transport_hdr, datalen)) == 0)
+    return 1;
+  igmp_dbg("IGMP: invalid checksum\n");
+  return 0;
+}
+
+/* RFC 3376 $7.1 */
+static int pico_igmp_compatibility_mode(struct pico_frame *f)
+{
+  struct pico_ipv4_hdr *hdr = NULL;
+  struct pico_ipv4_link *link = NULL;
+  struct pico_tree_node *index = NULL, *_tmp = NULL;
+  struct igmp_timer t = {0};
+  uint8_t ihl = 24, datalen = 0;
+
+  link = pico_ipv4_link_by_dev(f->dev);
+  if (!link)
+    return -1;
+
+  hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+  ihl = (hdr->vhl & 0x0F) * 4; /* IHL is in 32bit words */
+  datalen = short_be(hdr->len) - ihl;
+  igmp_dbg("IGMP: IHL = %u, LEN = %u, OCTETS = %u\n", ihl, short_be(hdr->len), datalen);
+
+  if (datalen > 12) {
+    /* IGMPv3 query */
+    t.type = IGMP_TIMER_V2_QUERIER;
+    if (pico_igmp_timer_is_running(&t)) { /* IGMPv2 querier present timer still running */
+      return -1;
+    } else {
+      link->mcast_compatibility = PICO_IGMPV3;
+      return 0;
+    }
+  } else if (datalen == 8) {
+    struct igmp_message *query = (struct igmp_message *)f->transport_hdr;
+    if (query->max_resp_time != 0) {
+      /* IGMPv2 query */
+      /* When changing compatibility mode, cancel all pending response 
+       * and retransmission timers.
+       */
+      pico_tree_foreach_safe(index, &IGMPTimers, _tmp) 
+      {
+        ((struct igmp_timer *)index->keyValue)->stopped = IGMP_TIMER_STOPPED;
+        pico_tree_delete(&IGMPTimers, index->keyValue);
+      }
+      igmp_dbg("IGMP: switch to compatibility mode IGMPv2\n");
+      link->mcast_compatibility = PICO_IGMPV2;
+      t.type = IGMP_TIMER_V2_QUERIER;
+      t.delay = ((IGMP_ROBUSTNESS * link->mcast_last_query_interval) + IGMP_QUERY_RESPONSE_INTERVAL) * 1000;
+      t.f = f;
+      t.callback = pico_igmp_v2querier_expired;
+      /* only one of this type of timer may exist! */
+      pico_igmp_timer_start(&t);
+    } else {
+      /* IGMPv1 query, not supported */
+      return -1;
+    }
+  } else {
+    /* invalid query, silently ignored */
+    return -1;
+  }
+  return 0;
+}
+
+static struct igmp_parameters *pico_igmp_analyse_packet(struct pico_frame *f)
+{
+  struct igmp_message *message = NULL;
+  struct igmp_parameters *p = NULL;
+  struct pico_ipv4_link *link = NULL;
+  struct pico_ip4 mcast_group = {0};
+
+  link = pico_ipv4_link_by_dev(f->dev);
+  if (!link)
+    return NULL;
+
+  /* IGMPv2 and IGMPv3 have a similar structure for the first 8 bytes */ 
+  message = (struct igmp_message *)f->transport_hdr;
+  mcast_group.addr = message->mcast_group;
+  p = pico_igmp_find_parameter(&link->address, &mcast_group);
+  if (!p && mcast_group.addr == 0) { /* general query */
+    p = pico_zalloc(sizeof(struct igmp_parameters));
+    if (!p)
+      return NULL;
+    p->state = IGMP_STATE_NON_MEMBER;
+    p->mcast_link.addr = link->address.addr;
+    p->mcast_group.addr = mcast_group.addr;
+    pico_tree_insert(&IGMPParameters, p);
+  } else if (!p) {
+    return NULL;
+  }
+
+  switch (message->type) {
+    case IGMP_TYPE_MEM_QUERY:
+       p->event = IGMP_EVENT_QUERY_RECV;
+       break;
+    case IGMP_TYPE_MEM_REPORT_V1:
+       p->event = IGMP_EVENT_REPORT_RECV;
+       break;
+    case IGMP_TYPE_MEM_REPORT_V2:
+       p->event = IGMP_EVENT_REPORT_RECV;
+       break;
+    case IGMP_TYPE_MEM_REPORT_V3:
+       p->event = IGMP_EVENT_REPORT_RECV;
+       break;
+    default:
+       return NULL;
+  }
+  p->max_resp_time = message->max_resp_time; /* if IGMPv3 report this will be 0 (res0 field) */
+  p->f = f;
+
+  return p;
+}
+
+static int pico_igmp_process_in(struct pico_protocol *self, struct pico_frame *f)
+{
+  struct igmp_parameters *p = NULL;
+ 
+  if (!pico_igmp_is_checksum_valid(f))
+    goto out;
+  if (pico_igmp_compatibility_mode(f) < 0)
+    goto out;
+  p = pico_igmp_analyse_packet(f);
+  if (!p)
+    goto out;
+
+  return pico_igmp_process_event(p);
+
+  out:
+    pico_frame_discard(f);
+    return 0;
+}
+
+static int pico_igmp_process_out(struct pico_protocol *self, struct pico_frame *f) {
+  /* packets are directly transferred to the IP layer by calling pico_ipv4_frame_push */
+  return 0;
+}
+
+/* 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) 
+{
+  struct igmp_parameters *p = NULL;
+  
+  if (mcast_group->addr == IGMP_ALL_HOST_GROUP)
+    return 0;
+
+  p = pico_igmp_find_parameter(mcast_link, mcast_group);
+  if (!p && state == PICO_IGMP_STATE_CREATE) {
+    p = pico_zalloc(sizeof(struct igmp_parameters));
+    if (!p) {
+      pico_err = PICO_ERR_ENOMEM;
+      return -1;
+    }
+    p->state = IGMP_STATE_NON_MEMBER;
+    p->mcast_link = *mcast_link;
+    p->mcast_group = *mcast_group;
+    pico_tree_insert(&IGMPParameters, p);
+  } else if (!p) {
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+
+  switch (state) {
+    case PICO_IGMP_STATE_CREATE:
+      p->event = IGMP_EVENT_CREATE_GROUP;
+      break;
+
+    case PICO_IGMP_STATE_UPDATE:
+      p->event = IGMP_EVENT_UPDATE_GROUP;
+      break;
+    
+    case PICO_IGMP_STATE_DELETE:
+      p->event = IGMP_EVENT_DELETE_GROUP;
+      break;
+
+    default:
+      return -1;
+  }
+  p->filter_mode = filter_mode;
+  p->MCASTFilter = MCASTFilter;
+
+  return pico_igmp_process_event(p);
+}
+
+static int pico_igmp_send_report(struct igmp_parameters *p, struct pico_frame *f)
+{
+  struct pico_ip4 dst = {0};
+  struct pico_ip4 mcast_group = {0};
+  struct pico_ipv4_link *link = NULL;
+  
+  link = pico_ipv4_link_get(&p->mcast_link);
+  if (!link)
+    return -1;
+
+  mcast_group.addr = p->mcast_group.addr;
+  switch (link->mcast_compatibility) {
+    case PICO_IGMPV2:
+      if (p->event == IGMP_EVENT_DELETE_GROUP)
+        dst.addr = IGMP_ALL_ROUTER_GROUP;
+      else
+        dst.addr = mcast_group.addr;
+      break;
+
+    case PICO_IGMPV3:
+      dst.addr = IGMPV3_ALL_ROUTER_GROUP;
+      break;
+
+    default:
+      pico_err = PICO_ERR_EPROTONOSUPPORT;
+      return -1;
+  }
+
+  igmp_dbg("IGMP: send membership report on group %08X to %08X\n", mcast_group.addr, dst.addr);
+  pico_ipv4_frame_push(f, &dst, PICO_PROTO_IGMP);
+  return 0;
+}
+
+static int pico_igmp_generate_report(struct igmp_parameters *p)
+{
+  struct pico_ipv4_link *link = NULL;
+  int i = 0;
+
+  link = pico_ipv4_link_get(&p->mcast_link);
+  if (!link) {
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+
+  switch (link->mcast_compatibility) {
+    case PICO_IGMPV1:
+      pico_err = PICO_ERR_EPROTONOSUPPORT;
+      return -1;
+      
+    case PICO_IGMPV2:
+    {
+      struct igmp_message *report = NULL;
+      uint8_t report_type = IGMP_TYPE_MEM_REPORT_V2;
+      if (p->event == IGMP_EVENT_DELETE_GROUP)
+        report_type = IGMP_TYPE_LEAVE_GROUP;
+
+      p->f = pico_proto_ipv4.alloc(&pico_proto_ipv4, IP_OPTION_ROUTER_ALERT_LEN + sizeof(struct igmp_message));
+      p->f->net_len += IP_OPTION_ROUTER_ALERT_LEN;
+      p->f->transport_hdr += IP_OPTION_ROUTER_ALERT_LEN;
+      p->f->transport_len -= IP_OPTION_ROUTER_ALERT_LEN;
+      p->f->dev = pico_ipv4_link_find(&p->mcast_link);
+      /* p->f->len is correctly set by alloc */
+
+      report = (struct igmp_message *)p->f->transport_hdr;
+      report->type = report_type;
+      report->max_resp_time = IGMP_DEFAULT_MAX_RESPONSE_TIME;
+      report->mcast_group = p->mcast_group.addr;
+
+      report->crc = 0;
+      report->crc = short_be(pico_checksum(report, sizeof(struct igmp_message)));
+      break;
+    }
+    case PICO_IGMPV3:
+    {
+      struct igmpv3_report *report = NULL;
+      struct igmpv3_group_record *record = NULL;
+      struct pico_mcast_group *g = NULL, test = {0};
+      struct pico_tree_node *index = NULL, *_tmp = NULL;
+      struct pico_tree *IGMPFilter = NULL;
+      struct pico_ip4 *source = NULL;
+      uint8_t record_type = 0;
+      uint8_t sources = 0;
+      int len = 0;
+
+      test.mcast_addr = p->mcast_group;
+      g = pico_tree_findKey(link->MCASTGroups, &test);
+      if (!g) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+      }
+
+      if (p->event == IGMP_EVENT_DELETE_GROUP) { /* "non-existent" state of filter mode INCLUDE and empty source list */
+        p->filter_mode = PICO_IP_MULTICAST_INCLUDE;
+        p->MCASTFilter = NULL;
+      }
+
+      /* cleanup filters */
+      pico_tree_foreach_safe(index, &IGMPAllow, _tmp) 
+      {
+        pico_tree_delete(&IGMPAllow, index->keyValue);
+      }
+      pico_tree_foreach_safe(index, &IGMPBlock, _tmp) 
+      {
+        pico_tree_delete(&IGMPBlock, index->keyValue);
+      }
+
+      switch (g->filter_mode) {
+
+        case PICO_IP_MULTICAST_INCLUDE:
+          switch (p->filter_mode) {
+            case PICO_IP_MULTICAST_INCLUDE:
+              if (p->event == IGMP_EVENT_DELETE_GROUP) { /* all ADD_SOURCE_MEMBERSHIP had an equivalent DROP_SOURCE_MEMBERSHIP */
+                /* TO_IN (B) */
+                record_type = IGMP_CHANGE_TO_INCLUDE_MODE;
+                IGMPFilter = &IGMPAllow;
+                if (p->MCASTFilter) {
+                  pico_tree_foreach(index, p->MCASTFilter) /* B */
+                  {
+                    pico_tree_insert(&IGMPAllow, index->keyValue);
+                    sources++;
+                  }
+                } /* else { IGMPAllow stays empty } */
+                break;
+              }
+
+              /* ALLOW (B-A) */
+              /* if event is CREATE A will be empty, thus only ALLOW (B-A) has sense */
+              if (p->event == IGMP_EVENT_CREATE_GROUP) /* first ADD_SOURCE_MEMBERSHIP */
+                record_type = IGMP_CHANGE_TO_INCLUDE_MODE;
+              else
+                record_type = IGMP_ALLOW_NEW_SOURCES;
+              IGMPFilter = &IGMPAllow;
+              pico_tree_foreach(index, p->MCASTFilter) /* B */
+              {
+                pico_tree_insert(&IGMPAllow, index->keyValue);
+                sources++;
+              }
+              pico_tree_foreach(index, &g->MCASTSources) /* A */
+              {
+                source = pico_tree_findKey(&IGMPAllow, index->keyValue);
+                if (source) {
+                  pico_tree_delete(&IGMPAllow, source);
+                  sources--;
+                }
+              }
+              if (!pico_tree_empty(&IGMPAllow)) /* record type is ALLOW */
+                break;
+
+              /* BLOCK (A-B) */
+              record_type = IGMP_BLOCK_OLD_SOURCES;
+              IGMPFilter = &IGMPBlock;
+              pico_tree_foreach(index, &g->MCASTSources) /* A */
+              {
+                pico_tree_insert(&IGMPBlock, index->keyValue);
+                sources++;
+              }
+              pico_tree_foreach(index, p->MCASTFilter) /* B */
+              {
+                source = pico_tree_findKey(&IGMPBlock, index->keyValue);
+                if (source) {
+                  pico_tree_delete(&IGMPBlock, source);
+                  sources--;
+                }
+              }
+              if (!pico_tree_empty(&IGMPBlock)) /* record type is BLOCK */
+                break;
+
+              /* ALLOW (B-A) and BLOCK (A-B) are empty: do not send report (RFC 3376 $5.1) */
+              p->f = NULL;
+              return 0;
+
+            case PICO_IP_MULTICAST_EXCLUDE:
+              /* TO_EX (B) */
+              record_type = IGMP_CHANGE_TO_EXCLUDE_MODE;
+              IGMPFilter = &IGMPBlock;
+              pico_tree_foreach(index, p->MCASTFilter) /* B */
+              {
+                pico_tree_insert(&IGMPBlock, index->keyValue);
+                sources++;
+              }
+              break;
+
+            default:
+              pico_err = PICO_ERR_EINVAL;
+              return -1;
+          }
+          break;
+
+        case PICO_IP_MULTICAST_EXCLUDE:
+          switch (p->filter_mode) {
+            case PICO_IP_MULTICAST_INCLUDE:
+              /* TO_IN (B) */
+              record_type = IGMP_CHANGE_TO_INCLUDE_MODE;
+              IGMPFilter = &IGMPAllow;
+              if (p->MCASTFilter) {
+                pico_tree_foreach(index, p->MCASTFilter) /* B */
+                {
+                  pico_tree_insert(&IGMPAllow, index->keyValue);
+                  sources++;
+                }
+              } /* else { IGMPAllow stays empty } */
+              break;
+
+            case PICO_IP_MULTICAST_EXCLUDE:
+              /* BLOCK (B-A) */
+              record_type = IGMP_BLOCK_OLD_SOURCES;
+              IGMPFilter = &IGMPBlock;
+              pico_tree_foreach(index, p->MCASTFilter)
+              {
+                pico_tree_insert(&IGMPBlock, index->keyValue);
+                sources++;
+              }
+              pico_tree_foreach(index, &g->MCASTSources) /* A */
+              {
+                source = pico_tree_findKey(&IGMPBlock, index->keyValue); /* B */
+                if (source) {
+                  pico_tree_delete(&IGMPBlock, source);
+                  sources--;
+                }
+              }
+              if (!pico_tree_empty(&IGMPBlock)) /* record type is BLOCK */
+                break;
+
+              /* ALLOW (A-B) */
+              record_type = IGMP_ALLOW_NEW_SOURCES;
+              IGMPFilter = &IGMPAllow;
+              pico_tree_foreach(index, &g->MCASTSources)
+              {
+                pico_tree_insert(&IGMPAllow, index->keyValue);
+                sources++;
+              }
+              pico_tree_foreach(index, p->MCASTFilter) /* B */
+              {
+                source = pico_tree_findKey(&IGMPAllow, index->keyValue); /* A */
+                if (source) {
+                  pico_tree_delete(&IGMPAllow, source);
+                  sources--;
+                }
+              }
+              if (!pico_tree_empty(&IGMPAllow)) /* record type is ALLOW */
+                break;
+
+              /* BLOCK (B-A) and ALLOW (A-B) are empty: do not send report (RFC 3376 $5.1) */
+              p->f = NULL;
+              return 0;
+
+            default:
+              pico_err = PICO_ERR_EINVAL;
+              return -1;
+          }
+          break;
+
+        default:
+          pico_err = PICO_ERR_EINVAL;
+          return -1;
+      }
+
+      len = sizeof(struct igmpv3_report) + sizeof(struct igmpv3_group_record) + (sources * sizeof(struct pico_ip4));
+      p->f = pico_proto_ipv4.alloc(&pico_proto_ipv4, IP_OPTION_ROUTER_ALERT_LEN + len);
+      p->f->net_len += IP_OPTION_ROUTER_ALERT_LEN;
+      p->f->transport_hdr += IP_OPTION_ROUTER_ALERT_LEN;
+      p->f->transport_len -= IP_OPTION_ROUTER_ALERT_LEN;
+      p->f->dev = pico_ipv4_link_find(&p->mcast_link);
+      /* p->f->len is correctly set by alloc */
+
+      report = (struct igmpv3_report *)p->f->transport_hdr;
+      report->type = IGMP_TYPE_MEM_REPORT_V3;
+      report->res0 = 0;
+      report->crc = 0;
+      report->res1 = 0;
+      report->groups = short_be(1);
+
+      record = &report->record[0];
+      record->type = record_type;
+      record->aux = 0;
+      record->sources = short_be(sources);
+      record->mcast_group = p->mcast_group.addr;
+      if (!pico_tree_empty(IGMPFilter)) {
+        i = 0;
+        pico_tree_foreach(index, IGMPFilter)
+        {
+          record->source_addr[i] = ((struct pico_ip4 *)index->keyValue)->addr;
+          i++;
+        }
+      }
+      report->crc = short_be(pico_checksum(report, len));
+      break;
+    }
+
+    default:
+      pico_err = PICO_ERR_EINVAL;
+      return -1;
+  }
+  return 0;
+}
+
+/* stop timer, send leave if flag set */
+static int stslifs(struct igmp_parameters *p)
+{
+  struct igmp_timer t = {0};
+
+  igmp_dbg("IGMP: event = leave group | action = stop timer, send leave if flag set\n");
+
+  t.type = IGMP_TIMER_GROUP_REPORT;
+  t.mcast_link = p->mcast_link;
+  t.mcast_group = p->mcast_group;
+  if (pico_igmp_timer_stop(&t) < 0)
+    return -1;
+
+  /* always send leave, even if not last host */
+  if (pico_igmp_send_report(p, p->f) < 0)
+    return -1;
+
+  pico_igmp_delete_parameter(p);
+  igmp_dbg("IGMP: new state = non-member\n");
+  return 0;
+}
+
+/* send report, set flag, start timer */
+static int srsfst(struct igmp_parameters *p)
+{
+  struct igmp_timer t = {0};
+  struct pico_frame *copy_frame = NULL;
+
+  igmp_dbg("IGMP: event = join group | action = send report, set flag, start timer\n");
+
+  p->last_host = IGMP_HOST_LAST;
+
+  if (pico_igmp_generate_report(p) < 0)
+    return -1;
+  if (!p->f)
+    return 0;
+  copy_frame = pico_frame_copy(p->f);
+  if (!copy_frame) {
+    pico_err = PICO_ERR_ENOMEM;
+    return -1;
+  }
+  if (pico_igmp_send_report(p, copy_frame) < 0)
+    return -1;
+
+  t.type = IGMP_TIMER_GROUP_REPORT;
+  t.mcast_link = p->mcast_link;
+  t.mcast_group = p->mcast_group;
+  t.delay = (pico_rand() % (IGMP_UNSOLICITED_REPORT_INTERVAL * 10000)); 
+  t.f = p->f;
+  t.callback = pico_igmp_report_expired;
+  pico_igmp_timer_start(&t);
+
+  p->state = IGMP_STATE_DELAYING_MEMBER;
+  igmp_dbg("IGMP: new state = delaying member\n");
+  return 0;
+}
+
+/* merge report, send report, reset timer (IGMPv3 only) */
+static int mrsrrt(struct igmp_parameters *p)
+{
+  struct igmp_timer *t = NULL;
+  struct pico_frame *copy_frame = NULL;
+  struct pico_ipv4_link *link = NULL;
+
+  igmp_dbg("IGMP: event = update group | action = merge report, send report, reset timer (IGMPv3 only)\n");
+
+  link = pico_ipv4_link_get(&p->mcast_link);
+  if (!link)
+    return -1;
+
+  if (link->mcast_compatibility != PICO_IGMPV3) {
+    igmp_dbg("IGMP: no IGMPv3 compatible router on network\n");
+    return -1;
+  }
+
+  /* XXX: merge with pending report rfc 3376 $5.1 */
+
+  copy_frame = pico_frame_copy(p->f);
+  if (!copy_frame)
+    return -1;
+  if (pico_igmp_send_report(p, copy_frame) < 0)
+    return -1;
+
+  t = pico_igmp_find_timer(IGMP_TIMER_GROUP_REPORT, &p->mcast_link, &p->mcast_group);
+  if (!t)
+    return -1;
+  t->delay = (pico_rand() % (IGMP_UNSOLICITED_REPORT_INTERVAL * 10000)); 
+  pico_igmp_timer_reset(t);
+
+  p->state = IGMP_STATE_DELAYING_MEMBER;
+  igmp_dbg("IGMP: new state = delaying member\n");
+  return 0;
+}
+
+/* send report, start timer (IGMPv3 only) */
+static int srst(struct igmp_parameters *p)
+{
+  struct igmp_timer t = {0};
+  struct pico_frame *copy_frame = NULL;
+  struct pico_ipv4_link *link = NULL;
+
+  igmp_dbg("IGMP: event = update group | action = send report, start timer (IGMPv3 only)\n");
+
+  link = pico_ipv4_link_get(&p->mcast_link);
+  if (!link)
+    return -1;
+
+  if (link->mcast_compatibility != PICO_IGMPV3) {
+    igmp_dbg("IGMP: no IGMPv3 compatible router on network\n");
+    return -1;
+  }
+
+  if (pico_igmp_generate_report(p) < 0)
+    return -1;
+  if (!p->f)
+    return 0;
+  copy_frame = pico_frame_copy(p->f);
+  if (!copy_frame)
+    return -1;
+  if (pico_igmp_send_report(p, copy_frame) < 0)
+    return -1;
+
+  t.type = IGMP_TIMER_GROUP_REPORT;
+  t.mcast_link = p->mcast_link;
+  t.mcast_group = p->mcast_group;
+  t.delay = (pico_rand() % (IGMP_UNSOLICITED_REPORT_INTERVAL * 10000)); 
+  t.f = p->f;
+  t.callback = pico_igmp_report_expired;
+  pico_igmp_timer_start(&t);
+
+  p->state = IGMP_STATE_DELAYING_MEMBER;
+  igmp_dbg("IGMP: new state = delaying member\n");
+  return 0;
+}
+
+/* send leave if flag set */
+static int slifs(struct igmp_parameters *p)
+{
+  igmp_dbg("IGMP: event = leave group | action = send leave if flag set\n");
+
+  /* always send leave, even if not last host */
+  if (pico_igmp_send_report(p, p->f) < 0)
+    return -1;
+
+  pico_igmp_delete_parameter(p);
+  igmp_dbg("IGMP: new state = non-member\n");
+  return 0;
+}
+
+/* start timer */
+static int st(struct igmp_parameters *p)
+{
+  struct igmp_timer t = {0};
+
+  igmp_dbg("IGMP: event = query received | action = start timer\n");
+
+  if (pico_igmp_generate_report(p) < 0)
+    return -1;
+  if (!p->f)
+    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 * 100)); 
+  t.f = p->f;
+  t.callback = pico_igmp_report_expired;
+  pico_igmp_timer_start(&t);
+
+  p->state = IGMP_STATE_DELAYING_MEMBER;
+  igmp_dbg("IGMP: new state = delaying member\n");
+  return 0;
+}
+
+/* stop timer, clear flag */
+static int stcl(struct igmp_parameters *p)
+{
+  struct igmp_timer t = {0};
+
+  igmp_dbg("IGMP: event = report received | action = stop timer, clear flag\n");
+
+  t.type = IGMP_TIMER_GROUP_REPORT;
+  t.mcast_link = p->mcast_link;
+  t.mcast_group = p->mcast_group;
+  if (pico_igmp_timer_stop(&t) < 0)
+    return -1;
+
+  p->last_host = IGMP_HOST_NOT_LAST;
+  p->state = IGMP_STATE_IDLE_MEMBER;
+  igmp_dbg("IGMP: new state = idle member\n");
+  return 0;
+}
+
+/* send report, set flag */
+static int srsf(struct igmp_parameters *p)
+{
+  igmp_dbg("IGMP: event = timer expired | action = send report, set flag\n");
+
+  if (pico_igmp_send_report(p, p->f) < 0)
+    return -1;
+
+  p->state = IGMP_STATE_IDLE_MEMBER;
+  igmp_dbg("IGMP: new state = idle member\n"); 
+  return 0;
+}
+
+/* reset timer if max response time < current timer */
+static int rtimrtct(struct igmp_parameters *p)
+{
+  struct igmp_timer *t = NULL;
+  unsigned long time_to_run = 0;
+
+  igmp_dbg("IGMP: event = query received | action = reset timer if max response time < current timer\n");
+
+  t = pico_igmp_find_timer(IGMP_TIMER_GROUP_REPORT, &p->mcast_link, &p->mcast_group);
+  if (!t)
+    return -1;
+
+  time_to_run = t->start + t->delay - PICO_TIME_MS();
+  if ((p->max_resp_time * 100) < time_to_run) { /* max_resp_time in units of 1/10 seconds */
+    t->delay = pico_rand() % (p->max_resp_time * 100); 
+    pico_igmp_timer_reset(t);
+  }
+
+  p->state = IGMP_STATE_DELAYING_MEMBER;
+  igmp_dbg("IGMP: new state = delaying member\n"); 
+  return 0;
+}
+
+static int discard(struct igmp_parameters *p){
+  igmp_dbg("IGMP: ignore and discard frame\n");
+  pico_frame_discard(p->f);
+  return 0;
+}
+
+/* finite state machine table */
+const callback host_membership_diagram_table[3][6] =
+{ /* event                    |Delete Group  |Create Group |Update Group |Query Received  |Report Received  |Timer Expired */
+/* state Non-Member      */ { discard,       srsfst,       srsfst,       discard,         discard,          discard },
+/* state Delaying Member */ { stslifs,       mrsrrt,       mrsrrt,       rtimrtct,        stcl,             srsf    },
+/* state Idle Member     */ { slifs,         srst,         srst,         st,              discard,          discard }
+};
+
+static int pico_igmp_process_event(struct igmp_parameters *p)
+{
+  struct pico_tree_node *index = NULL;
+  struct igmp_parameters *_p = NULL;
+
+  igmp_dbg("IGMP: process event on group address %08X\n", p->mcast_group.addr);
+  if (p->event == IGMP_EVENT_QUERY_RECV && p->mcast_group.addr == 0) { /* general query */
+    pico_tree_foreach(index, &IGMPParameters) {
+      _p = index->keyValue;
+      _p->max_resp_time = p->max_resp_time;
+      _p->event = IGMP_EVENT_QUERY_RECV;
+      igmp_dbg("IGMP: for each mcast_group = %08X | state = %u\n", _p->mcast_group.addr, _p->state);
+      host_membership_diagram_table[_p->state][_p->event](_p);
+    }
+  } else {
+    igmp_dbg("IGMP: state = %u (0: non-member - 1: delaying member - 2: idle member)\n", p->state); 
+    host_membership_diagram_table[p->state][p->event](p);
+  }
+  return 0;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_igmp.h	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,26 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+.
+
+Authors: Kristof Roelants, Simon Maes, Brecht Van Cauwenberghe
+*********************************************************************/
+
+#ifndef _INCLUDE_PICO_IGMP
+#define _INCLUDE_PICO_IGMP
+
+#define PICO_IGMPV1               1
+#define PICO_IGMPV2               2
+#define PICO_IGMPV3               3
+
+#define PICO_IGMP_STATE_CREATE    1
+#define PICO_IGMP_STATE_UPDATE    2
+#define PICO_IGMP_STATE_DELETE    3
+
+#define PICO_IGMP_QUERY_INTERVAL  125
+
+extern struct pico_protocol pico_proto_igmp;
+
+int pico_igmp_state_change(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t filter_mode, struct pico_tree *MCASTFilter, uint8_t state);
+#endif /* _INCLUDE_PICO_IGMP */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_ipfilter.c	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,267 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+Authors: Simon Maes
+*********************************************************************/
+
+#include "pico_ipv4.h"
+#include "pico_config.h"
+#include "pico_icmp4.h"
+#include "pico_stack.h"
+#include "pico_eth.h"
+#include "pico_socket.h"
+#include "pico_device.h"
+#include "pico_ipfilter.h"
+#include "pico_tcp.h"
+#include "pico_udp.h"
+
+
+//#define ipf_dbg dbg
+#define ipf_dbg(...) do{}while(0)
+
+struct filter_node;
+typedef int (*func_pntr)(struct filter_node *filter, struct pico_frame *f);
+
+struct filter_node {
+  struct pico_device *fdev;
+  struct filter_node *next_filter;
+  uint32_t out_addr;
+  uint32_t out_addr_netmask;
+  uint32_t in_addr;
+  uint32_t in_addr_netmask;
+  uint16_t out_port;
+  uint16_t in_port;
+  uint8_t proto;
+  int8_t priority;
+  uint8_t tos;
+  uint8_t filter_id;
+  func_pntr function_ptr;
+};
+
+static struct filter_node *head = NULL;
+static struct filter_node *tail = NULL;
+
+/*======================== FUNCTION PNTRS ==========================*/
+
+static int fp_accept(struct filter_node *filter, struct pico_frame *f) {return 0;}
+
+static int fp_priority(struct filter_node *filter, struct pico_frame *f) {
+
+  //TODO do priority-stuff
+  return 0;
+}
+
+static int fp_reject(struct filter_node *filter, struct pico_frame *f) {
+// TODO check first if sender is pico itself or not
+  ipf_dbg("ipfilter> #reject\n");
+  pico_icmp4_packet_filtered(f);
+  pico_frame_discard(f);
+  return 1;
+}
+
+static int fp_drop(struct filter_node *filter, struct pico_frame *f) {
+
+  ipf_dbg("ipfilter> # drop\n");
+  pico_frame_discard(f);
+  return 1;
+}
+
+/*============================ API CALLS ============================*/
+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)
+{
+  static uint8_t filter_id = 0;
+  struct filter_node *new_filter;
+
+  if ( !(dev != NULL || proto != 0 || (out_addr != NULL && out_addr->addr != 0U) || (out_addr_netmask != NULL && out_addr_netmask->addr != 0U)|| (in_addr != NULL && in_addr->addr != 0U) || (in_addr_netmask != NULL && in_addr_netmask->addr != 0U)|| out_port != 0 || in_port !=0 || tos != 0 )) {
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+  if ( priority > 10 || priority < -10) {
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+  if (action > 3 || action < 0) {
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+  ipf_dbg("ipfilter> # adding filter\n");
+
+  new_filter = pico_zalloc(sizeof(struct filter_node));
+  if (!head) {
+    head = tail = new_filter;
+  } else {
+    tail->next_filter = new_filter;
+    tail = new_filter;
+  }
+
+  new_filter->fdev = dev;
+  new_filter->proto = proto;
+  if (out_addr != NULL)
+    new_filter->out_addr = out_addr->addr;
+  else
+    new_filter->out_addr = 0U;
+
+  if (out_addr_netmask != NULL)
+    new_filter->out_addr_netmask = out_addr_netmask->addr;
+  else
+    new_filter->out_addr_netmask = 0U;
+
+  if (in_addr != NULL)
+    new_filter->in_addr = in_addr->addr;
+  else
+    new_filter->in_addr = 0U;
+ 
+  if (in_addr_netmask != NULL)
+    new_filter->in_addr_netmask = in_addr_netmask->addr;
+  else
+    new_filter->in_addr_netmask = 0U;
+
+  new_filter->out_port = out_port;
+  new_filter->in_port = in_port;
+  new_filter->priority = priority;
+  new_filter->tos = tos;
+  new_filter->filter_id = filter_id++;
+
+  /*Define filterType_functionPointer here instead of in ipfilter-function, to prevent running multiple times through switch*/
+  switch (action) {
+    case FILTER_ACCEPT:
+      new_filter->function_ptr = fp_accept;
+      break;
+    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;
+  }
+  return new_filter->filter_id;
+}
+
+int pico_ipv4_filter_del(uint8_t filter_id)
+{
+  struct filter_node *work;
+  struct filter_node *prev;
+
+  if (!tail || !head) {
+    pico_err = PICO_ERR_EPERM;
+    return -1;
+  }
+
+  work = head;
+  if (work->filter_id == filter_id) {
+      /*delete filter_node from linked list*/
+      head = work->next_filter;
+      pico_free(work);
+      return 0;
+  }
+  prev = work;
+  work = work->next_filter;
+
+  while (1) {
+    if (work->filter_id == filter_id) {
+        if (work != tail) {
+        /*delete filter_node from linked list*/
+        prev->next_filter = work->next_filter;
+        pico_free(work);
+        return 0;
+        } else {
+          prev->next_filter = NULL;
+          pico_free(work);
+          return 0;
+        }
+    } else {
+      /*check next filter_node*/
+      prev = work;
+      work = work->next_filter;
+      if (work == tail) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+      }
+    }
+  }
+}
+
+/*================================== CORE FILTER FUNCTIONS ==================================*/
+int match_filter(struct filter_node *filter, struct pico_frame *f)
+{
+  struct filter_node temp;
+  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;
+
+  if (!filter|| !f) {
+    ipf_dbg("ipfilter> ## nullpointer in match filter \n");
+    return -1;
+  }
+
+  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);
+  } else {
+    temp.out_port = temp.in_port = 0;
+  }
+  temp.proto = ipv4_hdr->proto;
+  temp.priority = f->priority;
+  temp.tos = ipv4_hdr->tos;
+
+
+
+  if ( ((filter->fdev == NULL || filter->fdev == temp.fdev) && \
+        (filter->in_addr == 0 || ((filter->in_addr_netmask == 0) ? (filter->in_addr == temp.in_addr) : 1)) &&\
+        (filter->in_port == 0 || filter->in_port == temp.in_port) &&\
+        (filter->out_addr == 0 || ((filter->out_addr_netmask == 0) ? (filter->out_addr == temp.out_addr) : 1)) && \
+        (filter->out_port == 0 || filter->out_port == temp.out_port)  && \
+        (filter->proto == 0 || filter->proto == temp.proto ) &&\
+        (filter->priority == 0 || filter->priority == temp.priority ) &&\
+        (filter->tos == 0 || filter->tos == temp.tos ) &&\
+        (filter->out_addr_netmask == 0 || ((filter->out_addr & filter->out_addr_netmask) == (temp.out_addr & filter->out_addr_netmask)) ) &&\
+        (filter->in_addr_netmask == 0 || ((filter->in_addr & filter->in_addr_netmask) == (temp.in_addr & filter->in_addr_netmask)) )\
+       ) ) 
+    return 0;
+
+  //No filter match!
+  ipf_dbg("ipfilter> #no match\n");
+  return 1;
+}
+
+int ipfilter(struct pico_frame *f)
+{
+  struct filter_node *work = head;
+
+  /*return 1 if pico_frame is discarded as result of the filtering, 0 for an incomming packet, -1 for faults*/
+  if (!tail || !head)  {
+    return 0;
+  }
+
+  if ( match_filter(work, f) == 0 ) { 
+    ipf_dbg("ipfilter> # ipfilter match\n");
+    /*filter match, execute filter!*/
+    return work->function_ptr(work, f);
+  } 
+  while (tail != work) {
+    ipf_dbg("ipfilter> next filter..\n");
+    work = work->next_filter;
+    if ( match_filter(work, f) == 0 ) {
+      ipf_dbg("ipfilter> # ipfilter match\n");
+      /*filter match, execute filter!*/
+      return work->function_ptr(work, f);
+    }
+  }
+  return 0;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_ipfilter.h	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,31 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+Authors: Simon Maes
+*********************************************************************/
+#ifndef _INCLUDE_PICO_IPFILTER
+#define _INCLUDE_PICO_IPFILTER
+
+#include "pico_device.h"
+
+enum filter_action {
+  FILTER_ACCEPT = 0,
+  FILTER_PRIORITY,
+  FILTER_REJECT,
+  FILTER_DROP,
+};
+
+
+
+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);
+
+int pico_ipv4_filter_del(uint8_t filter_id);
+
+int ipfilter(struct pico_frame *f);
+
+#endif /* _INCLUDE_PICO_IPFILTER */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_ipv4.c	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,1418 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+Authors: Daniele Lacamera, Markian Yskout
+*********************************************************************/
+
+
+#include "pico_config.h"
+#include "pico_ipfilter.h"
+#include "pico_ipv4.h"
+#include "pico_icmp4.h"
+#include "pico_stack.h"
+#include "pico_eth.h"
+#include "pico_udp.h"
+#include "pico_tcp.h"
+#include "pico_socket.h"
+#include "pico_device.h"
+#include "pico_nat.h"
+#include "pico_igmp.h"
+#include "pico_tree.h"
+
+#ifdef PICO_SUPPORT_IPV4
+
+#ifdef PICO_SUPPORT_MCAST
+# define ip_mcast_dbg(...) do{}while(0) /* so_mcast_dbg in pico_socket.c */
+# define PICO_MCAST_ALL_HOSTS long_be(0xE0000001) /* 224.0.0.1 */
+/* Default network interface for multicast transmission */
+static struct pico_ipv4_link *mcast_default_link = NULL;
+#endif
+#ifdef PICO_SUPPORT_IPFRAG
+# define reassembly_dbg(...) do{}while(0) 
+#endif
+
+/* Queues */
+static struct pico_queue in = {};
+static struct pico_queue out = {};
+
+/* Functions */
+static int ipv4_route_compare(void *ka, void * kb);
+
+int pico_ipv4_to_string(char *ipbuf, const uint32_t ip)
+{
+  const unsigned char *addr = (unsigned char *) &ip;
+  int i;
+
+  if (!ipbuf) {
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+
+  for(i = 0; i < 4; i++)
+  {
+    if(addr[i] > 99){
+      *ipbuf++ = '0' + (addr[i] / 100);
+      *ipbuf++ = '0' + ((addr[i] % 100) / 10);
+      *ipbuf++ = '0' + ((addr[i] % 100) % 10);
+    }else if(addr[i] > 9){
+      *ipbuf++ = '0' + (addr[i] / 10);
+      *ipbuf++ = '0' + (addr[i] % 10);
+    }else{
+      *ipbuf++ = '0' + addr[i];
+    }
+    if(i < 3)
+      *ipbuf++ = '.';
+  }
+  *ipbuf = '\0';
+  
+  return 0;
+}
+    
+int pico_string_to_ipv4(const char *ipstr, uint32_t *ip)
+{
+  unsigned char buf[4] = {0};
+  int cnt = 0;
+  int p;
+
+  if(!ipstr || !ip) {
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+
+  while((p = *ipstr++) != 0)
+  {
+    if(pico_is_digit(p)){
+      buf[cnt] = (10 * buf[cnt]) + (p - '0');
+    }else if(p == '.'){
+        cnt++;
+    }else{
+      return -1;
+    }
+  }   
+  
+  /* Handle short notation */
+  if(cnt == 1){
+    buf[3] = buf[1];
+    buf[1] = 0;
+    buf[2] = 0;
+  }else if (cnt == 2){
+    buf[3] = buf[2];
+    buf[2] = 0;
+  }else if(cnt != 3){
+    /* String could not be parsed, return error */
+    return -1;
+  }   
+
+  *ip = long_from(buf);
+
+  return 0;
+
+}  
+
+int pico_ipv4_valid_netmask(uint32_t mask)
+{
+  int cnt = 0;
+  int end = 0;
+  int i;
+  uint32_t mask_swap = long_be(mask);
+
+  /* 
+   * Swap bytes for convenient parsing 
+   * e.g. 0x..f8ff will become 0xfff8..
+   * Then, we count the consecutive bits
+   *
+   * */
+
+  for(i = 0; i < 32; i++){
+    if((mask_swap << i) & (1 << 31)){
+      if(end) {
+        pico_err = PICO_ERR_EINVAL;
+        return -1;
+      }
+      cnt++;
+    }else{
+      end = 1;
+    }        
+  }
+  return cnt;
+}
+
+int pico_ipv4_is_unicast(uint32_t address) 
+{
+  const unsigned char *addr = (unsigned char *) &address;
+  if((addr[0] & 0xe0) == 0xe0)
+    return 0; /* multicast */
+    
+  return 1;
+}
+
+int pico_ipv4_is_multicast(uint32_t address) 
+{
+  const unsigned char *addr = (unsigned char *) &address;
+  if((addr[0] != 0xff) && ((addr[0] & 0xe0) == 0xe0))
+    return 1; /* multicast */
+    
+  return 0;
+}
+
+static int pico_ipv4_checksum(struct pico_frame *f)
+{
+  struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+  if (!hdr)
+    return -1;
+  hdr->crc = 0;
+  hdr->crc = short_be(pico_checksum(hdr, f->net_len));
+  return 0;
+}
+
+#ifdef PICO_SUPPORT_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;
+  a = (struct pico_ipv4_hdr *) frame_a->net_hdr;
+  b = (struct pico_ipv4_hdr *) frame_b->net_hdr;
+
+  if (short_be((a->frag & PICO_IPV4_FRAG_MASK)) < short_be((b->frag & PICO_IPV4_FRAG_MASK)))
+    return -1; 
+  else if (short_be((a->frag & PICO_IPV4_FRAG_MASK)) > short_be((b->frag & PICO_IPV4_FRAG_MASK)))
+    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 int 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_udp_hdr *udp_hdr = NULL;
+  struct pico_tcp_hdr *tcp_hdr = NULL;
+  struct pico_ipv4_fragmented_packet *pfrag = NULL, frag; 
+  struct pico_frame *f_new = NULL, *f_frag = NULL;
+  struct pico_tree_node *index, *_tmp;
+
+  data_len = 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 = 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);
+      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);
+      pfrag = pico_tree_findKey(&pico_ipv4_fragmented_tree, &frag);
+      if (pfrag) {
+        pfrag->total_len += (short_be(hdr->len) - (*f)->net_len);
+        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);
+    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);
+    pfrag = pico_tree_findKey(&pico_ipv4_fragmented_tree, &frag);
+    if (pfrag) {
+      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);
+      f_new = self->alloc(self, pfrag->total_len);
+
+      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 = 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);
+      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 = 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 += (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 = 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) {
+        tcp_hdr = (struct pico_tcp_hdr *) f_new->transport_hdr;
+        tcp_hdr->crc = 0;
+        tcp_hdr->crc = short_be(pico_tcp_checksum_ipv4(f_new));
+  #endif
+  #ifdef PICO_SUPPORT_UDP
+      } else if (hdr->proto == PICO_PROTO_UDP){
+        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 int 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)
+{
+  uint16_t checksum_invalid = 1;
+  struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+
+  checksum_invalid = short_be(pico_checksum(hdr, f->net_len));
+  if (checksum_invalid) {
+    dbg("IP: checksum failed!\n");
+    pico_frame_discard(f);
+    return 0;
+  }
+  return 1;
+}
+#else
+static inline int pico_ipv4_crc_check(struct pico_frame *f)
+{
+  return 1;
+}
+#endif /* PICO_SUPPORT_CRC */
+
+static int pico_ipv4_forward(struct pico_frame *f);
+#ifdef PICO_SUPPORT_MCAST
+static int pico_ipv4_mcast_filter(struct pico_frame *f);
+#endif
+
+static int ipv4_link_compare(void *ka, void *kb)
+{
+  struct pico_ipv4_link *a = ka, *b =kb;
+  if (a->address.addr < b->address.addr)
+    return -1;
+  if (a->address.addr > b->address.addr)
+    return 1;
+
+  //zero can be assigned multiple times (e.g. for DHCP)
+  if (a->dev != NULL && b->dev != NULL && a->address.addr == PICO_IP4_ANY && b->address.addr == PICO_IP4_ANY){
+    if (a->dev < b->dev)
+      return -1;
+    if (a->dev > b->dev)
+      return 1;
+  }
+  return 0;
+}
+
+PICO_TREE_DECLARE(Tree_dev_link, ipv4_link_compare);
+
+static int pico_ipv4_process_in(struct pico_protocol *self, struct pico_frame *f)
+{
+  uint8_t option_len = 0;
+  int ret = 0;
+  struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+  struct pico_ipv4_link test = {.address = {.addr = PICO_IP4_ANY}, .dev = NULL};
+
+  /* NAT needs transport header information */
+  if(((hdr->vhl) & 0x0F )> 5){
+     option_len =  4*(((hdr->vhl) & 0x0F)-5);
+  }
+  f->transport_hdr = ((uint8_t *)f->net_hdr) + PICO_SIZE_IP4HDR + option_len;
+  f->transport_len = short_be(hdr->len) - PICO_SIZE_IP4HDR - option_len;
+  f->net_len = PICO_SIZE_IP4HDR + option_len;
+
+#ifdef PICO_SUPPORT_IPFILTER
+  if (ipfilter(f)) {
+    /*pico_frame is discarded as result of the filtering*/
+    return 0;
+  }
+#endif
+
+  /* ret == 1 indicates to continue the function */
+  ret = pico_ipv4_crc_check(f);
+  if (ret < 1)
+    return ret;
+  ret = pico_ipv4_fragmented_check(self, &f);
+  if (ret < 1)
+    return ret;
+
+#ifdef PICO_SUPPORT_MCAST
+  /* Multicast address in source, discard quietly */
+  if (pico_ipv4_is_multicast(hdr->src.addr)) {
+    ip_mcast_dbg("MCAST: ERROR multicast address %08X in source address\n", hdr->src.addr);
+    pico_frame_discard(f);
+    return 0;
+  }
+#endif
+  if (hdr->frag & 0x80) {
+    pico_frame_discard(f); //RFC 3514
+    return 0;
+  }
+  if (0) {
+#ifdef PICO_SUPPORT_UDP
+  } else if (pico_ipv4_is_broadcast(hdr->dst.addr) && (hdr->proto == PICO_PROTO_UDP)) {
+      /* Receiving UDP broadcast datagram */
+      f->flags |= PICO_FRAME_FLAG_BCAST;
+      pico_enqueue(pico_proto_udp.q_in, f);
+#endif
+  } else if (pico_ipv4_is_multicast(hdr->dst.addr)) {
+#ifdef PICO_SUPPORT_MCAST
+    /* Receiving UDP multicast datagram TODO set f->flags? */
+    if (hdr->proto == PICO_PROTO_IGMP) {
+      ip_mcast_dbg("MCAST: received IGMP message\n");
+      pico_transport_receive(f, PICO_PROTO_IGMP);
+    } else if ((pico_ipv4_mcast_filter(f) == 0) && (hdr->proto == PICO_PROTO_UDP)) {
+      pico_enqueue(pico_proto_udp.q_in, f);
+    } else {
+      pico_frame_discard(f);
+    }
+#endif
+  } else if (pico_ipv4_link_find(&hdr->dst)) {
+   if (pico_ipv4_nat_isenabled_in(f) == 0) {  /* if NAT enabled (dst port registerd), do NAT */
+      if(pico_ipv4_nat(f, hdr->dst) != 0) {
+        return -1;
+      }
+      pico_ipv4_forward(f); /* Local packet became forward packet after NAT */
+    } else {                              /* no NAT so enqueue to next layer */
+      pico_transport_receive(f, hdr->proto);
+    }
+  } else if (pico_tree_findKey(&Tree_dev_link, &test)){
+#ifdef PICO_SUPPORT_UDP
+    //address of this device is apparently 0.0.0.0; might be a DHCP packet
+    pico_enqueue(pico_proto_udp.q_in, f);
+#endif
+  } else {
+    /* Packet is not local. Try to forward. */
+    if (pico_ipv4_forward(f) != 0) {
+      pico_frame_discard(f);
+    }
+  }
+  return 0;
+}
+
+PICO_TREE_DECLARE(Routes, ipv4_route_compare);
+
+
+static int pico_ipv4_process_out(struct pico_protocol *self, struct pico_frame *f)
+{
+  f->start = (uint8_t*) f->net_hdr;
+  #ifdef PICO_SUPPORT_IPFILTER
+  if (ipfilter(f)) {
+    /*pico_frame is discarded as result of the filtering*/
+    return 0;
+  }
+  #endif
+  return pico_sendto_dev(f);
+}
+
+
+static struct pico_frame *pico_ipv4_alloc(struct pico_protocol *self, int size)
+{
+  struct pico_frame *f =  pico_frame_alloc(size + PICO_SIZE_IP4HDR + PICO_SIZE_ETHHDR);
+  if (!f)
+    return NULL;
+  f->datalink_hdr = f->buffer;
+  f->net_hdr = f->buffer + PICO_SIZE_ETHHDR;
+  f->net_len = PICO_SIZE_IP4HDR;
+  f->transport_hdr = f->net_hdr + PICO_SIZE_IP4HDR;
+  f->transport_len = size;
+  f->len =  size + PICO_SIZE_IP4HDR;
+  return f;
+}
+
+static int pico_ipv4_frame_sock_push(struct pico_protocol *self, struct pico_frame *f);
+
+/* Interface: protocol definition */
+struct pico_protocol pico_proto_ipv4 = {
+  .name = "ipv4",
+  .proto_number = PICO_PROTO_IPV4,
+  .layer = PICO_LAYER_NETWORK,
+  .alloc = pico_ipv4_alloc,
+  .process_in = pico_ipv4_process_in,
+  .process_out = pico_ipv4_process_out,
+  .push = pico_ipv4_frame_sock_push,
+  .q_in = &in,
+  .q_out = &out,
+};
+
+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;
+
+  /* Routes are sorted by (host side) netmask len, then by addr, then by metric. */
+  if (long_be(a->netmask.addr) < long_be(b->netmask.addr))
+    return -1;
+
+  if (long_be(a->netmask.addr) > long_be(b->netmask.addr))
+    return 1;
+
+  if (a->dest.addr < b->dest.addr)
+    return -1;
+
+  if (a->dest.addr > b->dest.addr)
+    return 1;
+
+  if (a->metric < b->metric)
+    return -1;
+
+  if (a->metric > b->metric)
+    return 1;
+
+  return 0;
+}
+
+static struct pico_ipv4_route *route_find(struct pico_ip4 *addr)
+{
+  struct pico_ipv4_route *r;
+  struct pico_tree_node * index;
+
+  if(addr->addr != PICO_IP4_BCAST)
+  {
+    pico_tree_foreach_reverse(index, &Routes) {
+      r = index->keyValue;
+      if ((addr->addr & (r->netmask.addr)) == (r->dest.addr)) {
+        return r;
+      }
+    }
+  }
+  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;
+}
+
+struct pico_ip4 pico_ipv4_route_get_gateway(struct pico_ip4 *addr)
+{
+  struct pico_ip4 nullip;
+  struct pico_ipv4_route *route;
+  nullip.addr = 0U;
+
+  if(!addr) {
+    pico_err = PICO_ERR_EINVAL;
+    return nullip;
+  }
+
+  route = route_find(addr);
+  if (!route) {
+    pico_err = PICO_ERR_EHOSTUNREACH;
+    return nullip;
+  }
+  else
+    return route->gateway;
+}
+
+struct pico_ip4 *pico_ipv4_source_find(struct pico_ip4 *dst)
+{
+  struct pico_ip4 *myself = NULL;
+  struct pico_ipv4_route *rt;
+
+  if(!dst) {
+    pico_err = PICO_ERR_EINVAL;
+    return NULL;
+  }
+
+  rt = route_find(dst);
+  if (rt) {
+    myself = &rt->link->address;
+  } else
+    pico_err = PICO_ERR_EHOSTUNREACH;
+  return myself;
+}
+
+
+#ifdef PICO_SUPPORT_MCAST
+/*                        link
+ *                         |  
+ *                    MCASTGroups
+ *                    |    |     |
+ *         ------------    |     ------------
+ *         |               |                |
+ *   MCASTSources    MCASTSources     MCASTSources    
+ *   |  |  |  |      |  |  |  |       |  |  |  |
+ *   S  S  S  S      S  S  S  S       S  S  S  S
+ *
+ *   MCASTGroups: RBTree(mcast_group)
+ *   MCASTSources: RBTree(source)
+ */
+static int ipv4_mcast_groups_cmp(void * ka, void * kb)
+{
+  struct pico_mcast_group *a = ka, *b = kb;
+  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;
+  }
+}
+
+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;
+}
+
+static void pico_ipv4_mcast_print_groups(struct pico_ipv4_link *mcast_link)
+{
+  uint16_t i = 0;
+  struct pico_mcast_group __attribute__ ((unused)) *g = NULL;
+  struct pico_ip4 __attribute__ ((unused)) *source = NULL;
+  struct pico_tree_node *index = NULL, *index2 = NULL;
+
+  ip_mcast_dbg("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
+  ip_mcast_dbg("+                           MULTICAST list interface %-16s             +\n", mcast_link->dev->name);
+  ip_mcast_dbg("+---------------------------------------------------------------------------------+\n");
+  ip_mcast_dbg("+  nr  |    interface     | host group | reference count | filter mode |  source  +\n");
+  ip_mcast_dbg("+---------------------------------------------------------------------------------+\n");
+
+  pico_tree_foreach(index, mcast_link->MCASTGroups)
+  {
+    g = index->keyValue;
+    ip_mcast_dbg("+ %04d | %16s |  %08X  |      %05u      |      %u      | %8s +\n", i, mcast_link->dev->name, g->mcast_addr.addr, g->reference_count, g->filter_mode, "");
+    pico_tree_foreach(index2, &g->MCASTSources)
+    {
+      source = index2->keyValue;
+      ip_mcast_dbg("+ %4s | %16s |  %8s  |      %5s      |      %s      | %08X +\n", "", "", "", "", "", source->addr);
+    }
+    i++;
+  }
+  ip_mcast_dbg("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
+}
+
+int pico_ipv4_mcast_join(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter)
+{
+  struct pico_mcast_group *g = NULL, test = {0};
+  struct pico_ipv4_link *link = NULL;
+  struct pico_tree_node *index = NULL, *_tmp = NULL;
+  struct pico_ip4 *source = NULL;
+
+  if (mcast_link)
+    link = pico_ipv4_link_get(mcast_link);
+  else
+    link = mcast_default_link;
+
+  test.mcast_addr = *mcast_group;
+  g = pico_tree_findKey(link->MCASTGroups, &test);
+  if (g) {
+    if (reference_count)
+      g->reference_count++;
+    pico_igmp_state_change(mcast_link, mcast_group, filter_mode, MCASTFilter, PICO_IGMP_STATE_UPDATE);
+  } else {
+    g = pico_zalloc(sizeof(struct pico_mcast_group));
+    if (!g) {
+      pico_err = PICO_ERR_ENOMEM;
+      return -1;
+    }
+    /* "non-existent" state of filter mode INCLUDE and empty source list */
+    g->filter_mode = PICO_IP_MULTICAST_INCLUDE;
+    g->reference_count = 1;
+    g->mcast_addr = *mcast_group;
+    g->MCASTSources.root = &LEAF;
+    g->MCASTSources.compare = ipv4_mcast_sources_cmp;
+    pico_tree_insert(link->MCASTGroups, g);
+    pico_igmp_state_change(mcast_link, mcast_group, filter_mode, MCASTFilter, PICO_IGMP_STATE_CREATE);
+  }
+
+  /* cleanup filter */
+  pico_tree_foreach_safe(index, &g->MCASTSources, _tmp)
+  {
+    source = index->keyValue;
+    pico_tree_delete(&g->MCASTSources, source);
+    pico_free(source);
+  }
+  /* insert new filter */
+  if (MCASTFilter) {
+    pico_tree_foreach(index, MCASTFilter)
+    {
+      source = pico_zalloc(sizeof(struct pico_ip4));
+      if (!source) {
+        pico_err = PICO_ERR_ENOMEM;
+        return -1;
+      }
+      source->addr = ((struct pico_ip4 *)index->keyValue)->addr;
+      pico_tree_insert(&g->MCASTSources, source);
+    }
+  }
+  g->filter_mode = filter_mode;
+
+  pico_ipv4_mcast_print_groups(link);
+  return 0;
+}
+
+int pico_ipv4_mcast_leave(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter)
+{
+
+  struct pico_mcast_group *g = NULL, test = {0};
+  struct pico_ipv4_link *link = NULL;
+  struct pico_tree_node *index = NULL, *_tmp = NULL;
+  struct pico_ip4 *source = NULL;
+
+  if (mcast_link)
+    link = pico_ipv4_link_get(mcast_link);
+  else
+    link = mcast_default_link;
+
+  test.mcast_addr = *mcast_group;
+  g = pico_tree_findKey(link->MCASTGroups, &test);
+  if (!g) {
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  } else {
+    if (reference_count && (--(g->reference_count) < 1)) {
+      pico_igmp_state_change(mcast_link, mcast_group, filter_mode, MCASTFilter, PICO_IGMP_STATE_DELETE);
+      /* cleanup filter */
+      pico_tree_foreach_safe(index, &g->MCASTSources, _tmp)
+      {
+        source = index->keyValue;
+        pico_tree_delete(&g->MCASTSources, source);
+        pico_free(source);
+      }
+      pico_tree_delete(link->MCASTGroups, g);
+      pico_free(g); 
+    } else {
+      pico_igmp_state_change(mcast_link, mcast_group, filter_mode, MCASTFilter, PICO_IGMP_STATE_UPDATE);
+      /* cleanup filter */
+      pico_tree_foreach_safe(index, &g->MCASTSources, _tmp)
+      {
+        source = index->keyValue;
+        pico_tree_delete(&g->MCASTSources, source);
+        pico_free(source);
+      }
+      /* insert new filter */
+      if (MCASTFilter) {
+        pico_tree_foreach(index, MCASTFilter)
+        {
+          source = pico_zalloc(sizeof(struct pico_ip4));
+          if (!source) {
+            pico_err = PICO_ERR_ENOMEM;
+            return -1;
+          }
+          source->addr = ((struct pico_ip4 *)index->keyValue)->addr;
+          pico_tree_insert(&g->MCASTSources, source);
+        }
+      }
+      g->filter_mode = filter_mode;
+    }
+  }
+
+  pico_ipv4_mcast_print_groups(link);
+  return 0;
+}
+
+struct pico_ipv4_link *pico_ipv4_get_default_mcastlink(void)
+{
+  return mcast_default_link;
+}
+
+static int pico_ipv4_mcast_filter(struct pico_frame *f)
+{
+  struct pico_ipv4_link *link = NULL;
+  struct pico_tree_node *index = NULL, *index2 = NULL;
+  struct pico_mcast_group *g = NULL, test = {0};
+  struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+
+  test.mcast_addr = hdr->dst; 
+
+  pico_tree_foreach(index, &Tree_dev_link) 
+  {
+    link = index->keyValue;
+    g = pico_tree_findKey(link->MCASTGroups, &test);
+    if (g) {
+      if (f->dev == link->dev) {
+        ip_mcast_dbg("MCAST: IP %08X is group member of current link %s\n", hdr->dst.addr, f->dev->name);
+        /* perform source filtering */
+        switch (g->filter_mode)
+        {
+          case PICO_IP_MULTICAST_INCLUDE:
+            pico_tree_foreach(index2, &g->MCASTSources)
+            {
+              if (hdr->src.addr == ((struct pico_ip4 *)index2->keyValue)->addr) {
+                ip_mcast_dbg("MCAST: IP %08X in included interface source list\n", hdr->src.addr);
+                return 0;
+              }
+            }
+            ip_mcast_dbg("MCAST: IP %08X NOT in included interface source list\n", hdr->src.addr);
+            return -1;
+            break;
+
+          case PICO_IP_MULTICAST_EXCLUDE:
+            pico_tree_foreach(index2, &g->MCASTSources)
+            {
+              if (hdr->src.addr == ((struct pico_ip4 *)index2->keyValue)->addr) {
+                ip_mcast_dbg("MCAST: IP %08X in excluded interface source list\n", hdr->src.addr);
+                return -1;
+              }
+            }
+            ip_mcast_dbg("MCAST: IP %08X NOT in excluded interface source list\n", hdr->src.addr);
+            return 0;
+            break;
+
+          default:
+            return -1;
+            break;
+        }
+      } else {
+        ip_mcast_dbg("MCAST: IP %08X is group member of different link %s\n", hdr->dst.addr, link->dev->name);
+      }
+    } else {
+      ip_mcast_dbg("MCAST: IP %08X is not a group member of link %s\n", hdr->dst.addr, f->dev->name);
+    }
+  }
+  return -1;
+}
+
+#else 
+
+int pico_ipv4_mcast_join(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter)
+{
+  pico_err = PICO_ERR_EPROTONOSUPPORT;
+  return -1;
+}
+int pico_ipv4_mcast_leave(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter)
+{
+  pico_err = PICO_ERR_EPROTONOSUPPORT;
+  return -1;
+}
+struct pico_ipv4_link *pico_ipv4_get_default_mcastlink(void)
+{
+  pico_err = PICO_ERR_EPROTONOSUPPORT;
+  return NULL;
+}
+#endif /* PICO_SUPPORT_MCAST */
+
+int pico_ipv4_frame_push(struct pico_frame *f, struct pico_ip4 *dst, uint8_t proto)
+{
+
+  struct pico_ipv4_route *route;
+  struct pico_ipv4_link *link;
+  struct pico_ipv4_hdr *hdr;
+  uint8_t ttl = PICO_IPV4_DEFAULT_TTL;
+  uint8_t vhl = 0x45; /* version 4, header length 20 */
+  static uint16_t ipv4_progressive_id = 0x91c0;
+#ifdef PICO_SUPPORT_MCAST
+  struct pico_tree_node *index;
+#endif
+
+  if(!f || !dst) {
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+  hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+  if (!hdr) {
+    dbg("IP header error\n");
+    pico_err = PICO_ERR_EINVAL;
+    goto drop;
+  }
+
+  if (dst->addr == 0) {
+    dbg("IP destination addr error\n");
+    pico_err = PICO_ERR_EINVAL;
+    goto drop;
+  }
+
+  route = route_find(dst);
+  if (!route) {
+    dbg("Route to %08x not found.\n", long_be(dst->addr));
+    pico_err = PICO_ERR_EHOSTUNREACH;
+    goto drop;
+  } else {
+    link = route->link;
+#ifdef PICO_SUPPORT_MCAST
+    if (pico_ipv4_is_multicast(dst->addr)) { /* if multicast */
+      switch (proto) {
+        case PICO_PROTO_UDP:
+          if(pico_udp_get_mc_ttl(f->sock, &ttl) < 0)
+            ttl = PICO_IP_DEFAULT_MULTICAST_TTL;
+          break;
+        case PICO_PROTO_IGMP:
+          vhl = 0x46; /* header length 24 */
+          ttl = 1;
+          /* router alert (RFC 2113) */ 
+          hdr->options[0] = 0x94;
+          hdr->options[1] = 0x04;
+          hdr->options[2] = 0x00;
+          hdr->options[3] = 0x00;
+          if (f->dev && link->dev != f->dev) { /* default link is not requested link */
+            pico_tree_foreach(index, &Tree_dev_link) {
+              link = index->keyValue;
+              if (link->dev == f->dev)
+                break;
+            }
+          }
+          break;
+        default:
+          ttl = PICO_IPV4_DEFAULT_TTL;
+      }
+    }
+#endif
+  }
+
+  hdr->vhl = vhl;
+  hdr->len = short_be(f->transport_len + f->net_len);
+  if (f->transport_hdr != f->payload)
+    ipv4_progressive_id++;
+  hdr->id = short_be(ipv4_progressive_id);
+  hdr->dst.addr = dst->addr;
+  hdr->src.addr = link->address.addr;
+  hdr->ttl = ttl;
+  hdr->proto = proto;
+  hdr->frag = short_be(PICO_IPV4_DONTFRAG);
+#ifdef PICO_SUPPORT_IPFRAG
+#  ifdef PICO_SUPPORT_UDP
+  if (proto == PICO_PROTO_UDP) {
+    /* first fragment, can not use transport_len to calculate IP length */
+    if (f->transport_hdr != f->payload)
+      hdr->len = short_be(f->payload_len + sizeof(struct pico_udp_hdr) + f->net_len);
+    /* set fragmentation flags and offset calculated in socket layer */
+    hdr->frag = f->frag;
+  }
+#  endif /* PICO_SUPPORT_UDP */
+#endif /* PICO_SUPPORT_IPFRAG */
+  pico_ipv4_checksum(f);
+
+  if (f->sock && f->sock->dev){
+    //if the socket has its device set, use that (currently used for DHCP)
+    f->dev = f->sock->dev;
+  } else {
+    f->dev = link->dev;
+  }
+
+#ifdef PICO_SUPPORT_MCAST
+  if (pico_ipv4_is_multicast(hdr->dst.addr)) {
+    struct pico_frame *cpy;
+    /* Sending UDP multicast datagram, am I member? If so, loopback copy */
+    if ((proto != PICO_PROTO_IGMP) && (pico_ipv4_mcast_filter(f) == 0)) {
+      ip_mcast_dbg("MCAST: sender is member of group, loopback copy\n");
+      cpy = pico_frame_copy(f);
+      pico_enqueue(&in, cpy);
+    }
+  }
+#endif
+
+  if(pico_ipv4_link_get(&hdr->dst)){
+    //it's our own IP
+    return pico_enqueue(&in, f);
+  }else{
+    /* TODO: Check if there are members subscribed here */
+    return pico_enqueue(&out, f);
+  }
+
+drop:
+  pico_frame_discard(f);
+  return -1;
+}
+
+
+static int pico_ipv4_frame_sock_push(struct pico_protocol *self, struct pico_frame *f)
+{
+  struct pico_ip4 *dst;
+  struct pico_remote_duple *remote_duple = (struct pico_remote_duple *) f->info;
+  if (!f->sock) {
+    pico_frame_discard(f);
+    return -1;
+  }
+
+  if (remote_duple) {
+    dst = &remote_duple->remote_addr.ip4;
+  } else {
+    dst = &f->sock->remote_addr.ip4;
+  }
+
+  return pico_ipv4_frame_push(f, dst, f->sock->proto->proto_number);
+}
+
+
+#ifdef DEBUG_ROUTE
+static void dbg_route(void)
+{
+  struct pico_ipv4_route *r;
+  struct pico_tree_node * index;
+  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);
+  }
+}
+#else
+#define dbg_route() do{ }while(0)
+#endif
+
+int 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 = metric;
+
+  if(pico_tree_findKey(&Routes,&test)){
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+  
+  new = pico_zalloc(sizeof(struct pico_ipv4_route));
+  if (!new) {
+    pico_err = PICO_ERR_ENOMEM;
+    return -1;
+  }
+  new->dest.addr = address.addr;
+  new->netmask.addr = netmask.addr;
+  new->gateway.addr = gateway.addr;
+  new->metric = metric;
+  if (gateway.addr == 0) {
+    /* No gateway provided, use the link */
+    new->link = link;
+  } else {
+    struct pico_ipv4_route *r = route_find(&gateway);
+    if (!r ) { /* Specified Gateway is unreachable */
+      pico_err = PICO_ERR_EHOSTUNREACH;
+      pico_free(new);
+      return -1;
+    }
+    if (r->gateway.addr) { /* Specified Gateway is not a neighbor */
+      pico_err = PICO_ERR_ENETUNREACH;
+      pico_free(new);
+      return -1;
+    }
+    new->link = r->link;
+  }
+  if (!new->link) {
+      pico_err = PICO_ERR_EINVAL;
+      pico_free(new);
+      return -1;
+  }
+
+  pico_tree_insert(&Routes,new);
+  dbg_route();
+  return 0;
+}
+
+int pico_ipv4_route_del(struct pico_ip4 address, struct pico_ip4 netmask, struct pico_ip4 gateway, int metric, struct pico_ipv4_link *link)
+{
+  struct pico_ipv4_route test, *found;
+  if (!link) {
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+  test.dest.addr = address.addr;
+  test.netmask.addr = netmask.addr;
+  test.metric = metric;
+
+  found = pico_tree_findKey(&Routes,&test);
+  if (found) {
+
+    pico_tree_delete(&Routes,found);
+    pico_free(found);
+
+    dbg_route();
+    return 0;
+  }
+  pico_err = PICO_ERR_EINVAL;
+  return -1;
+}
+
+
+int pico_ipv4_link_add(struct pico_device *dev, struct pico_ip4 address, struct pico_ip4 netmask)
+{
+  struct pico_ipv4_link test, *new;
+  struct pico_ip4 network, gateway;
+  char ipstr[30];
+
+  if(!dev) {
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+  test.address.addr = address.addr;
+  test.netmask.addr = netmask.addr;
+  test.dev = dev;
+  /** XXX: Valid netmask / unicast address test **/
+
+  if(pico_tree_findKey(&Tree_dev_link, &test)) {
+    dbg("IPv4: Trying to assign an invalid address (in use)\n");
+    pico_err = PICO_ERR_EADDRINUSE;
+    return -1;
+  }
+
+  /** XXX: Check for network already in use (e.g. trying to assign 10.0.0.1/24 where 10.1.0.1/8 is in use) **/
+  new = pico_zalloc(sizeof(struct pico_ipv4_link));
+  if (!new) {
+    dbg("IPv4: Out of memory!\n");
+    pico_err = PICO_ERR_ENOMEM;
+    return -1;
+  }
+  new->address.addr = address.addr;
+  new->netmask.addr = netmask.addr;
+  new->dev = dev;
+#ifdef PICO_SUPPORT_MCAST
+  new->MCASTGroups = pico_zalloc(sizeof(struct pico_tree));
+  if (!new->MCASTGroups) {
+    pico_free(new);
+    dbg("IPv4: Out of memory!\n");
+    pico_err = PICO_ERR_ENOMEM;
+    return -1;
+  }
+
+  new->MCASTGroups->root = &LEAF;
+  new->MCASTGroups->compare = ipv4_mcast_groups_cmp;
+  new->mcast_compatibility = PICO_IGMPV3; /* default RFC 3376 $7.2.1 */
+  new->mcast_last_query_interval = PICO_IGMP_QUERY_INTERVAL;
+#endif
+
+  pico_tree_insert(&Tree_dev_link, new);
+#ifdef PICO_SUPPORT_MCAST
+  do {
+    struct pico_ip4 mcast_all_hosts, mcast_addr, mcast_nm, mcast_gw;
+    if (!mcast_default_link) {
+      mcast_addr.addr = long_be(0xE0000000); /* 224.0.0.0 */
+      mcast_nm.addr = long_be(0xF0000000); /* 15.0.0.0 */
+      mcast_gw.addr = long_be(0x00000000);
+      mcast_default_link = new;
+      pico_ipv4_route_add(mcast_addr, mcast_nm, mcast_gw, 1, new);
+    }
+    mcast_all_hosts.addr = PICO_MCAST_ALL_HOSTS;
+    pico_ipv4_mcast_join(&address, &mcast_all_hosts, 1, PICO_IP_MULTICAST_EXCLUDE, NULL);
+  } while(0);
+#endif
+
+  network.addr = address.addr & netmask.addr;
+  gateway.addr = 0U;
+  pico_ipv4_route_add(network, netmask, gateway, 1, new);
+  pico_ipv4_to_string(ipstr, new->address.addr);
+  dbg("Assigned ipv4 %s to device %s\n", ipstr, new->dev->name);
+  return 0;
+}
+
+
+int pico_ipv4_link_del(struct pico_device *dev, struct pico_ip4 address)
+{
+  struct pico_ipv4_link test, *found;
+  struct pico_ip4 network;
+
+  if(!dev) {
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+  test.address.addr = address.addr;
+  test.dev = dev;
+  found = pico_tree_findKey(&Tree_dev_link, &test);
+  if (!found) {
+    pico_err = PICO_ERR_ENXIO;
+    return -1;
+  }
+
+  network.addr = found->address.addr & found->netmask.addr;
+  pico_ipv4_route_del(network, found->netmask,pico_ipv4_route_get_gateway(&found->address), 1, found);
+#ifdef PICO_SUPPORT_MCAST
+  do {
+    struct pico_ip4 mcast_all_hosts, mcast_addr, mcast_nm, mcast_gw;
+    struct pico_mcast_group *g = NULL;
+    struct pico_tree_node * index, * _tmp;
+    if (found == mcast_default_link) {
+      mcast_addr.addr = long_be(0xE0000000); /* 224.0.0.0 */
+      mcast_nm.addr = long_be(0xF0000000); /* 15.0.0.0 */
+      mcast_gw.addr = long_be(0x00000000);
+      mcast_default_link = NULL;
+      pico_ipv4_route_del(mcast_addr, mcast_nm, mcast_gw, 1, found);
+    }
+    mcast_all_hosts.addr = PICO_MCAST_ALL_HOSTS;
+    pico_ipv4_mcast_leave(&address, &mcast_all_hosts, 1, PICO_IP_MULTICAST_EXCLUDE, NULL);
+    pico_tree_foreach_safe(index,found->MCASTGroups, _tmp)
+    {
+      g = index->keyValue;
+      pico_tree_delete(found->MCASTGroups, g);
+      pico_free(g);
+    }
+  } while(0);
+#endif
+
+  pico_tree_delete(&Tree_dev_link, found);
+  /* XXX: pico_free(found); */
+  /* XXX: cleanup all routes containing the removed link */
+  return 0;
+}
+
+
+struct pico_ipv4_link *pico_ipv4_link_get(struct pico_ip4 *address)
+{
+  struct pico_ipv4_link test = {0}, *found = NULL;
+  test.address.addr = address->addr;
+
+  found = pico_tree_findKey(&Tree_dev_link, &test);
+  if (!found)
+    return NULL;
+  else
+    return found;
+}
+
+struct pico_ipv4_link *pico_ipv4_link_by_dev(struct pico_device *dev)
+{
+  struct pico_tree_node *index = NULL;
+  struct pico_ipv4_link *link = NULL;
+
+  pico_tree_foreach(index, &Tree_dev_link) 
+  {
+    link = index->keyValue;
+    if (link->dev == dev)
+      return link;
+  }
+  return NULL;
+}
+
+
+struct pico_device *pico_ipv4_link_find(struct pico_ip4 *address)
+{
+  struct pico_ipv4_link test, *found;
+  if(!address) {
+    pico_err = PICO_ERR_EINVAL;
+    return NULL;
+  }
+  test.dev = NULL;
+  test.address.addr = address->addr;
+  found = pico_tree_findKey(&Tree_dev_link, &test);
+  if (!found) {
+    pico_err = PICO_ERR_ENXIO;
+    return NULL;
+  }
+  return found->dev;
+}
+
+int pico_ipv4_rebound(struct pico_frame *f)
+{
+  struct pico_ip4 dst;
+  struct pico_ipv4_hdr *hdr;
+  if(!f) {
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+
+  hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+  if (!hdr) {
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+  dst.addr = hdr->src.addr;
+  return pico_ipv4_frame_push(f, &dst, hdr->proto);
+}
+
+static int pico_ipv4_forward(struct pico_frame *f)
+{
+  struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *)f->net_hdr;
+  struct pico_ipv4_route *rt;
+  if (!hdr) {
+    return -1;
+  }
+
+  //dbg("IP> FORWARDING.\n");
+  rt = route_find(&hdr->dst);
+  if (!rt) {
+    pico_notify_dest_unreachable(f);
+    return -1;
+  }
+  //dbg("ROUTE: valid..\n");
+  f->dev = rt->link->dev;
+  hdr->ttl-=1;
+  if (hdr->ttl < 1) {
+    pico_notify_ttl_expired(f);
+    return -1;
+  }
+  hdr->crc++;
+
+  /* check if NAT enbled on link and do NAT if so */
+  if (pico_ipv4_nat_isenabled_out(rt->link) == 0)
+    pico_ipv4_nat(f, rt->link->address);
+
+  //dbg("Routing towards %s\n", f->dev->name);
+  f->start = f->net_hdr;
+  if(f->dev->eth != NULL)
+    f->len -= PICO_SIZE_ETHHDR;
+  pico_sendto_dev(f);
+  return 0;
+
+}
+
+int pico_ipv4_is_broadcast(uint32_t addr)
+{
+  struct pico_ipv4_link *link;
+  struct pico_tree_node * index;
+  if (addr == PICO_IP4_ANY)
+    return 1;
+  if (addr == PICO_IP4_BCAST)
+    return 1;
+
+  pico_tree_foreach(index,&Tree_dev_link) {
+    link = index->keyValue;
+    if ((link->address.addr | (~link->netmask.addr)) == addr)
+      return 1;
+  }
+  return 0;
+}
+
+void pico_ipv4_unreachable(struct pico_frame *f, int err)
+{
+  struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+#if defined PICO_SUPPORT_TCP || defined PICO_SUPPORT_UDP
+  f->transport_hdr = ((uint8_t *)f->net_hdr) + PICO_SIZE_IP4HDR;
+  pico_transport_error(f, hdr->proto, err);
+#endif
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_ipv4.h	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,96 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+.
+
+*********************************************************************/
+#ifndef _INCLUDE_PICO_IPV4
+#define _INCLUDE_PICO_IPV4
+#include "pico_addressing.h"
+#include "pico_protocol.h"
+#include "pico_tree.h"
+
+#define PICO_IPV4_INADDR_ANY 0x00000000U
+
+#define PICO_SIZE_IP4HDR ((sizeof(struct pico_ipv4_hdr)))
+#define PICO_IPV4_DONTFRAG 0x4000
+#define PICO_IPV4_MOREFRAG 0x2000
+#define PICO_IPV4_FRAG_MASK 0x1FFF
+#define PICO_IPV4_DEFAULT_TTL 64
+
+extern struct pico_protocol pico_proto_ipv4;
+
+struct __attribute__((packed)) pico_ipv4_hdr {
+  uint8_t vhl;
+  uint8_t tos;
+  uint16_t len;
+  uint16_t id;
+  uint16_t frag;
+  uint8_t ttl;
+  uint8_t proto;
+  uint16_t crc;
+  struct pico_ip4 src;
+  struct pico_ip4 dst;
+  uint8_t options[0];
+};
+
+struct __attribute__((packed)) pico_ipv4_pseudo_hdr
+{
+  struct pico_ip4 src;
+  struct pico_ip4 dst;
+  uint8_t zeros;
+  uint8_t proto;
+  uint16_t len;
+};
+
+/* Interface: link to device */
+struct pico_mcast_list;
+
+struct pico_ipv4_link
+{
+  struct pico_device *dev;
+  struct pico_ip4 address;
+  struct pico_ip4 netmask;
+#ifdef PICO_SUPPORT_MCAST
+  struct pico_tree *MCASTGroups;
+  uint8_t mcast_compatibility;
+  uint8_t mcast_last_query_interval;
+#endif
+};
+
+#ifdef PICO_SUPPORT_MCAST
+struct pico_mcast_group {
+  uint8_t filter_mode;
+  uint16_t reference_count;
+  struct pico_ip4 mcast_addr;
+  struct pico_tree MCASTSources;
+};
+#endif
+
+int pico_ipv4_to_string(char *ipbuf, const uint32_t ip);
+int pico_string_to_ipv4(const char *ipstr, uint32_t *ip);
+int pico_ipv4_valid_netmask(uint32_t mask);
+int pico_ipv4_is_unicast(uint32_t address); 
+int pico_ipv4_is_multicast(uint32_t address); 
+int pico_ipv4_is_broadcast(uint32_t addr);
+
+int pico_ipv4_link_add(struct pico_device *dev, struct pico_ip4 address, struct pico_ip4 netmask);
+int pico_ipv4_link_del(struct pico_device *dev, struct pico_ip4 address);
+int pico_ipv4_rebound(struct pico_frame *f);
+
+int pico_ipv4_frame_push(struct pico_frame *f, struct pico_ip4 *dst, uint8_t proto);
+struct pico_ipv4_link *pico_ipv4_link_get(struct pico_ip4 *address);
+struct pico_ipv4_link *pico_ipv4_link_by_dev(struct pico_device *dev);
+struct pico_device *pico_ipv4_link_find(struct pico_ip4 *address);
+struct pico_ip4 *pico_ipv4_source_find(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, struct pico_ip4 gateway, int metric, struct pico_ipv4_link *link);
+struct pico_ip4 pico_ipv4_route_get_gateway(struct pico_ip4 *addr);
+void pico_ipv4_unreachable(struct pico_frame *f, int err);
+
+int pico_ipv4_mcast_join(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter);
+int pico_ipv4_mcast_leave(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter);
+struct pico_ipv4_link *pico_ipv4_get_default_mcastlink(void);
+
+#endif /* _INCLUDE_PICO_IPV4 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_ipv6.h	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,30 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+.
+
+*********************************************************************/
+#ifndef _INCLUDE_PICO_IPV6
+#define _INCLUDE_PICO_IPV6
+#include "pico_addressing.h"
+#include "pico_protocol.h"
+
+extern struct pico_protocol pico_proto_ipv6;
+extern const uint8_t PICO_IPV6_ANY[PICO_SIZE_IP6];
+
+
+/* This module is responsible for routing outgoing packets and 
+ * delivering incoming packets to other layers
+ */
+
+/* Interface for processing incoming ipv6 packets (decap/deliver) */
+int pico_ipv6_process_in(struct pico_frame *f);
+
+/* Interface for processing outgoing ipv6 frames (encap/push) */
+int pico_ipv6_process_out(struct pico_frame *f);
+
+/* Return estimated overhead for ipv6 frames to define allocation */
+int pico_ipv6_overhead(struct pico_frame *f);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_nat.c	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,683 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+.
+
+Authors: Kristof Roelants, Brecht Van Cauwenberghe,
+         Simon Maes, Philippe Mariman
+*********************************************************************/
+
+#include "pico_stack.h"
+#include "pico_frame.h"
+#include "pico_tcp.h"
+#include "pico_udp.h"
+#include "pico_ipv4.h"
+#include "pico_addressing.h"
+#include "pico_nat.h"
+
+
+#ifdef PICO_SUPPORT_IPV4
+#ifdef PICO_SUPPORT_NAT
+
+#define nat_dbg(...) do{}while(0)
+//#define nat_dbg dbg
+#define NAT_TCP_TIMEWAIT 240000 /* 4mins (in msec) */
+//#define NAT_TCP_TIMEWAIT 10000 /* 10 sec (in msec)  - for testing purposes only*/
+
+
+struct pico_nat_key {
+  struct pico_ip4 pub_addr;
+  uint16_t pub_port;
+  struct pico_ip4 priv_addr;
+  uint16_t priv_port;
+  uint8_t proto;
+  /*
+  del_flags:
+              1                   0 
+    5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |F|B|S|R|P|~| CONNECTION ACTIVE |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+  F: FIN from Forwarding packet
+  B: FIN from Backwarding packet
+  S: SYN 
+  R: RST  
+  P: Persistant
+         
+  */
+  uint16_t del_flags;
+  /* Connector for trees */
+};
+
+static struct pico_ipv4_link pub_link;
+static uint8_t enable_nat_flag = 0;
+
+static int nat_cmp_backward(void * ka, void * kb)
+{
+    struct pico_nat_key *a = ka, *b = kb;
+  if (a->pub_port < b->pub_port) {
+    return -1;
+  }
+  else if (a->pub_port > b->pub_port) {
+    return 1;
+  }
+  else {
+    if (a->proto < b->proto) {
+      return -1;
+    }
+    else if (a->proto > b->proto) {
+      return 1;
+    }
+    else {
+      /* a and b are identical */
+      return 0;
+    }
+  }
+}
+
+static int nat_cmp_forward(void * ka, void * kb)
+{
+    struct pico_nat_key *a =ka, *b = kb;
+  if (a->priv_addr.addr < b->priv_addr.addr) {
+    return -1;
+  }
+  else if (a->priv_addr.addr > b->priv_addr.addr) {
+    return 1;
+  }
+  else {
+    if (a->priv_port < b->priv_port) {
+      return -1;
+    }
+    else if (a->priv_port > b->priv_port) {
+      return 1;
+    }
+    else {
+      if (a->proto < b->proto) {
+        return -1;
+      }
+      else if (a->proto > b->proto) {
+        return 1;
+      }
+      else {
+        /* a and b are identical */
+        return 0;
+      }
+    }
+  }
+}
+
+PICO_TREE_DECLARE(KEYTable_forward,nat_cmp_forward);
+PICO_TREE_DECLARE(KEYTable_backward,nat_cmp_backward);
+
+/* 
+  2 options: 
+    find on proto and pub_port 
+    find on priv_addr, priv_port and proto 
+  zero the unused parameters 
+*/
+static struct pico_nat_key *pico_ipv4_nat_find_key(uint16_t pub_port, struct pico_ip4 *priv_addr, uint16_t priv_port, uint8_t proto)
+{
+  struct pico_nat_key test;
+  test.pub_port = pub_port;
+  test.priv_port = priv_port;
+  test.proto = proto;
+  if (priv_addr)
+    test.priv_addr = *priv_addr;
+  else
+    test.priv_addr.addr = 0;
+
+  /* returns NULL if test can not be found */ 
+  if (!pub_port)
+      return pico_tree_findKey(&KEYTable_forward,&test);
+  else
+      return pico_tree_findKey(&KEYTable_backward, &test);
+}
+
+int pico_ipv4_nat_find(uint16_t pub_port, struct pico_ip4 *priv_addr, uint16_t priv_port, uint8_t proto)
+{
+  struct pico_nat_key *k = NULL;
+
+  k = pico_ipv4_nat_find_key(pub_port, priv_addr, priv_port, proto); 
+  if (k)
+    return 0;
+  else
+    return -1;
+}
+
+int pico_ipv4_nat_snif_forward(struct pico_nat_key *nk, struct pico_frame *f)
+{
+  uint8_t proto;
+  struct pico_ipv4_hdr *ipv4_hdr = (struct pico_ipv4_hdr *)f->net_hdr;
+  struct pico_tcp_hdr *tcp_hdr;
+ 
+  if (!ipv4_hdr)
+    return -1;
+  proto = ipv4_hdr->proto;
+
+  if (proto == PICO_PROTO_TCP) {
+    tcp_hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+    if (!tcp_hdr)
+      return -1;
+    if (tcp_hdr->flags & PICO_TCP_FIN) {
+      nk->del_flags |= PICO_DEL_FLAGS_FIN_FORWARD; //FIN from forwarding packet
+    }
+    if (tcp_hdr->flags & PICO_TCP_SYN) {
+      nk->del_flags |= PICO_DEL_FLAGS_SYN; 
+    }
+    if (tcp_hdr->flags & PICO_TCP_RST) {
+      nk->del_flags |= PICO_DEL_FLAGS_RST;
+    }
+  } else if (proto == PICO_PROTO_UDP) {
+    /* set conn active to 1 */
+    nk->del_flags &= 0xFE00; 
+    nk->del_flags++;
+  } 
+  return 0; 
+}
+
+
+int pico_ipv4_nat_snif_backward(struct pico_nat_key *nk, struct pico_frame *f)
+{
+  uint8_t proto;
+  struct pico_ipv4_hdr *ipv4_hdr = (struct pico_ipv4_hdr *)f->net_hdr;
+  struct pico_tcp_hdr *tcp_hdr;
+
+  if (!ipv4_hdr)
+    return -1;
+  proto = ipv4_hdr->proto;
+
+  if (proto == PICO_PROTO_TCP) {
+    tcp_hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+    if (!tcp_hdr)
+      return -1;
+    if (tcp_hdr->flags & PICO_TCP_FIN) {
+      nk->del_flags |= PICO_DEL_FLAGS_FIN_BACKWARD; //FIN from backwarding packet
+    }
+    if (tcp_hdr->flags & PICO_TCP_SYN) {
+      nk->del_flags |= PICO_DEL_FLAGS_SYN;
+    }
+    if (tcp_hdr->flags & PICO_TCP_RST) {
+      nk->del_flags |= PICO_DEL_FLAGS_RST;
+    }
+  } else if (proto == PICO_PROTO_UDP) {
+    /* set conn active to 1 */
+    nk->del_flags &= 0xFE00; 
+    nk->del_flags++;
+  }
+  return 0;
+}
+
+void pico_ipv4_nat_table_cleanup(unsigned long now, void *_unused)
+{
+  struct pico_tree_node * idx, * safe;
+  struct pico_nat_key *k = NULL;
+    nat_dbg("NAT: before table cleanup:\n");
+  pico_ipv4_nat_print_table();
+
+  //struct pico_nat_key *tmp;
+  pico_tree_foreach_reverse_safe(idx,&KEYTable_forward,safe){
+      k = idx->keyValue;
+    switch (k->proto)
+    {
+      case PICO_PROTO_TCP:
+        if ((k->del_flags & 0x0800) >> 11) {
+          /* entry is persistant */
+          break;
+        }
+        else if ((k->del_flags & 0x01FF) == 0) {
+          /* conn active is zero, delete entry */
+          pico_ipv4_nat_del(k->pub_port, k->proto);
+        }
+        else if ((k->del_flags & 0x1000) >> 12) {
+          /* RST flag set, set conn active to zero */
+          k->del_flags &= 0xFE00;
+        }
+        else if (((k->del_flags & 0x8000) >> 15) && ((k->del_flags & 0x4000) >> 14)) {
+          /* FIN1 and FIN2 set, set conn active to zero */
+          k->del_flags &= 0xFE00; 
+        }
+        else if ((k->del_flags & 0x01FF) > 360) {
+          /* conn is active for 24 hours, delete entry */
+          pico_ipv4_nat_del(k->pub_port, k->proto);
+        }
+        else {
+          k->del_flags++;
+        } 
+        break;
+
+      case PICO_PROTO_UDP:
+        if ((k->del_flags & 0x0800) >> 11) {
+          /* entry is persistant */
+          break;
+        }
+        else if ((k->del_flags & 0x01FF) > 1) {
+          /* Delete entry when it has existed NAT_TCP_TIMEWAIT */
+          pico_ipv4_nat_del(k->pub_port, k->proto);
+        }
+        else {
+          k->del_flags++;
+        }
+        break;
+
+      default:
+        /* Unknown protocol in NAT table, delete when it has existed NAT_TCP_TIMEWAIT */
+        if ((k->del_flags & 0x01FF) > 1) {
+          pico_ipv4_nat_del(k->pub_port, k->proto);
+        }
+        else {
+          k->del_flags++;
+        }
+    }
+  }
+
+  nat_dbg("NAT: after table cleanup:\n");
+  pico_ipv4_nat_print_table();
+  pico_timer_add(NAT_TCP_TIMEWAIT, pico_ipv4_nat_table_cleanup, NULL);
+}
+
+int pico_ipv4_nat_add(struct pico_ip4 pub_addr, uint16_t pub_port, struct pico_ip4 priv_addr, uint16_t priv_port, uint8_t proto)
+{
+  struct pico_nat_key *key = pico_zalloc(sizeof(struct pico_nat_key));
+  if (!key) {
+    pico_err = PICO_ERR_ENOMEM;
+    return -1;
+  }
+
+  key->pub_addr = pub_addr;
+  key->pub_port = pub_port;
+  key->priv_addr = priv_addr;
+  key->priv_port = priv_port;
+  key->proto = proto;
+  key->del_flags = 0x0001; /* set conn active to 1, other flags to 0 */
+
+  /* RB_INSERT returns NULL when element added, pointer to the element if already in tree */
+  if(!pico_tree_insert(&KEYTable_forward, key) && !pico_tree_insert(&KEYTable_backward, key)){
+    return 0; /* New element added */
+  }
+  else {
+    pico_free(key);
+    pico_err = PICO_ERR_EINVAL;
+    return -1; /* Element key already exists */
+  }
+}
+
+
+int pico_ipv4_nat_del(uint16_t pub_port, uint8_t proto)
+{
+  struct pico_nat_key *key = NULL;
+  key = pico_ipv4_nat_find_key(pub_port, NULL, 0, proto);
+  if (!key) {
+    nat_dbg("NAT: key to delete not found: proto %u | pub_port %u\n", proto, pub_port);
+    return -1;
+  }
+  else {
+    nat_dbg("NAT: key to delete found: proto %u | pub_port %u\n", proto, pub_port);  
+    /* RB_REMOVE returns pointer to removed element, NULL to indicate error */
+    if(pico_tree_delete(&KEYTable_forward, key) && pico_tree_delete(&KEYTable_backward, key))
+          pico_free(key);
+    else
+      return -1; /* Error on removing element, do not free! */
+  }
+  return 0;
+}
+
+int pico_ipv4_port_forward(struct pico_ip4 pub_addr, uint16_t pub_port, struct pico_ip4 priv_addr, uint16_t priv_port, uint8_t proto, uint8_t persistant)
+{
+  struct pico_nat_key *key = NULL;
+
+  switch (persistant)
+  {
+    case PICO_IPV4_FORWARD_ADD:
+      if (pico_ipv4_nat_add(pub_addr, pub_port, priv_addr, priv_port, proto) != 0)
+        return -1;  /* pico_err set in nat_add */
+      key = pico_ipv4_nat_find_key(pub_port, &priv_addr, priv_port, proto);
+      if (!key) {
+        pico_err = PICO_ERR_EAGAIN;
+        return -1;
+      }
+      key->del_flags = (key->del_flags & ~(0x1 << 11)) | (persistant << 11);
+      break;
+
+    case PICO_IPV4_FORWARD_DEL:
+      return pico_ipv4_nat_del(pub_port, proto);
+
+    default:
+      pico_err = PICO_ERR_EINVAL;
+      return -1;
+  }
+  pico_ipv4_nat_print_table();
+  return 0;
+}
+
+
+void pico_ipv4_nat_print_table(void)
+{
+  struct pico_nat_key __attribute__((unused)) *k = NULL ;
+  struct pico_tree_node * index;
+  uint16_t i = 0;
+
+  nat_dbg("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
+  nat_dbg("+                                                       NAT table                                                       +\n");
+  nat_dbg("+-----------------------------------------------------------------------------------------------------------------------+\n");
+  nat_dbg("+  pointer   | private_addr | private_port | proto | pub_addr | pub_port | conn active | FIN1 | FIN2 | SYN | RST | PERS +\n");
+  nat_dbg("+-----------------------------------------------------------------------------------------------------------------------+\n");
+
+  pico_tree_foreach(index,&KEYTable_forward){
+      k = index->keyValue;
+    nat_dbg("+ %10p |   %08X   |    %05u     |  %04u | %08X |  %05u   |     %03u     |   %u  |   %u  |  %u  |  %u  |   %u  +\n", 
+           k, k->priv_addr.addr, k->priv_port, k->proto, k->pub_addr.addr, k->pub_port, (k->del_flags)&0x01FF, ((k->del_flags)&0x8000)>>15, 
+           ((k->del_flags)&0x4000)>>14, ((k->del_flags)&0x2000)>>13, ((k->del_flags)&0x1000)>>12, ((k->del_flags)&0x0800)>>11);
+    i++;
+  }
+  nat_dbg("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
+}
+
+int pico_ipv4_nat_generate_key(struct pico_nat_key* nk, struct pico_frame* f, struct pico_ip4 pub_addr)
+{
+  uint16_t pub_port = 0;
+  uint8_t proto;
+  struct pico_tcp_hdr *tcp_hdr = NULL;  /* forced to use pico_trans */
+  struct pico_udp_hdr *udp_hdr = NULL;  /* forced to use pico_trans */
+  struct pico_ipv4_hdr *ipv4_hdr = (struct pico_ipv4_hdr *)f->net_hdr;
+  if (!ipv4_hdr)
+    return -1;
+  proto = ipv4_hdr->proto;
+  do {
+    /* 1. generate valid new NAT port entry */
+    uint32_t rand = pico_rand();
+    pub_port = (uint16_t) (rand & 0xFFFFU);
+    pub_port = (uint16_t)(pub_port % (65535 - 1024)) + 1024U;
+        pub_port = short_be(pub_port);
+
+    /* 2. check if already in table, if no exit */
+    nat_dbg("NAT: check if generated port %u is free\n", short_be(pub_port));
+    if (pico_is_port_free(proto, pub_port, NULL, &pico_proto_ipv4))
+      break;
+  
+  } while (1);
+  nat_dbg("NAT: port %u is free\n", short_be(pub_port));
+    
+  if (proto == PICO_PROTO_TCP) {  
+    tcp_hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+    if (!tcp_hdr)
+      return -1;
+    nk->priv_port = tcp_hdr->trans.sport; 
+  } else if (proto == PICO_PROTO_UDP) {
+    udp_hdr = (struct pico_udp_hdr *) f->transport_hdr;
+    if (!udp_hdr)
+      return -1;
+    nk->priv_port = udp_hdr->trans.sport; 
+  } else if (proto == PICO_PROTO_ICMP4) {
+    nk->priv_port = (uint16_t)(ipv4_hdr->src.addr & 0x00FF); 
+    pub_port = (uint16_t)(ipv4_hdr->dst.addr & 0x00FF);
+    if (!pico_is_port_free(proto, pub_port, NULL, &pico_proto_ipv4))
+      return -1;
+  }
+
+  nk->pub_addr = pub_addr; /* get public ip address from device */
+  nk->pub_port = pub_port;
+  nk->priv_addr = ipv4_hdr->src;
+  nk->proto = ipv4_hdr->proto;
+  nk->del_flags = 0x0001; /* set conn active to 1 */
+  if (pico_ipv4_nat_add(nk->pub_addr, nk->pub_port, nk->priv_addr, nk->priv_port, nk->proto) < 0) {
+    return -1;
+  } else {
+    return 0;
+  }
+}
+
+
+static int pico_nat_tcp_checksum(struct pico_frame *f)
+{
+  struct pico_tcp_hdr *trans_hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+  struct pico_ipv4_hdr *net_hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+
+  struct tcp_pseudo_hdr_ipv4 pseudo;
+  if (!trans_hdr || !net_hdr)
+    return -1;
+
+  pseudo.src.addr = net_hdr->src.addr;
+  pseudo.dst.addr = net_hdr->dst.addr;
+  pseudo.res = 0;
+  pseudo.proto = PICO_PROTO_TCP;
+  pseudo.tcp_len = short_be(f->transport_len);
+
+  trans_hdr->crc = 0;
+  trans_hdr->crc = pico_dualbuffer_checksum(&pseudo, sizeof(struct tcp_pseudo_hdr_ipv4), trans_hdr, f->transport_len);
+  trans_hdr->crc = short_be(trans_hdr->crc);
+  return 0;
+}
+
+
+int pico_ipv4_nat_translate(struct pico_nat_key* nk, struct pico_frame* f)
+{
+  uint8_t proto;
+  struct pico_tcp_hdr *tcp_hdr = NULL;  /* forced to use pico_trans */
+  struct pico_udp_hdr *udp_hdr = NULL;  /* forced to use pico_trans */
+
+  struct pico_ipv4_hdr* ipv4_hdr = (struct pico_ipv4_hdr *)f->net_hdr;
+  if (!ipv4_hdr)
+    return -1;
+  proto = ipv4_hdr->proto;
+  
+  if (proto == PICO_PROTO_TCP) {
+    tcp_hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+    if (!tcp_hdr)
+      return -1;
+    tcp_hdr->trans.sport = nk->pub_port;
+  } else if (proto == PICO_PROTO_UDP) {  
+    udp_hdr = (struct pico_udp_hdr *) f->transport_hdr;
+    if (!udp_hdr)
+      return -1;
+    udp_hdr->trans.sport = nk->pub_port;
+  }
+
+  //if(f->proto == PICO_PROTO_ICMP){
+  //} XXX no action
+
+  ipv4_hdr->src = nk->pub_addr;
+
+  if (proto == PICO_PROTO_TCP) {
+    pico_nat_tcp_checksum(f);
+  } else if (proto == PICO_PROTO_UDP){
+    udp_hdr->crc = 0;
+    udp_hdr->crc = short_be(pico_udp_checksum_ipv4(f));
+  }
+
+  // pico_ipv4_checksum(f);
+  ipv4_hdr->crc = 0;
+  ipv4_hdr->crc = short_be(pico_checksum(ipv4_hdr, f->net_len));
+
+  return 0;
+}
+
+
+int pico_ipv4_nat_port_forward(struct pico_frame* f)
+{
+  struct pico_nat_key *nk = NULL;
+  struct pico_tcp_hdr *tcp_hdr = NULL;
+  struct pico_udp_hdr *udp_hdr = NULL; 
+  struct pico_icmp4_hdr *icmp_hdr = NULL;
+  struct pico_ipv4_hdr* ipv4_hdr;
+  uint16_t pub_port = 0; 
+  uint8_t proto;
+
+  ipv4_hdr = (struct pico_ipv4_hdr *)f->net_hdr;
+  if (!ipv4_hdr)
+    return -1; 
+  proto = ipv4_hdr->proto; 
+  
+  if (proto == PICO_PROTO_TCP) {
+    tcp_hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+    if (!tcp_hdr)
+      return -1;
+    pub_port = tcp_hdr->trans.dport;  
+  } else if (proto == PICO_PROTO_UDP) {  
+    udp_hdr = (struct pico_udp_hdr *) f->transport_hdr;
+    if (!udp_hdr)
+      return -1;
+    pub_port = udp_hdr->trans.dport;
+  } else if (proto == PICO_PROTO_ICMP4) {
+    icmp_hdr = (struct pico_icmp4_hdr *) f->transport_hdr;
+    if (!icmp_hdr)
+      return -1;
+    /* XXX PRELIMINARY ONLY LAST 16 BITS OF IP */
+    pub_port = (uint16_t)(ipv4_hdr->src.addr & 0x00FF);
+  }
+
+  nk = pico_ipv4_nat_find_key(pub_port, 0, 0, proto);
+
+  if (!nk) {
+    nat_dbg("\nNAT: ERROR key not found in table\n");
+    return -1;
+  } else {
+    pico_ipv4_nat_snif_forward(nk,f);
+    ipv4_hdr->dst.addr = nk->priv_addr.addr;
+
+    if (proto == PICO_PROTO_TCP) {
+       tcp_hdr->trans.dport = nk->priv_port;
+       pico_nat_tcp_checksum(f);
+    } else if (proto == PICO_PROTO_UDP) {
+      udp_hdr->trans.dport = nk->priv_port;
+      udp_hdr->crc = 0;
+      udp_hdr->crc = short_be(pico_udp_checksum_ipv4(f));
+    }
+  }
+
+  ipv4_hdr->crc = 0;
+  ipv4_hdr->crc = short_be(pico_checksum(ipv4_hdr, f->net_len));
+ 
+  return 0; 
+}
+
+
+
+int pico_ipv4_nat(struct pico_frame *f, struct pico_ip4 pub_addr)
+{
+  /*do nat---------*/
+  struct pico_nat_key *nk = NULL;
+  struct pico_nat_key key;
+  struct pico_ipv4_hdr *net_hdr = (struct pico_ipv4_hdr *) f->net_hdr; 
+  struct pico_tcp_hdr *tcp_hdr = NULL;  
+  struct pico_udp_hdr *udp_hdr = NULL;  
+  int ret;
+  uint8_t proto = net_hdr->proto;
+  uint16_t priv_port = 0;
+  struct pico_ip4 priv_addr= net_hdr->src;
+
+  nk= &key;
+
+  /* TODO DELME check if IN */
+  if (pub_addr.addr == net_hdr->dst.addr) {
+    nat_dbg("NAT: backward translation {dst.addr, dport}: {%08X,%u} ->", net_hdr->dst.addr, ((struct pico_trans *)f->transport_hdr)->dport);
+    ret = pico_ipv4_nat_port_forward(f);  /* our IN definition */
+    nat_dbg(" {%08X,%u}\n", net_hdr->dst.addr, short_be(((struct pico_trans *)f->transport_hdr)->dport));
+  } else {
+    if (net_hdr->proto == PICO_PROTO_TCP) {
+      tcp_hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+      priv_port = tcp_hdr->trans.sport;
+    } else if (net_hdr->proto == PICO_PROTO_UDP) {
+      udp_hdr = (struct pico_udp_hdr *) f->transport_hdr;
+      priv_port = udp_hdr->trans.sport;
+    } else if (net_hdr->proto == PICO_PROTO_ICMP4) {
+      //udp_hdr = (struct pico_udp_hdr *) f->transport_hdr;
+      priv_port = (uint16_t)(net_hdr->src.addr & 0x00FF);
+    }
+
+    ret = pico_ipv4_nat_find(0, &priv_addr, priv_port, proto);
+    if (ret >= 0) {
+      // Key is available in table
+      nk = pico_ipv4_nat_find_key(0, &priv_addr, priv_port, proto);
+    } else {
+      nat_dbg("NAT: key not found in NAT table -> generate key\n");
+      pico_ipv4_nat_generate_key(nk, f, pub_addr);
+    }
+    pico_ipv4_nat_snif_backward(nk,f);
+    nat_dbg("NAT: forward translation {src.addr, sport}: {%08X,%u} ->", net_hdr->src.addr, short_be(((struct pico_trans *)f->transport_hdr)->sport));
+    pico_ipv4_nat_translate(nk, f); /* our OUT definition */
+    nat_dbg(" {%08X,%u}\n", net_hdr->src.addr, short_be(((struct pico_trans *)f->transport_hdr)->sport));
+  } 
+  return 0;
+}
+
+
+int pico_ipv4_nat_enable(struct pico_ipv4_link *link)
+{
+  if (link == NULL) {
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+
+  pub_link = *link;
+  pico_timer_add(NAT_TCP_TIMEWAIT, pico_ipv4_nat_table_cleanup, NULL);
+  enable_nat_flag = 1;
+  return 0;
+}
+ 
+int pico_ipv4_nat_disable(void)
+{
+  pub_link.address.addr = 0;
+  enable_nat_flag = 0;   
+  return 0;
+}
+
+
+int pico_ipv4_nat_isenabled_out(struct pico_ipv4_link *link)
+{
+  if (enable_nat_flag) {
+    // is pub_link = *link
+    if (pub_link.address.addr == link->address.addr)
+      return 0;
+    else
+      return -1;
+  } else {
+    return -1;
+  }
+}
+
+
+int pico_ipv4_nat_isenabled_in(struct pico_frame *f)
+{
+  if (enable_nat_flag) {
+    struct pico_tcp_hdr *tcp_hdr = NULL;
+    struct pico_udp_hdr *udp_hdr = NULL;
+    uint16_t pub_port = 0;
+    int ret;
+    uint8_t proto;
+ 
+    struct pico_ipv4_hdr *ipv4_hdr = (struct pico_ipv4_hdr *) f->net_hdr; 
+    if (!ipv4_hdr)
+      return -1;
+    proto = ipv4_hdr->proto;    
+
+    if (proto == PICO_PROTO_TCP) {
+      tcp_hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+      if (!tcp_hdr)
+        return -1;
+      pub_port= tcp_hdr->trans.dport;
+    } else if (proto == PICO_PROTO_UDP) {
+      udp_hdr = (struct pico_udp_hdr *) f->transport_hdr;
+      if (!udp_hdr)
+        return -1;
+      pub_port= udp_hdr->trans.dport;
+    } else if (proto == PICO_PROTO_ICMP4) {
+      //icmp_hdr = (struct pico_icmp4_hdr *) f->transport_hdr;
+      //if (!icmp_hdr)
+      //  return -1;
+      /* XXX PRELIMINARY ONLY LAST 16 BITS OF IP */
+      pub_port = (uint16_t)(ipv4_hdr->src.addr & 0x00FF);
+    }
+    ret = pico_ipv4_nat_find(pub_port, NULL, 0, proto);
+    if (ret == 0)
+      return 0;
+    else
+      return -1;
+  } else {
+    return -1;    
+  }
+}
+#endif
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_nat.h	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,88 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+.
+  
+Authors: Kristof Roelants, Simon Maes, Brecht Van Cauwenberghe
+*********************************************************************/
+
+#ifndef _INCLUDE_PICO_NAT
+#define _INCLUDE_PICO_NAT
+#include "pico_frame.h"
+
+#define PICO_DEL_FLAGS_FIN_FORWARD   (0x8000)
+#define PICO_DEL_FLAGS_FIN_BACKWARD  (0x4000)
+#define PICO_DEL_FLAGS_SYN           (0x2000)
+#define PICO_DEL_FLAGS_RST           (0x1000)
+
+#define PICO_IPV4_FORWARD_DEL 0
+#define PICO_IPV4_FORWARD_ADD 1
+
+#ifdef PICO_SUPPORT_NAT
+void pico_ipv4_nat_print_table(void);
+int pico_ipv4_nat_add(struct pico_ip4 pub_addr, uint16_t pub_port, struct pico_ip4 priv_addr, uint16_t priv_port, uint8_t proto);
+int pico_ipv4_nat_del(uint16_t pub_port, uint8_t proto);
+int pico_ipv4_nat_find(uint16_t pub_port, struct pico_ip4 *priv_addr, uint16_t priv_port, uint8_t proto);
+int pico_ipv4_port_forward(struct pico_ip4 pub_addr, uint16_t pub_port, struct pico_ip4 priv_addr, uint16_t priv_port, uint8_t proto, uint8_t persistant);
+
+int pico_ipv4_nat(struct pico_frame* f, struct pico_ip4 pub_addr);
+int pico_ipv4_nat_enable(struct pico_ipv4_link *link);
+int pico_ipv4_nat_isenabled_out(struct pico_ipv4_link *link);
+int pico_ipv4_nat_isenabled_in(struct pico_frame *f);
+
+#else
+
+static inline int pico_ipv4_nat_isenabled_out(struct pico_ipv4_link *link)
+{
+  pico_err = PICO_ERR_EPROTONOSUPPORT;
+  return -1;
+}
+static inline int pico_ipv4_nat_isenabled_in(struct pico_frame *f)
+{
+  pico_err = PICO_ERR_EPROTONOSUPPORT;
+  return -1;
+}
+
+static inline int pico_ipv4_nat(struct pico_frame* f, struct pico_ip4 pub_addr)
+{
+  pico_err = PICO_ERR_EPROTONOSUPPORT;
+  return -1;
+}
+
+static inline int pico_ipv4_nat_enable(struct pico_ipv4_link *link)
+{
+  pico_err = PICO_ERR_EPROTONOSUPPORT;
+  return -1;
+}
+
+#define pico_ipv4_nat_print_table() do{}while(0)
+
+static inline int pico_ipv4_nat_add(struct pico_ip4 pub_addr, uint16_t pub_port, struct pico_ip4 priv_addr, uint16_t priv_port, uint8_t proto)
+{
+  pico_err = PICO_ERR_EPROTONOSUPPORT;
+  return -1;
+}
+
+static inline int pico_ipv4_nat_del(uint16_t pub_port, uint8_t proto)
+{
+  pico_err = PICO_ERR_EPROTONOSUPPORT;
+  return -1;
+}
+
+
+static inline int pico_ipv4_nat_find(uint16_t pub_port, struct pico_ip4 priv_addr, uint16_t priv_port, uint8_t proto)
+{
+  pico_err = PICO_ERR_EPROTONOSUPPORT;
+  return -1;
+}
+
+static inline int pico_ipv4_port_forward(struct pico_ip4 pub_addr, uint16_t pub_port, struct pico_ip4 priv_addr, uint16_t priv_port, uint8_t proto, uint8_t persistant)
+{
+  pico_err = PICO_ERR_EPROTONOSUPPORT;
+  return -1;
+}
+#endif
+
+#endif /* _INCLUDE_PICO_NAT */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_simple_http.c	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,128 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+Author: Andrei Carp <andrei.carp@tass.be>
+*********************************************************************/
+
+#include "pico_config.h"
+#include "pico_socket.h"
+#include "pico_tcp.h"
+#include "pico_ipv4.h"
+#include "pico_simple_http.h"
+
+/* The HTTP Server cannot be available without TCP support */
+#if (defined PICO_SUPPORT_HTTP) && (defined PICO_SUPPORT_IPV4) && (defined PICO_SUPPORT_TCP)
+
+#define HTTP_LISTEN_PORT    80u
+#define HTTP_BACKLOG             5u
+#define HTTP_HEADER_SIZE  256u
+
+#define HTTP_SUCCESS            0
+#define HTTP_ERROR                -1
+
+static struct pico_socket * httpServer = NULL;
+static char   httpResponse[] =
+"HTTP/1.0 200 OK\r\n\
+Content-Type: text/html\r\n\
+\r\n\
+<html><head>\r\n\
+<title>picoTCP Simple Http server</title>\r\n\
+</head>\r\n\
+<body>\r\n\
+<h1>Hello world from picoTCP !!</h1>\r\n\
+</body>\r\n";
+
+static void httpEventCbk(uint16_t ev, struct pico_socket *self)
+{
+    static struct pico_socket * client = NULL;
+    uint32_t peer;
+    uint16_t port;
+    int r;
+    char buffer[HTTP_HEADER_SIZE];
+
+    switch(ev)
+    {
+        case PICO_SOCK_EV_CONN :
+            if(!client)
+                client = pico_socket_accept(self, &peer, &port);
+            break;
+
+        case PICO_SOCK_EV_RD:
+            // do not check http integrity, just mark that the http header has arrived
+            // prepare to send the response
+            r = pico_socket_recvfrom(self, buffer, HTTP_HEADER_SIZE, &peer, &port);
+            if(r>0 && memcmp(buffer,"GET",3u) == 0u)
+            { // it is an http header asking for data, return data and close
+                pico_socket_write(self,httpResponse,sizeof(httpResponse));
+                pico_socket_close(self);
+            }
+            else
+            {
+                // kill the connection, invalid header
+                pico_socket_close(self);
+            }
+            break;
+
+        case PICO_SOCK_EV_ERR:
+        case PICO_SOCK_EV_CLOSE:
+            // free the used socket
+            client = NULL;
+            break;
+
+        default :
+            break;
+    }
+}
+
+int pico_startHttpServer(struct pico_ip4 * address)
+{
+
+    uint16_t localHttpPort = short_be(HTTP_LISTEN_PORT);
+
+    if(!pico_is_port_free(localHttpPort,PICO_PROTO_TCP, address, &pico_proto_ipv4))
+    {
+        pico_err = PICO_ERR_EADDRINUSE;
+        return HTTP_ERROR;
+    }
+
+    httpServer = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_TCP, httpEventCbk);
+
+    if(!httpServer)
+    {
+        pico_err = PICO_ERR_ENOMEM;
+        return HTTP_ERROR;
+    }
+
+    // both functions set the pico_err themselves.
+    if(pico_socket_bind(httpServer,address,&localHttpPort))
+        return HTTP_ERROR;
+
+    if(pico_socket_listen(httpServer,HTTP_BACKLOG))
+        return HTTP_ERROR;
+
+    return HTTP_SUCCESS;
+}
+
+int pico_stopHttpServer(void)
+{
+    if(!httpServer)
+    {
+        pico_err = PICO_ERR_EINVAL;
+        return HTTP_ERROR;
+    }
+
+    if(pico_socket_close(httpServer))
+    {
+        // no need to set the error here, function already set it
+        httpServer = NULL;
+        return HTTP_ERROR;
+    }
+
+    httpServer = NULL;
+    return HTTP_SUCCESS;
+}
+
+#endif
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_simple_http.h	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,14 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+Author: Andrei Carp <andrei.carp@tass.be>
+*********************************************************************/
+
+#ifndef PICO_SIMPLE_HTTP
+#define PICO_SIMPLE_HTTP
+
+extern int pico_startHttpServer(struct pico_ip4 * address);
+extern int pico_stopHttpServer(void);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_tcp.c	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,2217 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+.
+
+Authors: Daniele Lacamera, Philippe Mariman
+*********************************************************************/
+
+#include "pico_tcp.h"
+#include "pico_config.h"
+#include "pico_eth.h"
+#include "pico_socket.h"
+#include "pico_stack.h"
+#include "pico_socket.h"
+#include "pico_queue.h"
+#include "pico_tree.h"
+
+#define TCP_SOCK(s) ((struct pico_socket_tcp *)s)
+#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 PICO_TCP_RTO_MIN 10
+#define PICO_TCP_RTO_MAX 120000
+#define PICO_TCP_IW          2
+#define PICO_TCP_SYN_TO  1000
+
+#define PICO_TCP_MAX_CONNECT_RETRIES 7
+
+#define PICO_TCP_LOOKAHEAD      0x00
+#define PICO_TCP_FIRST_DUPACK   0x01
+#define PICO_TCP_SECOND_DUPACK  0x02
+#define PICO_TCP_RECOVER        0x03
+#define PICO_TCP_BLACKOUT       0x04
+#define PICO_TCP_UNREACHABLE    0x05
+#define PICO_TCP_WINDOW_FULL    0x06
+
+/* check if the Nagle algorithm is enabled on the socket */
+#define IS_NAGLE_ENABLED(s)     (!(!(!(s->opt_flags & (1 << PICO_SOCKET_OPT_TCPNODELAY)))))
+/* check if tcp connection is "idle" according to Nagle (RFC 896) */
+#define IS_TCP_IDLE(t)          ((t->in_flight == 0) && (t->tcpq_out.size == 0))
+/* check if the hold queue contains data (again Nagle) */
+#define IS_TCP_HOLDQ_EMPTY(t)   (t->tcpq_hold.size == 0)
+
+#ifdef PICO_SUPPORT_TCP
+#define tcp_dbg_nagle(...) do{}while(0)
+#define tcp_dbg_options(...) do{}while(0)
+
+
+#define tcp_dbg(...) do{}while(0)
+//#define tcp_dbg dbg
+
+
+#ifdef PICO_SUPPORT_MUTEX
+static void * Mutex = NULL;
+#define LOCK(x) {\
+  if (x == NULL) \
+    x = pico_mutex_init(); \
+  pico_mutex_lock(x); \
+}
+#define UNLOCK(x) pico_mutex_unlock(x);
+
+#else
+#define LOCK(x) do{}while(0)
+#define UNLOCK(x) do{}while(0)
+#endif
+
+
+static inline int seq_compare(uint32_t a, uint32_t b)
+{
+  uint32_t thresh = ((uint32_t)(-1))>>1;
+  if (((a > thresh) && (b > thresh)) || ((a <= thresh) && (b <= thresh))) {
+    if (a > b)
+      return 1;
+    if (b > a)
+      return -1;
+  } else {
+    if (a > b)
+      return -2;
+    if (b > a)
+      return 2;
+  }
+  return 0;
+}
+
+static int segment_compare(void * ka, void * kb)
+{
+  struct pico_frame *a = ka, *b = kb;
+  return seq_compare(SEQN(a), SEQN(b));
+}
+
+struct pico_tcp_queue
+{
+  struct pico_tree pool;
+  uint32_t max_size;
+  uint32_t size;
+  uint32_t frames;
+};
+
+static struct pico_frame *peek_segment(struct pico_tcp_queue *tq, uint32_t seq)
+{
+  struct pico_tcp_hdr H;
+  struct pico_frame f = {};
+  f.transport_hdr = (uint8_t *) (&H);
+  H.seq = long_be(seq);
+
+  return pico_tree_findKey(&tq->pool,&f);
+}
+
+static struct pico_frame *first_segment(struct pico_tcp_queue *tq)
+{
+  return pico_tree_first(&tq->pool);
+}
+
+static struct pico_frame *next_segment(struct pico_tcp_queue *tq, struct pico_frame *cur)
+{
+  if (!cur)
+    return NULL;
+  return peek_segment(tq, SEQN(cur) + cur->payload_len);
+}
+
+static int pico_enqueue_segment(struct pico_tcp_queue *tq, struct pico_frame *f)
+{
+    int ret = -1;
+  if (f->payload_len <= 0) {
+    tcp_dbg("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TRIED TO ENQUEUE INVALID SEGMENT!\n");
+    //abort();
+    return -1;
+  }
+    LOCK(Mutex);
+  if ((tq->size + f->payload_len) > tq->max_size)
+  {
+    ret = 0;
+    goto out;
+  }
+  if (pico_tree_insert(&tq->pool,f) != 0)
+  {
+    ret = 0;
+    goto out;
+  }
+  tq->size += f->payload_len;
+  if (f->payload_len > 0)
+    tq->frames++;
+  ret = f->payload_len;
+
+out :
+  UNLOCK(Mutex);
+  return ret;
+}
+
+static void pico_discard_segment(struct pico_tcp_queue *tq, struct pico_frame *f)
+{
+  struct pico_frame *f1;
+  LOCK(Mutex);
+  f1 = pico_tree_delete(&tq->pool,f);
+  if (f1) {
+    tq->size -= f->payload_len;
+    if (f->payload_len > 0)
+      tq->frames--;
+  }
+  pico_frame_discard(f);
+  UNLOCK(Mutex);
+}
+
+/* Structure for TCP socket */
+struct tcp_sack_block {
+  uint32_t left;
+  uint32_t right;
+  struct tcp_sack_block *next;
+};
+
+struct pico_socket_tcp {
+  struct pico_socket sock;
+
+  /* Tree/queues */
+  struct pico_tcp_queue tcpq_in;
+  struct pico_tcp_queue tcpq_out;
+  struct pico_tcp_queue tcpq_hold;  /* buffer to hold delayed frames according to Nagle */
+
+  /* tcp_output */
+  uint32_t snd_nxt;
+  uint32_t snd_last;
+  uint32_t snd_old_ack;
+  uint32_t snd_retry;
+  uint32_t snd_last_out;
+
+  /* congestion control */
+  uint32_t avg_rtt;
+  uint32_t rttvar;
+  uint32_t rto;
+  uint32_t in_flight;
+  uint8_t  timer_running;
+  uint8_t  keepalive_timer_running;
+  uint16_t cwnd_counter;
+  uint16_t cwnd;
+  uint16_t ssthresh;
+  uint16_t recv_wnd;
+  uint16_t recv_wnd_scale;
+
+  /* tcp_input */
+  uint32_t rcv_nxt;
+  uint32_t rcv_ackd;
+  uint32_t rcv_processed;
+  uint16_t wnd;
+  uint16_t wnd_scale;
+
+  /* options */
+  uint32_t ts_nxt;
+  uint16_t mss;
+  uint8_t sack_ok;
+  uint8_t ts_ok;
+  uint8_t mss_ok;
+  uint8_t scale_ok;
+  struct tcp_sack_block *sacks;
+  uint8_t jumbo;
+
+  /* Transmission */
+  uint8_t  x_mode;
+  uint8_t  dupacks;
+  uint8_t  backoff;
+
+};
+
+/* Queues */
+static struct pico_queue tcp_in = {};
+static struct pico_queue tcp_out = {};
+
+/* If Nagle enabled, this function can make 1 new segment from smaller segments in hold queue */
+static struct pico_frame * pico_hold_segment_make(struct pico_socket_tcp *t);
+
+/* checks if tcpq_in is empty */
+int pico_tcp_queue_in_is_empty(struct pico_socket *s)
+{
+  struct pico_socket_tcp *t = (struct pico_socket_tcp *) s;
+
+  if (t->tcpq_in.frames == 0)
+    return 1;
+  else
+    return 0;
+}
+
+/* Useful for getting rid of the beginning of the buffer (read() op) */
+static int release_until(struct pico_tcp_queue *q, uint32_t seq)
+{
+  struct pico_frame *head = first_segment(q);
+  int ret = 0;
+  while (head && (seq_compare(SEQN(head) + head->payload_len, seq) <= 0)) {
+    struct pico_frame *cur = head;
+    head = next_segment(q, cur);
+    tcp_dbg("Releasing %p\n", q);
+    pico_discard_segment(q, cur);
+    ret++;
+  }
+  return ret;
+}
+
+static int release_all_until(struct pico_tcp_queue *q, uint32_t seq)
+{
+  struct pico_frame *f = NULL, *tmp __attribute__((unused));
+  struct pico_tree_node * idx, * temp;
+  int ret = 0;
+
+  pico_tree_foreach_safe(idx,&q->pool,temp){
+  f = idx->keyValue;
+    if (seq_compare(SEQN(f) + f->payload_len, seq) <= 0) {
+      tcp_dbg("Releasing %p\n", f);
+      pico_discard_segment(q, f);
+      ret++;
+    } else
+      return ret;
+  }
+  return ret;
+}
+
+/* API calls */
+
+uint16_t pico_tcp_checksum_ipv4(struct pico_frame *f)
+{
+  struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+  struct pico_tcp_hdr *tcp_hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+  struct pico_socket *s = f->sock;
+  struct pico_ipv4_pseudo_hdr pseudo;
+
+  if (s) {
+    /* Case of outgoing frame */
+    //dbg("TCP 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("TCP CRC: on incomming frame\n");
+    pseudo.src.addr = hdr->src.addr;
+    pseudo.dst.addr = hdr->dst.addr;
+  }
+  pseudo.zeros = 0;
+  pseudo.proto = PICO_PROTO_TCP;
+  pseudo.len = short_be(f->transport_len);
+
+  return pico_dualbuffer_checksum(&pseudo, sizeof(struct pico_ipv4_pseudo_hdr), tcp_hdr, f->transport_len);
+}
+
+static void tcp_send_fin(struct pico_socket_tcp *t);
+static int pico_tcp_process_out(struct pico_protocol *self, struct pico_frame *f)
+{
+  struct pico_tcp_hdr *hdr;
+  struct pico_socket_tcp *t = (struct pico_socket_tcp *)f->sock;
+  hdr = (struct pico_tcp_hdr *)f->transport_hdr;
+
+  if (f->payload_len > 0) {
+    tcp_dbg("Process out: sending %p (%d bytes)\n",f, f->payload_len);
+  } else {
+    tcp_dbg("Sending empty packet\n");
+  }
+
+  if (f->payload_len > 0) {
+    if (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);
+    }
+  } else if (hdr->flags == PICO_TCP_ACK) { /* pure ack */
+    //hdr->seq = long_be(t->snd_nxt);   /* XXX disabled this to not to mess with seq nrs of ACKs anymore */
+  } else {
+    tcp_dbg("%s: non-pure ACK with len=0, fl:%04x\n", __FUNCTION__, hdr->flags);
+  }
+
+  pico_network_send(f);
+  return 0;
+}
+
+int pico_tcp_push(struct pico_protocol *self, struct pico_frame *data);
+
+/* Interface: protocol definition */
+struct pico_protocol pico_proto_tcp = {
+  .name = "tcp",
+  .proto_number = PICO_PROTO_TCP,
+  .layer = PICO_LAYER_TRANSPORT,
+  .process_in = pico_transport_process_in,
+  .process_out = pico_tcp_process_out,
+  .push = pico_tcp_push,
+  .q_in = &tcp_in,
+  .q_out = &tcp_out,
+};
+
+static uint32_t pico_paws(void)
+{
+  static unsigned long _paws = 0;
+  _paws = pico_rand();
+  return long_be(_paws); /*XXX: implement paws */
+}
+
+static void tcp_add_options(struct pico_socket_tcp *ts, struct pico_frame *f, uint16_t flags, int optsiz)
+{
+  uint32_t tsval = long_be(pico_tick);
+  uint32_t tsecr = long_be(ts->ts_nxt);
+  int i = 0;
+  f->start = f->transport_hdr + PICO_SIZE_TCPHDR;
+
+  memset(f->start, PICO_TCP_OPTION_NOOP, optsiz); /* fill blanks with noop */
+
+  if (flags & PICO_TCP_SYN) {
+    f->start[i++] = PICO_TCP_OPTION_MSS;
+    f->start[i++] = PICO_TCPOPTLEN_MSS;
+    f->start[i++] = (ts->mss >> 8) & 0xFF;
+    f->start[i++] = ts->mss & 0xFF;
+    f->start[i++] = PICO_TCP_OPTION_SACK_OK;
+    f->start[i++] = PICO_TCPOPTLEN_SACK_OK;
+  }
+
+  f->start[i++] = PICO_TCP_OPTION_WS;
+  f->start[i++] = PICO_TCPOPTLEN_WS;
+  f->start[i++] = ts->wnd_scale;
+
+  if (optsiz >= 12) {
+    f->start[i++] = PICO_TCP_OPTION_TIMESTAMP;
+    f->start[i++] = PICO_TCPOPTLEN_TIMESTAMP;
+    memcpy(f->start + i, &tsval, 4);
+    i += 4;
+    memcpy(f->start + i, &tsecr, 4);
+    i += 4;
+  }
+
+  if (flags & PICO_TCP_ACK) {
+    struct tcp_sack_block *sb;
+    int 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 * sizeof(uint32_t));
+        f->start[len_off] += (2 * sizeof(uint32_t));
+        pico_free(sb);
+      }
+    }
+  }
+  if (i < optsiz)
+    f->start[ optsiz - 1 ] = PICO_TCP_OPTION_END;
+}
+
+static void tcp_send_ack(struct pico_socket_tcp *t);
+
+static void tcp_set_space(struct pico_socket_tcp *t)
+{
+  int mtu, space;
+  int shift = 0;
+
+  mtu = t->mss + PICO_SIZE_TCPHDR + PICO_SIZE_TCPOPT_SYN ;
+  if (t->tcpq_in.max_size == 0) {
+    space = 1024 * 1024 * 1024; /* One Gigabyte, for unlimited sockets. */
+  } else {
+    space = ((t->tcpq_in.max_size - t->tcpq_in.size) / mtu) * t->mss;
+  }
+  if (space < 0)
+    space = 0;
+  while(space > 0xFFFF) {
+    space >>= 1;
+    shift++;
+  }
+  if ((space != t->wnd) || (shift != t->wnd_scale) || ((space - t->wnd) > (space>>2))) {
+    t->wnd = space;
+    t->wnd_scale = shift;
+  }
+}
+
+/* Return 32-bit aligned option size */
+static int tcp_options_size(struct pico_socket_tcp *t, uint16_t flags)
+{
+  int size = 0;
+  struct tcp_sack_block *sb = t->sacks;
+
+  if (flags & PICO_TCP_SYN) {  /* Full options */
+    size = PICO_TCPOPTLEN_MSS + PICO_TCP_OPTION_SACK_OK + PICO_TCPOPTLEN_WS + PICO_TCPOPTLEN_TIMESTAMP;
+  } else {
+
+   /* Always update window scale. */
+    size += PICO_TCPOPTLEN_WS;
+
+    if (t->ts_ok)
+      size += PICO_TCPOPTLEN_TIMESTAMP;
+
+    size+= PICO_TCPOPTLEN_END;
+  }
+  if ((flags & PICO_TCP_ACK) && (t->sack_ok && sb)) {
+    size += 2;
+    while(sb) {
+      size += (2 * sizeof(uint32_t));
+      sb = sb->next;
+    }
+  }
+  size = (((size + 3) >> 2) << 2);
+  return size;
+}
+
+int pico_tcp_overhead(struct pico_socket *s)
+{
+  if (!s)
+    return 0;
+
+  return PICO_SIZE_TCPHDR + tcp_options_size((struct pico_socket_tcp *)s, 0); /* hdr + Options size for data pkt */
+
+}
+
+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;
+  int count = 0;
+
+  pico_tree_foreach_safe(index,&t->tcpq_out.pool,temp){
+    f = index->keyValue;
+    cmp = seq_compare(SEQN(f), start);
+    if (cmp > 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:
+  if (t->x_mode > PICO_TCP_LOOKAHEAD) {
+    if (t->in_flight > (count))
+      t->in_flight -= (count);
+    else
+      t->in_flight = 0;
+  }
+}
+
+static void tcp_rcv_sack(struct pico_socket_tcp *t, uint8_t *opt, int len)
+{
+  uint32_t start, end;
+  int i = 0;
+  if (len % 8) {
+    tcp_dbg("SACK: Invalid len.\n");
+    return;
+  }
+  while (i < len) {
+    start = long_from(opt + i);
+    i += 4;
+    end = long_from(opt + i);
+    i += 4;
+    tcp_process_sack(t, long_be(start), long_be(end));
+  }
+}
+
+static void tcp_parse_options(struct pico_frame *f)
+{
+  struct pico_socket_tcp *t = (struct pico_socket_tcp *)f->sock;
+  uint8_t *opt = f->transport_hdr + PICO_SIZE_TCPHDR;
+  int i = 0;
+  f->timestamp = 0;
+  while (i < (f->transport_len - PICO_SIZE_TCPHDR)) {
+    uint8_t type =  opt[i++];
+    uint8_t len;
+    if(i < (f->transport_len - PICO_SIZE_TCPHDR) && (type > 1))
+      len =  opt[i++];
+    else
+      len = 1;
+    if (f->payload && ((opt + i) > f->payload))
+      break;
+    tcp_dbg_options("Received option '%d', len = %d \n", type, len);
+    switch (type) {
+      case PICO_TCP_OPTION_NOOP:
+      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 += len - 2;
+          break;
+        }
+        t->recv_wnd_scale = opt[i++];
+        tcp_dbg_options("TCP Window scale: received %d\n", t->recv_wnd_scale);
+        break;
+      case PICO_TCP_OPTION_SACK_OK:
+        if (len != PICO_TCPOPTLEN_SACK_OK) {
+          tcp_dbg_options("TCP option sack: bad len received.\n");
+          i += len - 2;
+          break;
+        }
+        t->sack_ok = 1;
+        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 += len - 2;
+          break;
+        }
+        t->mss_ok = 1;
+        mss = short_from(opt + i);
+        i += sizeof(uint16_t);
+        if (t->mss > short_be(mss))
+          t->mss = short_be(mss);
+        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 += len - 2;
+          break;
+        }
+        t->ts_ok = 1;
+        tsval = long_from(opt + i);
+        i += sizeof(uint32_t);
+        tsecr = long_from(opt + i);
+        f->timestamp = long_be(tsecr);
+        i += sizeof(uint32_t);
+        t->ts_nxt = long_be(tsval);
+        break;
+      }
+      case PICO_TCP_OPTION_SACK:
+      {
+        tcp_rcv_sack(t, opt + i, len - 2);
+        i += len - 2;
+        break;
+      }
+      default:
+        tcp_dbg_options("TCP: received unsupported option %u\n", type);
+        i += len - 2;
+    }
+  }
+}
+
+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;
+  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)) {
+      hdr->flags |= PICO_TCP_ACK;
+      hdr->ack = long_be(ts->rcv_nxt);
+      ts->rcv_ackd = ts->rcv_nxt;
+    }
+  }
+
+  if (hdr->flags & PICO_TCP_SYN) {
+    ts->snd_nxt++;
+  }
+  if (f->payload_len > 0) {
+    hdr->flags |= PICO_TCP_PSH | PICO_TCP_ACK;
+    hdr->ack = long_be(ts->rcv_nxt);
+    ts->rcv_ackd = ts->rcv_nxt;
+    ts->keepalive_timer_running = 2;    /* XXX TODO check fix: added 1 to counter to postpone sending keepalive, ACK is in data segments */
+  }
+
+  f->start = f->transport_hdr + PICO_SIZE_TCPHDR;
+  hdr->rwnd = short_be(ts->wnd);
+  hdr->crc = 0;
+  hdr->crc = short_be(pico_tcp_checksum_ipv4(f));
+
+  /* TCP: ENQUEUE to PROTO ( Transmit ) */
+  cpy = pico_frame_copy(f);
+  if ((pico_enqueue(&tcp_out, cpy) > 0)) {
+    if (f->payload_len > 0) {
+      ts->in_flight++;
+      ts->snd_nxt += f->payload_len;  /* update next pointer here to prevent sending same segment twice when called twice in same tick */
+    }
+    tcp_dbg("DBG> [tcp output] state: %02x --> local port:%d remote port: %d seq: %08x ack: %08x flags: %02x = t_len: %d, hdr: %u payload: %d\n",
+      TCPSTATE(&ts->sock) >> 8, short_be(hdr->trans.sport), short_be(hdr->trans.dport), SEQN(f), ACKN(f), hdr->flags, f->transport_len, (hdr->len & 0xf0) >> 2 , f->payload_len );
+  } else {
+    pico_frame_discard(cpy);
+  }
+  return 0;
+}
+
+//#define PICO_TCP_SUPPORT_SOCKET_STATS
+
+#ifdef PICO_TCP_SUPPORT_SOCKET_STATS
+static void sock_stats(unsigned long when, void *arg)
+{
+  struct pico_socket_tcp *t = (struct pico_socket_tcp *)arg;
+  tcp_dbg("STATISTIC> [%lu] socket state: %02x --> local port:%d remote port: %d queue size: %d snd_una: %08x snd_nxt: %08x timer: %d cwnd: %d\n",
+    when, t->sock.state, short_be(t->sock.local_port), short_be(t->sock.remote_port), t->tcpq_out.size, SEQN(first_segment(&t->tcpq_out)), t->snd_nxt, t->timer_running, t->cwnd);
+  pico_timer_add(2000, sock_stats, t);
+}
+#endif
+
+struct pico_socket *pico_tcp_open(void)
+{
+  struct pico_socket_tcp *t = pico_zalloc(sizeof(struct pico_socket_tcp));
+  if (!t)
+    return NULL;
+  t->mss = PICO_TCP_DEFAULT_MSS;
+
+  t->tcpq_in.pool.root = t->tcpq_hold.pool.root = t->tcpq_out.pool.root = &LEAF;
+  t->tcpq_hold.pool.compare = t->tcpq_in.pool.compare = t->tcpq_out.pool.compare = segment_compare;
+
+  t->tcpq_in.max_size = PICO_DEFAULT_SOCKETQ;
+  t->tcpq_out.max_size = PICO_DEFAULT_SOCKETQ;
+  t->tcpq_hold.max_size = 2*PICO_TCP_DEFAULT_MSS;
+
+  /* disable Nagle by default */
+  t->sock.opt_flags |= (1 << PICO_SOCKET_OPT_TCPNODELAY);
+
+#ifdef PICO_TCP_SUPPORT_SOCKET_STATS
+  pico_timer_add(2000, sock_stats, t);
+#endif
+  tcp_set_space(t);
+
+  return &t->sock;
+}
+
+int pico_tcp_read(struct pico_socket *s, void *buf, int len)
+{
+  struct pico_socket_tcp *t = TCP_SOCK(s);
+  struct pico_frame *f;
+  uint32_t in_frame_off, in_frame_len;
+  int 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) {
+      tcp_set_space(t);
+      goto out;
+    }
+
+    /* Hole at the beginning of data, awaiting retransmissions. */
+    if (seq_compare(t->rcv_processed, SEQN(f)) < 0) {
+      tcp_dbg("TCP> read hole beginning of data, %08x - %08x. rcv_nxt is %08x\n",t->rcv_processed, SEQN(f), t->rcv_nxt);
+      goto out;
+    }
+
+    if(seq_compare(t->rcv_processed, SEQN(f)) > 0) {
+      in_frame_off = t->rcv_processed - SEQN(f);
+      in_frame_len = f->payload_len - in_frame_off;
+    } else {
+      in_frame_off = 0;
+      in_frame_len = f->payload_len;
+    }
+    if ((in_frame_len + tot_rd_len) > len) {
+      in_frame_len = len - tot_rd_len;
+    }
+
+    if (in_frame_len > f->payload_len - in_frame_off)
+      in_frame_len = f->payload_len - in_frame_off;
+
+    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 == 0) || (in_frame_len == f->payload_len)) {
+      pico_discard_segment(&t->tcpq_in, f);
+    }
+  }
+
+out:
+  tcp_set_space(t);
+  if (t->tcpq_in.size == 0) {
+    s->ev_pending &= (~PICO_SOCK_EV_RD);
+  }
+  return tot_rd_len;
+}
+
+int pico_tcp_initconn(struct pico_socket *s);
+static void initconn_retry(unsigned long when, void *arg)
+{
+  struct pico_socket_tcp *t = (struct pico_socket_tcp *)arg;
+  if (TCPSTATE(&t->sock) == PICO_SOCKET_STATE_TCP_SYN_SENT) {
+    if (t->backoff > PICO_TCP_MAX_CONNECT_RETRIES) {
+      tcp_dbg("TCP> Connection timeout. \n");
+      if (t->sock.wakeup)
+        t->sock.wakeup(PICO_SOCK_EV_ERR, &t->sock);
+      return;
+    }
+    tcp_dbg("TCP> SYN retry %d...\n", t->backoff);
+    t->backoff++;
+    pico_tcp_initconn(&t->sock);
+  } else {
+    tcp_dbg("TCP> Connection is already established: no retry needed. good.\n");
+  }
+}
+
+int pico_tcp_initconn(struct pico_socket *s)
+{
+  struct pico_socket_tcp *ts = TCP_SOCK(s);
+  struct pico_frame *syn;
+  struct pico_tcp_hdr *hdr;
+  int opt_len = tcp_options_size(ts, PICO_TCP_SYN);
+
+  syn = s->net->alloc(s->net, PICO_SIZE_TCPHDR + opt_len);
+  if (!syn)
+    return -1;
+  hdr = (struct pico_tcp_hdr *) syn->transport_hdr;
+
+  if (!ts->snd_nxt)
+    ts->snd_nxt = long_be(pico_paws());
+  ts->snd_last = ts->snd_nxt;
+  ts->cwnd = PICO_TCP_IW;
+  ts->ssthresh = 40;
+  syn->sock = s;
+  hdr->seq = long_be(ts->snd_nxt);
+  hdr->len = (PICO_SIZE_TCPHDR + opt_len) << 2 | ts->jumbo;
+  hdr->flags = PICO_TCP_SYN;
+  tcp_set_space(ts);
+  hdr->rwnd = short_be(ts->wnd);
+  tcp_add_options(ts,syn, PICO_TCP_SYN, opt_len);
+  hdr->trans.sport = ts->sock.local_port;
+  hdr->trans.dport = ts->sock.remote_port;
+
+  hdr->crc = 0;
+  hdr->crc = short_be(pico_tcp_checksum_ipv4(syn));
+
+  /* TCP: ENQUEUE to PROTO ( SYN ) */
+  tcp_dbg("Sending SYN... (ports: %d - %d) size: %d\n", short_be(ts->sock.local_port), short_be(ts->sock.remote_port), syn->buffer_len);
+  pico_enqueue(&tcp_out, syn);
+  pico_timer_add(PICO_TCP_SYN_TO << ts->backoff, initconn_retry, ts);
+  return 0;
+}
+
+static int tcp_send_synack(struct pico_socket *s)
+{
+  struct pico_socket_tcp *ts = TCP_SOCK(s);
+  struct pico_frame *synack;
+  struct pico_tcp_hdr *hdr;
+  int opt_len = tcp_options_size(ts, PICO_TCP_SYN | PICO_TCP_ACK);
+
+  synack = s->net->alloc(s->net, PICO_SIZE_TCPHDR + opt_len);
+  if (!synack)
+    return -1;
+  hdr = (struct pico_tcp_hdr *) synack->transport_hdr;
+
+  synack->sock = s;
+  hdr->len = (PICO_SIZE_TCPHDR + opt_len) << 2 | ts->jumbo;
+  hdr->flags = PICO_TCP_SYN | PICO_TCP_ACK;
+  hdr->rwnd = short_be(ts->wnd);
+  hdr->seq = long_be(ts->snd_nxt);
+  ts->rcv_processed = long_be(hdr->seq);
+  ts->snd_last = ts->snd_nxt;
+  tcp_set_space(ts);
+  tcp_add_options(ts,synack, hdr->flags, opt_len);
+  synack->payload_len = 0;
+  synack->timestamp = pico_tick;
+  tcp_send(ts, synack);
+  pico_frame_discard(synack);
+  return 0;
+}
+
+static void tcp_send_empty(struct pico_socket_tcp *t, uint16_t flags)
+{
+  struct pico_frame *f;
+  struct pico_tcp_hdr *hdr;
+  int opt_len = tcp_options_size(t, flags);
+  f = t->sock.net->alloc(t->sock.net, PICO_SIZE_TCPHDR + opt_len);
+  if (!f) {
+    return;
+  }
+  f->sock = &t->sock;
+  hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+  hdr->len = (PICO_SIZE_TCPHDR + opt_len) << 2 | t->jumbo;
+  hdr->flags = flags;
+  hdr->rwnd = short_be(t->wnd);
+  tcp_set_space(t);
+  tcp_add_options(t,f, flags, opt_len);
+  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)
+    hdr->ack = long_be(t->rcv_nxt);
+  t->rcv_ackd = t->rcv_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_ipv4(f));
+
+  /* TCP: ENQUEUE to PROTO */
+  pico_enqueue(&tcp_out, f);
+}
+
+static void tcp_send_ack(struct pico_socket_tcp *t)
+{
+  return tcp_send_empty(t, PICO_TCP_ACK);
+}
+
+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_frame *f;
+  struct pico_tcp_hdr *hdr, *hdr_rcv;
+  int opt_len = tcp_options_size(t, PICO_TCP_RST);
+  int close;
+
+  tcp_dbg("TCP SEND_RST >>>>>>>>>>>>>>> START\n");
+
+  f = t->sock.net->alloc(t->sock.net, PICO_SIZE_TCPHDR + opt_len);
+
+  if (!f) {
+    return -1;
+  }
+
+  hdr_rcv = (struct pico_tcp_hdr *) fr->transport_hdr;
+
+  f->sock = &t->sock;
+  hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+  hdr->len = (PICO_SIZE_TCPHDR + opt_len) << 2 | t->jumbo;
+  hdr->flags = PICO_TCP_RST;
+  hdr->rwnd = short_be(t->wnd);
+  tcp_set_space(t);
+  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->ack = long_be(t->rcv_nxt);
+  t->rcv_ackd = t->rcv_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_ipv4(f));
+
+  /* TCP: ENQUEUE to PROTO */
+  pico_enqueue(&tcp_out, f);
+
+  /* goto CLOSED */
+  if (close) {
+    (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);
+
+    tcp_dbg("TCP SEND_RST >>>>>>>>>>>>>>> DONE, deleted socket\n");
+  }
+
+  return 0;
+}
+
+int pico_tcp_reply_rst(struct pico_frame *fr)
+{
+  struct pico_tcp_hdr *hdr;
+  struct pico_frame *f;
+  int size = PICO_SIZE_TCPHDR;
+
+  tcp_dbg("TCP>>>>>>>>>>>>>>>> sending RST ... <<<<<<<<<<<<<<<<<<\n");
+
+  f = fr->sock->net->alloc(fr->sock->net, size);
+
+  /* fill in IP data from original frame */
+  // TODO if IPv4
+  ((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;
+
+  /* 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;
+  hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+  hdr->len   = size << 2;
+  hdr->flags = PICO_TCP_RST | 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 = ((struct pico_tcp_hdr *)(fr->transport_hdr))->seq + short_be(fr->payload_len);
+
+  /* enqueue for transmission */
+  pico_ipv4_frame_push(f,&(((struct pico_ipv4_hdr *)(f->net_hdr))->dst),PICO_PROTO_TCP);
+
+  return 0;
+}
+
+static int tcp_nosync_rst(struct pico_socket *s, struct pico_frame *fr)
+{
+  struct pico_socket_tcp *t = (struct pico_socket_tcp *) s;
+  struct pico_frame *f;
+  struct pico_tcp_hdr *hdr, *hdr_rcv;
+  int opt_len = tcp_options_size(t, PICO_TCP_RST | PICO_TCP_ACK);
+
+  tcp_dbg("TCP SEND RST (NON-SYNC) >>>>>>>>>>>>>>>>>> state %x\n",(s->state & PICO_SOCKET_STATE_TCP));
+  if (((s->state & PICO_SOCKET_STATE_TCP) ==  PICO_SOCKET_STATE_TCP_LISTEN)) {
+    /* XXX TODO NOTE: to prevent the parent socket from trying to send, because this socket has no knowledge of dst IP !!! */
+    return pico_tcp_reply_rst(fr);
+  }
+
+  /***************************************************************************/
+  /* sending RST */
+  f = t->sock.net->alloc(t->sock.net, PICO_SIZE_TCPHDR + opt_len);
+
+  if (!f) {
+    return -1;
+  }
+
+  hdr_rcv = (struct pico_tcp_hdr *) fr->transport_hdr;
+
+  f->sock = &t->sock;
+  hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+  hdr->len = (PICO_SIZE_TCPHDR + opt_len) << 2 | t->jumbo;
+  hdr->flags = PICO_TCP_RST | PICO_TCP_ACK;
+  hdr->rwnd = short_be(t->wnd);
+  tcp_set_space(t);
+  tcp_add_options(t,f, PICO_TCP_RST | PICO_TCP_ACK, opt_len);
+  hdr->trans.sport = t->sock.local_port;
+  hdr->trans.dport = t->sock.remote_port;
+
+  /* non-synchronized state */
+  if (hdr_rcv->flags & PICO_TCP_ACK) {
+    hdr->seq = hdr_rcv->ack;
+  } else {
+    hdr->seq = 0U;
+  }
+
+  hdr->ack = hdr_rcv->seq + short_be(fr->payload_len);
+
+  t->rcv_ackd = t->rcv_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_ipv4(f));
+
+  /* TCP: ENQUEUE to PROTO */
+  pico_enqueue(&tcp_out, f);
+
+  /***************************************************************************/
+
+  tcp_dbg("TCP SEND_RST (NON_SYNC) >>>>>>>>>>>>>>> DONE, ...\n");
+
+  return 0;
+}
+
+static void tcp_send_fin(struct pico_socket_tcp *t)
+{
+  struct pico_frame *f;
+  struct pico_tcp_hdr *hdr;
+  int opt_len = tcp_options_size(t, PICO_TCP_FIN);
+  f = t->sock.net->alloc(t->sock.net, PICO_SIZE_TCPHDR + opt_len);
+  if (!f) {
+    return;
+  }
+  f->sock = &t->sock;
+  hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+  hdr->len = (PICO_SIZE_TCPHDR + opt_len) << 2 | t->jumbo;
+  hdr->flags = PICO_TCP_FIN | PICO_TCP_ACK;
+  hdr->ack = long_be(t->rcv_nxt);
+  t->rcv_ackd = t->rcv_nxt;
+  hdr->rwnd = short_be(t->wnd);
+  tcp_set_space(t);
+  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 */
+
+  f->start = f->transport_hdr + PICO_SIZE_TCPHDR;
+  hdr->rwnd = short_be(t->wnd);
+  hdr->crc = 0;
+  hdr->crc = short_be(pico_tcp_checksum_ipv4(f));
+  //tcp_dbg("SENDING FIN...\n");
+  /* TCP: ENQUEUE to PROTO ( Pure ACK ) */
+  pico_enqueue(&tcp_out, f);
+  t->snd_nxt++;
+}
+
+static void tcp_sack_prepare(struct pico_socket_tcp *t)
+{
+  struct pico_frame *pkt;
+  uint32_t left=0, right=0;
+  struct tcp_sack_block *sb;
+  int n = 0;
+  if (t->sacks) /* previous sacks are pending */
+    return;
+
+  pkt = first_segment(&t->tcpq_in);
+  while(n < 3) {
+    if (!pkt) {
+      if(left) {
+        sb = pico_zalloc(sizeof(struct tcp_sack_block));
+        if (!sb)
+          break;
+        sb->left = long_be(left);
+        sb->right = long_be(right);
+        n++;
+        sb->next = t->sacks;
+        t->sacks = sb;
+        left = 0;
+        right = 0;
+      }
+      break;
+    }
+    if ((SEQN(pkt) < t->rcv_nxt)) {
+      pkt = next_segment(&t->tcpq_in, pkt);
+      continue;
+    }
+    if (!left) {
+      left = SEQN(pkt);
+      right = SEQN(pkt) + pkt->payload_len;
+      pkt = next_segment(&t->tcpq_in, pkt);
+      continue;
+    }
+    if(SEQN(pkt) == (right + 1)) {
+      right += pkt->payload_len;
+      pkt = next_segment(&t->tcpq_in, pkt);
+      continue;
+    } else {
+      sb = pico_zalloc(sizeof(struct tcp_sack_block));
+      if (!sb)
+        break;
+      sb->left = long_be(left);
+      sb->right = long_be(right);
+      n++;
+      sb->next = t->sacks;
+      t->sacks = sb;
+      left = 0;
+      right = 0;
+      pkt = next_segment(&t->tcpq_in, pkt);
+    }
+  }
+}
+
+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;
+
+  if (((hdr->len & 0xf0) >> 2) <= f->transport_len) {
+    tcp_parse_options(f);
+    f->payload = f->transport_hdr + ((hdr->len & 0xf0) >>2);
+    f->payload_len = f->transport_len - ((hdr->len & 0xf0) >>2);
+    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 pico_frame *nxt;
+      if (seq_compare(SEQN(f), t->rcv_nxt) == 0) { /* Exactly what we expected */
+        struct pico_frame *cpy = pico_frame_copy(f);
+        /* Enqueue: try to put into RCV buffer */
+        if(pico_enqueue_segment(&t->tcpq_in, cpy) <= 0) {
+          pico_frame_discard(cpy);
+        }
+        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 += f->payload_len;
+          nxt = peek_segment(&t->tcpq_in, t->rcv_nxt);
+        }
+        t->sock.ev_pending |= PICO_SOCK_EV_RD;
+        t->rcv_nxt = SEQN(f) + f->payload_len;
+      } else {
+        tcp_dbg("TCP> lo segment. Uninteresting retransmission. (exp: %x got: %x)\n", t->rcv_nxt, SEQN(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) {
+        tcp_sack_prepare(t);
+      }
+    }
+    /* 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);
+      tcp_send_ack(t);
+    } else {
+      //tcp_dbg("SENDACK PREVENTED IN SYNSENT STATE\n");
+    }
+    return 0;
+  } else {
+    tcp_dbg("TCP: invalid data in pkt len, exp: %d, got %d\n", (hdr->len & 0xf0) >> 2, f->transport_len);
+    return -1;
+  }
+}
+
+static int tcp_ack_advance_una(struct pico_socket_tcp *t, struct pico_frame *f)
+{
+  int ret =  release_all_until(&t->tcpq_out, ACKN(f));
+  if (ret > 0)
+    t->sock.ev_pending |= PICO_SOCK_EV_WR;
+  return ret;
+}
+
+static uint16_t time_diff(unsigned long a, unsigned long b)
+{
+  if (a >= b)
+    return (a - b);
+  else
+    return (b - a);
+}
+
+static void tcp_rtt(struct pico_socket_tcp *t, uint32_t rtt)
+{
+
+  uint32_t avg = t->avg_rtt;
+  uint32_t rvar = t->rttvar;
+  if (!avg) {
+    /* This follows RFC2988
+     * (2.2) When the first RTT measurement R is made, the host MUST set
+     *
+     * SRTT <- R
+     * RTTVAR <- R/2
+     * RTO <- SRTT + max (G, K*RTTVAR)
+     */
+    t->avg_rtt = rtt;
+    t->rttvar = rtt >> 1;
+    t->rto = t->avg_rtt + (t->rttvar << 4);
+  } else {
+    int var = (t->avg_rtt - rtt);
+    if (var < 0)
+      var = 0-var;
+    /* RFC2988, section (2.3). Alpha and beta are the ones suggested. */
+
+    /* First, evaluate a new value for the rttvar */
+    t->rttvar <<= 2;
+    t->rttvar -= rvar;
+    t->rttvar += var;
+    t->rttvar >>= 2;
+
+    /* Then, calculate the new avg_rtt */
+    t->avg_rtt <<= 3;
+    t->avg_rtt -= avg;
+    t->avg_rtt += rtt;
+    t->avg_rtt >>= 3;
+
+    /* Finally, assign a new value for the RTO, as specified in the RFC, with K=4 */
+    t->rto = t->avg_rtt + (t->rttvar << 2);
+  }
+  tcp_dbg(" -----=============== RTT CUR: %u AVG: %u RTTVAR: %u RTO: %u ======================----\n", rtt, t->avg_rtt, t->rttvar, t->rto);
+}
+
+static void tcp_congestion_control(struct pico_socket_tcp *t)
+{
+  if (t->x_mode > PICO_TCP_LOOKAHEAD)
+    return;
+  if (t->cwnd > t->tcpq_out.frames) {
+    tcp_dbg("Limited by app: %d\n", t->cwnd);
+    if (t->sock.wakeup)
+      t->sock.wakeup(PICO_SOCK_EV_WR, &t->sock);
+    return;
+  }
+  tcp_dbg("Doing congestion control\n");
+  if (t->cwnd < t->ssthresh) {
+    t->cwnd++;
+  } else {
+    t->cwnd_counter++;
+    if (t->cwnd_counter >= t->cwnd) {
+      t->cwnd++;
+      t->cwnd_counter = 0;
+    }
+  }
+  tcp_dbg("TCP_CWND, %lu, %u, %u, %u\n", pico_tick, t->cwnd, t->ssthresh, t->in_flight);
+}
+
+static void add_retransmission_timer(struct pico_socket_tcp *t, unsigned long next_ts);
+static void tcp_retrans_timeout(unsigned long val, void *sock)
+{
+  struct pico_socket_tcp *t = (struct pico_socket_tcp *) sock;
+  struct pico_frame *f = NULL;
+  unsigned long limit = val - t->rto;
+  struct pico_tcp_hdr *hdr;
+  if( t->sock.net && ((t->sock.state & 0xFF00) == PICO_SOCKET_STATE_TCP_ESTABLISHED
+        || (t->sock.state & 0xFF00) == PICO_SOCKET_STATE_TCP_CLOSE_WAIT) )
+  {
+        tcp_dbg("\n\nTIMEOUT! backoff = %d\n", t->backoff);
+        /* was timer cancelled? */
+        if (t->timer_running == 0) {
+            add_retransmission_timer(t, 0);
+            return;
+        }
+        t->timer_running--;
+
+        f = first_segment(&t->tcpq_out);
+        while (f) {
+            if ((t->x_mode == PICO_TCP_WINDOW_FULL) ||
+                    ((f->timestamp != 0) && (f->timestamp <= limit))) {
+                struct pico_frame *cpy;
+                hdr = (struct pico_tcp_hdr *)f->transport_hdr;
+                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);
+                if ((t->x_mode != PICO_TCP_WINDOW_FULL) ) {
+                    t->x_mode = PICO_TCP_BLACKOUT;
+                    tcp_dbg("Mode: Blackout.\n");
+                    t->cwnd = PICO_TCP_IW;
+                    t->in_flight = 0;
+                }
+                f->timestamp = pico_tick;
+                tcp_add_options(t, f, 0, f->transport_len - f->payload_len - PICO_SIZE_TCPHDR);
+                hdr->rwnd = short_be(t->wnd);
+                hdr->flags |= PICO_TCP_PSH;
+                hdr->ack = long_be(t->rcv_nxt);
+                hdr->crc = 0;
+                hdr->crc = short_be(pico_tcp_checksum_ipv4(f));
+                /* TCP: ENQUEUE to PROTO ( retransmit )*/
+                cpy = pico_frame_copy(f);
+                if (pico_enqueue(&tcp_out, cpy) > 0) {
+                    t->backoff++;
+                    add_retransmission_timer(t, (t->rto << t->backoff) + pico_tick);
+                    tcp_dbg("TCP_CWND, %lu, %u, %u, %u\n", pico_tick, t->cwnd, t->ssthresh, t->in_flight);
+                    return;
+                } else {
+                    add_retransmission_timer(t, (t->rto << t->backoff) + pico_tick);
+                    pico_frame_discard(cpy);
+                }
+            }
+            f = next_segment(&t->tcpq_out, f);
+        }
+        t->backoff = 0;
+        add_retransmission_timer(t, 0);
+        if (t->tcpq_out.size < t->tcpq_out.max_size)
+             t->sock.ev_pending |= PICO_SOCK_EV_WR;
+        return;
+    }
+}
+
+static void add_retransmission_timer(struct pico_socket_tcp *t, unsigned long next_ts)
+{
+  struct pico_tree_node * index;
+
+  if (t->timer_running > 0)
+    return;
+
+  if (next_ts == 0) {
+    struct pico_frame *f;
+
+    pico_tree_foreach(index,&t->tcpq_out.pool){
+      f = index->keyValue;
+      if (((next_ts == 0) || (f->timestamp < next_ts)) && (f->timestamp > 0)) {
+        next_ts = f->timestamp;
+      }
+    }
+  }
+  if (next_ts > 0) {
+    if ((next_ts + t->rto) > pico_tick) {
+      pico_timer_add(next_ts + t->rto - pico_tick, tcp_retrans_timeout, t);
+    } else {
+      pico_timer_add(1, tcp_retrans_timeout, t);
+    }
+    t->timer_running++;
+  }
+}
+
+static int tcp_retrans(struct pico_socket_tcp *t, struct pico_frame *f)
+{
+  struct pico_frame *cpy;
+  struct pico_tcp_hdr *hdr;
+  if (f) {
+    hdr = (struct pico_tcp_hdr *)f->transport_hdr;
+    tcp_dbg("TCP> RETRANS (by dupack) frame %08x, len= %d\n", SEQN(f), f->payload_len);
+    f->timestamp = pico_tick;
+    tcp_add_options(t, f, 0, f->transport_len - f->payload_len - PICO_SIZE_TCPHDR);
+    hdr->rwnd = short_be(t->wnd);
+    hdr->flags |= PICO_TCP_PSH;
+    hdr->ack = long_be(t->rcv_nxt);
+    hdr->crc = 0;
+    hdr->crc = short_be(pico_tcp_checksum_ipv4(f));
+    /* TCP: ENQUEUE to PROTO ( retransmit )*/
+    cpy = pico_frame_copy(f);
+    if (pico_enqueue(&tcp_out, cpy) > 0) {
+      t->in_flight++;
+      t->snd_last_out = SEQN(cpy);
+      add_retransmission_timer(t, pico_tick + t->rto);
+    } else {
+      pico_frame_discard(cpy);
+    }
+    return(f->payload_len);
+  }
+  return 0;
+}
+
+#ifdef TCP_ACK_DBG
+static void tcp_ack_dbg(struct pico_socket *s, struct pico_frame *f)
+{
+  uint32_t una, nxt, ack, cur;
+  struct pico_frame *una_f = NULL, *cur_f;
+  struct pico_tree_node *idx;
+  struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
+  char info[64];
+  char tmp[64];
+  ack = ACKN(f);
+  nxt = t->snd_nxt;
+  tcp_dbg("===================================\n");
+  tcp_dbg("Queue out (%d/%d). ACKED=%08x\n", t->tcpq_out.size, t->tcpq_out.max_size, ack);
+
+  pico_tree_foreach(idx, &t->tcpq_out.pool) {
+    info[0] = 0;
+    cur_f = idx->keyValue;
+    cur = SEQN(cur_f);
+    if (!una_f) {
+      una_f = cur_f;
+      una = SEQN(una_f);
+    }
+
+    if (cur == nxt) {
+      strncpy(tmp, info, strlen(info));
+      snprintf(info,64, "%s SND_NXT", tmp);
+    }
+    if (cur == ack) {
+      strncpy(tmp, info, strlen(info));
+      snprintf(info,64, "%s ACK", tmp);
+    }
+    if (cur == una) {
+      strncpy(tmp, info, strlen(info));
+      snprintf(info,64, "%s SND_UNA", tmp);
+    }
+    if (cur == t->snd_last) {
+      strncpy(tmp, info, strlen(info));
+      snprintf(info,64, "%s SND_LAST", tmp);
+    }
+    tcp_dbg("%08x %d%s\n", cur, cur_f->payload_len, info);
+
+  }
+  tcp_dbg("SND_NXT is %08x, snd_LAST is %08x", nxt, t->snd_last);
+  tcp_dbg("===================================\n");
+  tcp_dbg("\n\n");
+}
+#endif
+
+static int tcp_ack(struct pico_socket *s, struct pico_frame *f)
+{
+  struct pico_frame *f_new; /* use with Nagle to push to out queue */
+  struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
+  struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) f->transport_hdr;
+  uint32_t rtt = 0;
+  int acked = 0;
+  struct pico_frame *una = NULL;
+  if ((hdr->flags & PICO_TCP_ACK) == 0)
+    return -1;
+
+#ifdef TCP_ACK_DBG
+  tcp_ack_dbg(s,f);
+#endif
+
+  tcp_parse_options(f);
+  t->recv_wnd = short_be(hdr->rwnd);
+
+  acked = tcp_ack_advance_una(t, f);
+  una = first_segment(&t->tcpq_out);
+
+  if ((t->x_mode == PICO_TCP_BLACKOUT) || 
+    ((t->x_mode == PICO_TCP_WINDOW_FULL) && ((t->recv_wnd << t->recv_wnd_scale) > t->mss))) {
+    tcp_dbg("Re-entering look-ahead...\n\n\n");
+    t->x_mode = PICO_TCP_LOOKAHEAD;
+    t->backoff = 0;
+  }
+
+  /* One should be acked. */
+//  if ((acked == 0) && (t->in_flight > 0))
+  if ((acked == 0) && (f->payload_len  == 0) && (t->in_flight > 0))
+    t->in_flight--;
+  if (!una || acked > 0) {
+    t->x_mode = PICO_TCP_LOOKAHEAD;
+    tcp_dbg("Mode: Look-ahead. In flight: %d/%d buf: %d\n", t->in_flight, t->cwnd, t->tcpq_out.frames);
+    t->backoff = 0;
+
+    /* Do rtt/rttvar/rto calculations */
+    /* First, try with timestamps, using the value from options */
+    if(f && (f->timestamp != 0)) {
+      rtt = time_diff(pico_tick, f->timestamp);
+      if (rtt)
+        tcp_rtt(t, rtt);
+    } else if(una && (una->timestamp != 0)) {
+      /* If no timestamps are there, use conservatve estimation on the una */
+        rtt = time_diff(pico_tick, una->timestamp);
+        if (rtt)
+          tcp_rtt(t, rtt);
+    }
+
+    tcp_dbg("TCP ACK> FRESH ACK %08x (acked %d) Queue size: %u/%u frames: %u cwnd: %u in_flight: %u snd_una: %u\n", ACKN(f), acked, t->tcpq_out.size, t->tcpq_out.max_size, t->tcpq_out.frames, t->cwnd, t->in_flight, SEQN(una));
+    if (acked > t->in_flight) {
+      tcp_dbg("WARNING: in flight < 0\n");
+      t->in_flight = 0;
+    } else
+      t->in_flight -= (acked);
+
+  } else if ((t->snd_old_ack == ACKN(f)) && /* We've just seen this ack, and... */
+      ((0 == (hdr->flags & (PICO_TCP_PSH | PICO_TCP_SYN))) &&
+          (f->payload_len == 0)) && /* This is a pure ack, and... */
+      (ACKN(f) != t->snd_nxt)) /* There is something in flight awaiting to be acked... */
+  {
+    /* Process incoming duplicate ack. */
+    if (t->x_mode < PICO_TCP_RECOVER) {
+      t->x_mode++;
+      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->snd_retry = SEQN(first_segment(&t->tcpq_out));
+        if (t->ssthresh > t->cwnd)
+          t->ssthresh >>=2;
+        else
+          t->ssthresh = (t->cwnd >> 1);
+        if (t->ssthresh < 2)
+          t->ssthresh = 2;
+      }
+    } else if (t->x_mode == PICO_TCP_RECOVER) {
+     tcp_dbg("TCP RECOVER> DUPACK! snd_una: %08x, snd_nxt: %08x, acked now: %08x\n", SEQN(first_segment(&t->tcpq_out)), t->snd_nxt, ACKN(f));
+      if (t->in_flight <= t->cwnd) {
+        struct pico_frame *nxt = peek_segment(&t->tcpq_out, t->snd_retry);
+        if (!nxt)
+          nxt = first_segment(&t->tcpq_out);
+
+        while (nxt && (nxt->flags & PICO_FRAME_FLAG_SACKED) && (nxt != first_segment(&t->tcpq_out))) {
+          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)
+          nxt = NULL;
+        if (nxt && (seq_compare(SEQN(nxt), SEQN(first_segment(&t->tcpq_out))) > (t->recv_wnd << t->recv_wnd_scale)))
+          nxt = NULL;
+
+        if(!nxt)
+          nxt = first_segment(&t->tcpq_out);
+        if (nxt) {
+          tcp_retrans(t, peek_segment(&t->tcpq_out, t->snd_retry));
+          t->snd_retry = SEQN(nxt);
+        }
+      }
+
+      if (++t->cwnd_counter > 1) {
+        t->cwnd--;
+        if (t->cwnd < 2)
+          t->cwnd = 2;
+        t->cwnd_counter = 0;
+      }
+    } else {
+      tcp_dbg("DUPACK in mode %d \n", t->x_mode);
+
+    }
+  } /* End case duplicate ack detection */
+
+  /* Do congestion control */
+  tcp_congestion_control(t);
+  if ((acked > 0) && t->sock.wakeup) {
+    if (t->tcpq_out.size < t->tcpq_out.max_size)
+      t->sock.wakeup(PICO_SOCK_EV_WR, &(t->sock));
+      //t->sock.ev_pending |= PICO_SOCK_EV_WR;
+  }
+
+  /* if Nagle enabled, check if no unack'ed data and fill out queue (till window) */
+  if (IS_NAGLE_ENABLED((&(t->sock)))) {
+    while (!IS_TCP_HOLDQ_EMPTY(t) && ((t->tcpq_out.max_size - t->tcpq_out.size) >= PICO_TCP_DEFAULT_MSS)) {
+      tcp_dbg_nagle("TCP_ACK - NAGLE add new segment\n");
+      f_new = pico_hold_segment_make(t);
+      if (f_new == NULL)
+        break;            /* XXX corrupt !!! (or no memory) */
+      if (pico_enqueue_segment(&t->tcpq_out,f_new) <= 0)
+        // handle error
+        tcp_dbg_nagle("TCP_ACK - NAGLE FAILED to enqueue in out\n");
+    }
+  }
+
+  /* If some space was created, put a few segments out. */
+  tcp_dbg("TCP_CWND, %lu, %u, %u, %u\n", pico_tick, t->cwnd, t->ssthresh, t->in_flight);
+  if (t->x_mode ==  PICO_TCP_LOOKAHEAD) {
+    if ((t->cwnd >= t->in_flight) && (t->snd_nxt > t->snd_last_out)) {
+      pico_tcp_output(&t->sock, t->cwnd - t->in_flight);
+    }
+  }
+
+  t->snd_old_ack = ACKN(f);
+  return 0;
+}
+
+static int tcp_finwaitack(struct pico_socket *s, struct pico_frame *f)
+{
+  tcp_dbg("RECEIVED ACK IN FIN_WAIT1\nTCP> 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;
+
+  return 0;
+}
+
+static void tcp_deltcb(unsigned long when, void *arg)
+{
+  struct pico_socket_tcp *t = (struct pico_socket_tcp *)arg;
+  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);
+  } else {
+    tcp_dbg("TCP> trying to go to closed, wrong state\n");
+  }
+}
+
+static int tcp_finwaitfin(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);
+  tcp_dbg("TCP> received fin in FIN_WAIT2\n");
+  /* received FIN, increase ACK nr */
+  t->rcv_nxt = long_be(hdr->seq) + 1;
+  s->state &= 0x00FFU;
+  s->state |= PICO_SOCKET_STATE_TCP_TIME_WAIT;
+  /* set SHUT_REMOTE */
+  s->state |= PICO_SOCKET_STATE_SHUT_REMOTE;
+  if (s->wakeup)
+    s->wakeup(PICO_SOCK_EV_CLOSE, s);
+  if (f->payload_len > 0) /* needed?? */
+    tcp_data_in(s,f);
+  /* send ACK */
+  tcp_send_ack(t);
+  /* set timer */
+  pico_timer_add(200, tcp_deltcb, t);
+  return 0;
+}
+
+static int tcp_closewaitack(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);
+  return 0;
+}
+
+static int tcp_lastackwait(struct pico_socket *s, struct pico_frame *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);
+  return 0;
+}
+
+static int tcp_syn(struct pico_socket *s, struct pico_frame *f)
+{
+  /* TODO: Check against backlog length */
+  struct pico_socket_tcp *new = (struct pico_socket_tcp *)pico_socket_clone(s);
+  struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *)f->transport_hdr;
+  if (!new)
+    return -1;
+
+#ifdef PICO_TCP_SUPPORT_SOCKET_STATS
+  pico_timer_add(2000, sock_stats, s);
+#endif
+
+  new->sock.remote_port = ((struct pico_trans *)f->transport_hdr)->sport;
+#ifdef PICO_SUPPORT_IPV4
+  if (IS_IPV4(f)) {
+    new->sock.remote_addr.ip4.addr = ((struct pico_ipv4_hdr *)(f->net_hdr))->src.addr;
+    new->sock.local_addr.ip4.addr = ((struct pico_ipv4_hdr *)(f->net_hdr))->dst.addr;
+  }
+#endif
+#ifdef PICO_SUPPORT_IPV6
+  if (IS_IPV6(f)) {
+    new->sock.remote_addr.ip6 = ((struct pico_ipv6_hdr *)(f->net_hdr))->src;
+    new->sock.local_addr.ip6 = ((struct pico_ipv6_hdr *)(f->net_hdr))->dst;
+  }
+#endif
+
+  /* Set socket limits */
+  new->tcpq_in.max_size = PICO_DEFAULT_SOCKETQ;
+  new->tcpq_out.max_size = PICO_DEFAULT_SOCKETQ;
+  new->tcpq_hold.max_size = 2*PICO_TCP_DEFAULT_MSS;
+
+  f->sock = &new->sock;
+  tcp_parse_options(f);
+  new->mss = PICO_TCP_DEFAULT_MSS;
+  new->rcv_nxt = long_be(hdr->seq) + 1;
+  new->snd_nxt = long_be(pico_paws());
+  new->snd_last = new->snd_nxt;
+  new->cwnd = PICO_TCP_IW;
+  new->ssthresh = 40;
+  new->recv_wnd = short_be(hdr->rwnd);
+  new->jumbo = hdr->len & 0x07;
+  new->sock.parent = s;
+  new->sock.wakeup = s->wakeup;
+  /* 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);
+  tcp_send_synack(&new->sock);
+  tcp_dbg("SYNACK sent, socket added. snd_nxt is %08x\n", new->snd_nxt);
+  return 0;
+}
+
+static void tcp_set_init_point(struct pico_socket *s)
+{
+  struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
+  t->rcv_processed = t->rcv_nxt;
+}
+
+static int tcp_synack(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 (ACKN(f) ==  (1 + t->snd_nxt)) {
+    t->rcv_nxt = long_be(hdr->seq);
+    t->rcv_processed = t->rcv_nxt + 1;
+    tcp_ack(s, f);
+
+    s->state &= 0x00FFU;
+    s->state |= PICO_SOCKET_STATE_TCP_ESTABLISHED;
+    tcp_dbg("TCP> Established. State: %x\n", s->state);
+
+    if (s->wakeup)
+      s->wakeup(PICO_SOCK_EV_CONN, s);
+    s->ev_pending |= PICO_SOCK_EV_WR;
+
+    t->rcv_nxt++;
+    t->snd_nxt++;
+    tcp_send_ack(t);  /* return ACK */
+
+    return 0;
+
+  } else {
+    tcp_dbg("TCP> Not established, RST sent.\n");
+    tcp_nosync_rst(s,f);
+    return 0;
+  }
+}
+
+static int tcp_first_ack(struct pico_socket *s, struct pico_frame *f)
+{
+  struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
+  tcp_dbg("ACK in SYN_RECV: expecting %08x got %08x\n", t->snd_nxt, ACKN(f));
+  if (t->snd_nxt == ACKN(f)) {
+    tcp_set_init_point(s);
+    tcp_ack(s, f);
+    s->state &= 0x00FFU;
+    s->state |= PICO_SOCKET_STATE_TCP_ESTABLISHED;
+    tcp_dbg("TCP: Established. State now: %04x\n", s->state);
+    if( !s->parent && s->wakeup) { /* If the socket has no parent, -> sending socket that has a sim_open */
+        tcp_dbg("FIRST ACK - No parent found -> sending socket\n");
+        s->wakeup(PICO_SOCK_EV_CONN,  s);
+    }
+    if (s->parent && s->parent->wakeup) {
+      tcp_dbg("FIRST ACK - Parent found -> listening socket\n");
+      s->wakeup = s->parent->wakeup;
+      s->parent->wakeup(PICO_SOCK_EV_CONN, s->parent);
+    }
+    s->ev_pending |= PICO_SOCK_EV_WR;
+    tcp_dbg("%s: snd_nxt is now %08x\n", __FUNCTION__, t->snd_nxt);
+    return 0;
+  } else {
+    tcp_nosync_rst(s,f);
+    return 0;
+  }
+}
+
+static int tcp_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);
+  if (seq_compare(SEQN(f), t->rcv_nxt) == 0) {
+    /* received FIN, increase ACK nr */
+    t->rcv_nxt = long_be(hdr->seq) + 1;
+        s->state &= 0x00FFU;
+        s->state |= PICO_SOCKET_STATE_TCP_CLOSE_WAIT;
+        /* set SHUT_REMOTE */
+    s->state |= PICO_SOCKET_STATE_SHUT_REMOTE;
+      tcp_dbg("TCP> Close-wait\n");
+
+    if (s->wakeup){
+      if(f->payload_len>0){
+        struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
+        t->sock.ev_pending |=PICO_SOCK_EV_CLOSE;
+      }else
+        s->wakeup(PICO_SOCK_EV_CLOSE, s);
+    }
+  } else {
+    tcp_send_ack(t);  /* return ACK */
+  }
+  return 0;
+}
+
+/*static int tcp_fin(struct pico_socket *s, struct pico_frame *f)
+{
+  return 0;
+}*/
+
+static int tcp_rcvfin(struct pico_socket *s, struct pico_frame *f)
+{
+  struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
+  tcp_dbg("TCP> Received FIN in FIN_WAIT1\n");
+  s->state &= 0x00FFU;
+  s->state |= PICO_SOCKET_STATE_TCP_CLOSING;
+  t->rcv_processed = t->rcv_nxt + 1;
+  t->rcv_nxt++;
+  /* send ACK */
+  tcp_send_ack(t);
+  return 0;
+}
+
+static int tcp_finack(struct pico_socket *s, struct pico_frame *f)
+{
+  struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
+  tcp_dbg("TCP> ENTERED finack\n");
+  t->rcv_nxt++;
+  /* send ACK */
+  tcp_send_ack(t);
+
+  /* call socket wakeup with EV_FIN */
+  if (s->wakeup)
+    s->wakeup(PICO_SOCK_EV_FIN, s);
+  s->state &= 0x00FFU;
+  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);
+
+  return 0;
+}
+
+static int tcp_rst(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);
+
+  tcp_dbg("TCP >>>>>>>>>>>>>> received RST <<<<<<<<<<<<<<<<<<<<\n");
+  if ((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_SYN_SENT) {
+    /* the RST is acceptable if the ACK field acknowledges the SYN */
+    if ((t->snd_nxt + 1) == ACKN(f)) {  /* valid, got to closed 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));
+
+      /* call EV_ERR wakeup before deleting */
+      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);
+    } else {                      /* not valid, ignore */
+      tcp_dbg("TCP RST> IGNORE\n");
+      return 0;
+    }
+  } else {  /* all other states */
+    /* all reset (RST) segments are validated by checking their SEQ-fields,
+    a reset is valid if its sequence number is in the window */
+    if ((long_be(hdr->seq) >= t->rcv_ackd) && (long_be(hdr->seq) <= ((short_be(hdr->rwnd)<<(t->wnd_scale)) + t->rcv_ackd))) {
+      if ((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_SYN_RECV) {
+        /* go to closed */
+        (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_ERR wakeup */
+        pico_err = PICO_ERR_ECONNRESET;
+        if ((t->sock).wakeup)
+          (t->sock).wakeup(PICO_SOCK_EV_ERR, &(t->sock));
+        tcp_dbg("TCP RST> SOCKET BACK TO LISTEN\n");
+        pico_socket_del(s);
+      } else {
+        /* go to closed */
+        (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));
+        /* call EV_ERR wakeup before deleting */
+        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);
+      }
+    } else {                      /* not valid, ignore */
+      tcp_dbg("TCP RST> IGNORE\n");
+      return 0;
+    }
+  }
+
+  return 0;
+}
+
+struct tcp_action_entry {
+  uint16_t tcpstate;
+  int (*syn)(struct pico_socket *s, struct pico_frame *f);
+  int (*synack)(struct pico_socket *s, struct pico_frame *f);
+  int (*ack)(struct pico_socket *s, struct pico_frame *f);
+  int (*data)(struct pico_socket *s, struct pico_frame *f);
+  int (*fin)(struct pico_socket *s, struct pico_frame *f);
+  int (*finack)(struct pico_socket *s, struct pico_frame *f);
+  int (*rst)(struct pico_socket *s, struct pico_frame *f);
+};
+
+static struct tcp_action_entry tcp_fsm[] = {
+    /* State                            syn              synack             ack                data             fin              finack           rst*/
+  { PICO_SOCKET_STATE_TCP_UNDEF,        NULL,            NULL,              NULL,              NULL,            NULL,            NULL,            NULL     },
+  { PICO_SOCKET_STATE_TCP_CLOSED,       NULL,            NULL,              NULL,              NULL,            NULL,            NULL,            NULL     },
+  { PICO_SOCKET_STATE_TCP_LISTEN,       &tcp_syn,        &tcp_nosync_rst,   &tcp_nosync_rst,   &tcp_nosync_rst, &tcp_nosync_rst, &tcp_nosync_rst, NULL     },
+  { PICO_SOCKET_STATE_TCP_SYN_SENT,     &tcp_nosync_rst, &tcp_synack,       &tcp_nosync_rst,   &tcp_nosync_rst, &tcp_nosync_rst, &tcp_nosync_rst, &tcp_rst },
+  { PICO_SOCKET_STATE_TCP_SYN_RECV,     NULL,            &tcp_nosync_rst,   &tcp_first_ack,    &tcp_nosync_rst, &tcp_nosync_rst, &tcp_nosync_rst, &tcp_rst },
+  { PICO_SOCKET_STATE_TCP_ESTABLISHED,  NULL,            &tcp_ack,          &tcp_ack,          &tcp_data_in,    &tcp_closewait,  &tcp_closewait,  &tcp_rst },
+  { PICO_SOCKET_STATE_TCP_CLOSE_WAIT,   NULL,            &tcp_ack,          &tcp_ack,          &tcp_send_rst,   &tcp_closewait,  &tcp_closewait,  &tcp_rst },
+  { 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 }
+};
+
+/*
+   NOTE: in SYN-RECV receiving syn when cloned by default (see yellow pos-it), should send reset.
+*/
+
+int pico_tcp_input(struct pico_socket *s, struct pico_frame *f)
+{
+  struct pico_tcp_hdr *hdr = (struct pico_tcp_hdr *) (f->transport_hdr);
+  int ret = 0;
+  uint8_t flags = hdr->flags;
+  struct tcp_action_entry *action = &tcp_fsm[s->state >> 8];
+
+  f->payload = (f->transport_hdr + ((hdr->len & 0xf0) >> 2));
+  f->payload_len = f->transport_len - ((hdr->len & 0xf0) >> 2);
+
+  tcp_dbg("[%lu] TCP> [tcp input] socket: %p state: %d <-- local port:%d remote port: %d seq: %08x ack: %08x flags: %02x = t_len: %d, hdr: %u payload: %d\n", pico_tick,
+      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;
+
+  /* Those are not supported at this time. */
+  flags &= ~(PICO_TCP_CWR | PICO_TCP_URG | PICO_TCP_ECN);
+  if (flags == PICO_TCP_SYN) {
+    if (action->syn)
+      action->syn(s,f);
+  } else if (flags == (PICO_TCP_SYN | PICO_TCP_ACK)) {
+    if (action->synack)
+      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) {
+      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);
+    }
+  }
+
+//discard:
+  pico_frame_discard(f);
+  return ret;
+}
+
+static void tcp_send_keepalive(unsigned long when, void *_t)
+{
+  struct pico_socket_tcp *t = (struct pico_socket_tcp *)_t;
+  tcp_dbg("\n\nSending keepalive (%d), [State = %d]...\n", t->backoff,t->sock.state );
+  if( t->sock.net && ((t->sock.state & 0xFF00) == PICO_SOCKET_STATE_TCP_ESTABLISHED) )
+  {
+        tcp_send_ack(t);
+
+        if (t->keepalive_timer_running > 0) {
+            t->keepalive_timer_running--;
+        }
+
+        if (t->keepalive_timer_running == 0) {
+            t->keepalive_timer_running++;
+            tcp_dbg("[Self] Adding timer(retransmit keepalive)\n");
+            pico_timer_add(t->rto << (++t->backoff), tcp_send_keepalive, t);
+        }
+  }
+}
+
+int pico_tcp_output(struct pico_socket *s, int loop_score)
+{
+  struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
+  struct pico_frame *f, *una;
+  struct pico_tcp_hdr *hdr;
+  int sent = 0;
+
+  una = first_segment(&t->tcpq_out);
+
+  f = peek_segment(&t->tcpq_out, t->snd_nxt);
+  while((f) && (t->cwnd >= t->in_flight)) {
+    hdr = (struct pico_tcp_hdr *)f->transport_hdr;
+    f->timestamp = pico_tick;
+    tcp_add_options(t, f, hdr->flags, tcp_options_size(t, hdr->flags));
+    if (seq_compare(SEQN(f) + f->payload_len, SEQN(una) + (t->recv_wnd << t->recv_wnd_scale)) > 0) {
+      t->cwnd = t->in_flight;
+      if (t->cwnd < 1)
+        t->cwnd = 1;
+      if (t->x_mode != PICO_TCP_WINDOW_FULL) {
+        tcp_dbg("TCP> RIGHT SIZING (rwnd: %d, frame len: %d\n",t->recv_wnd << t->recv_wnd_scale, f->payload_len);
+        tcp_dbg("In window full...\n");
+        t->snd_nxt = SEQN(una);   /* XXX prevent out-of-order-packets ! */ /*DLA re-enabled.*/
+        t->snd_retry = SEQN(una);   /* XXX replace by retry pointer? */
+
+        /* Alternative to the line above:  (better performance, but seems to lock anyway with larger buffers)
+        if (seq_compare(t->snd_nxt, SEQN(una)) > 0)
+          t->snd_nxt -= f->payload_len;
+        */
+
+        t->x_mode = PICO_TCP_WINDOW_FULL;
+        if (t->keepalive_timer_running == 0) {
+          tcp_dbg("[Window full] Adding timer(send keepalive)\n");
+          tcp_send_keepalive(0, t);
+        }
+      }
+      break;
+    }
+    tcp_dbg("TCP> DEQUEUED (for output) frame %08x, acks %08x len= %d, remaining frames %d\n", SEQN(f), ACKN(f), f->payload_len,t->tcpq_out.frames);
+    tcp_send(t, f);
+    sent++;
+    loop_score--;
+    t->snd_last_out = SEQN(f);
+    if (loop_score < 1)
+      break;
+    if (f->payload_len > 0) {
+      f = next_segment(&t->tcpq_out, f);
+    } else {
+      f = NULL;
+    }
+  }
+  if (sent > 0) {
+    if (t->rto < PICO_TCP_RTO_MIN)
+      t->rto = PICO_TCP_RTO_MIN;
+    //if (s->wakeup)
+    //  t->sock.wakeup(PICO_SOCK_EV_WR, &t->sock);
+    add_retransmission_timer(t, pico_tick + t->rto);
+  } else {
+    /* 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 ((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_ESTABLISHED) {
+      tcp_dbg("TCP> buffer empty, shutdown established ...\n");
+      /* send fin if queue empty and in state shut local (write) */
+      tcp_send_fin(t);
+      /* change tcp state to FIN_WAIT1 */
+      s->state &= 0x00FFU;
+      s->state |= PICO_SOCKET_STATE_TCP_FIN_WAIT1;
+    } else if ((s->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_CLOSE_WAIT) {
+      /* send fin if queue empty and in state shut local (write) */
+      tcp_send_fin(t);
+      /* change tcp state to LAST_ACK */
+      s->state &= 0x00FFU;
+      s->state |= PICO_SOCKET_STATE_TCP_LAST_ACK;
+      tcp_dbg("TCP> STATE: LAST_ACK.\n");
+    }
+  }
+  return loop_score;
+}
+
+/* function to make new segment from hold queue with specific size (mss) */
+static struct pico_frame * pico_hold_segment_make(struct pico_socket_tcp *t)
+{
+  struct pico_frame *f_temp,*f_new;
+  struct pico_socket *s = (struct pico_socket *) &t->sock;
+  struct pico_tcp_hdr *hdr;
+  int total_len = 0, total_payload_len = 0;
+  int off = 0, test = 0;
+
+  off = pico_tcp_overhead(s);
+
+  /* init with first frame in hold queue */
+  f_temp = first_segment(&t->tcpq_hold);
+  total_len = f_temp->payload_len;
+  f_temp = next_segment(&t->tcpq_hold, f_temp);
+
+  /* check till total_len <= MSS */
+  while ((f_temp != NULL) && ((total_len+f_temp->payload_len) <= PICO_TCP_DEFAULT_MSS)) {
+    total_len += f_temp->payload_len;
+    f_temp = next_segment(&t->tcpq_hold, f_temp);
+    if (f_temp == NULL)
+      break;
+  }
+  /* alloc new frame with payload size = off + total_len */
+  f_new = pico_socket_frame_alloc(s, off + total_len);
+  if (!f_new) {
+    pico_err = PICO_ERR_ENOMEM;
+    return f_new;
+  }
+
+  hdr = (struct pico_tcp_hdr *) f_new->transport_hdr;
+  /* init new frame */
+  f_new->payload += off;
+  f_new->payload_len -= off;
+  f_new->sock = s;
+
+  f_temp = first_segment(&t->tcpq_hold);
+  hdr->seq = ((struct pico_tcp_hdr *)(f_temp->transport_hdr))->seq;  /* get sequence number of first frame */
+  hdr->trans.sport = t->sock.local_port;
+  hdr->trans.dport = t->sock.remote_port;
+
+  /* check till total_payload_len <= MSS */
+  while ((f_temp != NULL) && ((total_payload_len + f_temp->payload_len) <= PICO_TCP_DEFAULT_MSS)) {
+    /* cpy data and discard frame */
+    test++;
+    memcpy(f_new->payload + total_payload_len, f_temp->payload, f_temp->payload_len);
+    total_payload_len += f_temp->payload_len;
+    pico_discard_segment(&t->tcpq_hold, f_temp);
+    f_temp = first_segment(&t->tcpq_hold);
+  }
+
+  hdr->len = (f_new->payload - f_new->transport_hdr) << 2 | t->jumbo;
+
+  tcp_dbg_nagle("NAGLE make - joined %d segments, len %d bytes\n",test,total_payload_len);
+
+  return f_new;
+}
+
+/* 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;
+  int total_len = 0;
+
+  hdr->trans.sport = t->sock.local_port;
+  hdr->trans.dport = t->sock.remote_port;
+  hdr->seq = long_be(t->snd_last + 1);
+  hdr->len = (f->payload - f->transport_hdr) << 2 | t->jumbo;
+
+  if (f->payload_len > (t->tcpq_out.max_size - t->tcpq_out.size))
+    t->sock.ev_pending &= (~PICO_SOCK_EV_WR);
+
+  /***************************************************************************/
+
+  if (!IS_NAGLE_ENABLED((&(t->sock)))) {
+    /* TCP_NODELAY enabled, original behavior */
+    if (pico_enqueue_segment(&t->tcpq_out,f) > 0) {
+      tcp_dbg_nagle("TCP_PUSH - NO 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 {
+    /* 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 >= PICO_TCP_DEFAULT_MSS) && ((t->tcpq_out.max_size - t->tcpq_out.size) >= PICO_TCP_DEFAULT_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 {
+          tcp_dbg_nagle("TCP_PUSH - NAGLE - enqueue hold failed 2\n");
+          return 0;
+        }
+      }
+    }
+  }
+  /***************************************************************************/
+}
+#endif //PICO_SUPPORT_TCP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_tcp.h	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,98 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+.
+
+*********************************************************************/
+#ifndef _INCLUDE_PICO_TCP
+#define _INCLUDE_PICO_TCP
+#include "pico_addressing.h"
+#include "pico_protocol.h"
+#include "pico_socket.h"
+
+extern struct pico_protocol pico_proto_tcp;
+
+struct __attribute__((packed)) pico_tcp_hdr {
+  struct pico_trans trans;
+  uint32_t seq;
+  uint32_t ack;
+  uint8_t  len;
+  uint8_t flags;
+  uint16_t  rwnd;
+  uint16_t crc;
+  uint16_t urgent;
+};
+
+struct __attribute__((packed)) tcp_pseudo_hdr_ipv4
+{
+  struct pico_ip4 src;
+  struct pico_ip4 dst;
+  uint16_t tcp_len;
+  uint8_t res;
+  uint8_t proto;
+};
+
+#define PICO_TCPHDR_SIZE 20
+#define PICO_SIZE_TCPOPT_SYN 20
+#define PICO_SIZE_TCPHDR (sizeof(struct pico_tcp_hdr))
+
+#define PICO_TCP_DEFAULT_MSS 1444
+
+
+
+/* TCP options */
+#define PICO_TCP_OPTION_END         0x00
+#define PICO_TCPOPTLEN_END        1
+#define PICO_TCP_OPTION_NOOP        0x01
+#define PICO_TCPOPTLEN_NOOP       1
+#define PICO_TCP_OPTION_MSS         0x02
+#define PICO_TCPOPTLEN_MSS        4
+#define PICO_TCP_OPTION_WS          0x03
+#define PICO_TCPOPTLEN_WS         3
+#define PICO_TCP_OPTION_SACK_OK        0x04
+#define PICO_TCPOPTLEN_SACK_OK       2
+#define PICO_TCP_OPTION_SACK        0x05
+#define PICO_TCPOPTLEN_SACK       2 /* Plus the block */
+#define PICO_TCP_OPTION_TIMESTAMP   0x08
+#define PICO_TCPOPTLEN_TIMESTAMP  10
+
+/* TCP flags */
+#define PICO_TCP_FIN 0x01
+#define PICO_TCP_SYN 0x02
+#define PICO_TCP_RST 0x04
+#define PICO_TCP_PSH 0x08
+#define PICO_TCP_ACK 0x10
+#define PICO_TCP_URG 0x20
+#define PICO_TCP_ECN 0x40
+#define PICO_TCP_CWR 0x80
+
+
+
+struct __attribute__((packed)) pico_tcp_option
+{
+  uint8_t kind;
+  uint8_t len;
+#if 0
+  union {
+   uint16_t mss;
+    uint8_t wshift;
+    struct {
+      uint32_t tsval;
+      uint32_t tsecr;
+    } timestamp;
+  } data;
+#endif
+};
+
+struct pico_socket *pico_tcp_open(void);
+int pico_tcp_read(struct pico_socket *s, void *buf, int len);
+int pico_tcp_initconn(struct pico_socket *s);
+int pico_tcp_input(struct pico_socket *s, struct pico_frame *f);
+uint16_t pico_tcp_checksum_ipv4(struct pico_frame *f);
+int pico_tcp_overhead(struct pico_socket *s);
+int pico_tcp_output(struct pico_socket *s, int loop_score);
+int pico_tcp_queue_in_is_empty(struct pico_socket *s);
+int pico_tcp_reply_rst(struct pico_frame *f);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_udp.c	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,176 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+.
+
+Authors: Daniele Lacamera
+*********************************************************************/
+
+
+#include "pico_udp.h"
+#include "pico_config.h"
+#include "pico_eth.h"
+#include "pico_socket.h"
+#include "pico_stack.h"
+
+
+/* Queues */
+static struct pico_queue udp_in = {};
+static struct pico_queue udp_out = {};
+
+
+/* Functions */
+
+uint16_t pico_udp_checksum_ipv4(struct pico_frame *f)
+{
+  struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr;
+  struct pico_udp_hdr *udp_hdr = (struct pico_udp_hdr *) f->transport_hdr;
+  struct pico_socket *s = f->sock;
+  struct pico_ipv4_pseudo_hdr pseudo;
+
+  if (s) {
+    /* Case of outgoing frame */
+    //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");
+    pseudo.src.addr = hdr->src.addr;
+    pseudo.dst.addr = hdr->dst.addr;
+  }
+  pseudo.zeros = 0;
+  pseudo.proto = PICO_PROTO_UDP;
+  pseudo.len = short_be(f->transport_len);
+
+  return pico_dualbuffer_checksum(&pseudo, sizeof(struct pico_ipv4_pseudo_hdr), udp_hdr, f->transport_len);
+}
+
+
+static int pico_udp_process_out(struct pico_protocol *self, struct pico_frame *f)
+{
+  return pico_network_send(f); 
+}
+
+static int pico_udp_push(struct pico_protocol *self, struct pico_frame *f)
+{
+  struct pico_udp_hdr *hdr = (struct pico_udp_hdr *) f->transport_hdr;
+  struct pico_remote_duple *remote_duple = (struct pico_remote_duple *) f->info;
+
+  /* this (fragmented) frame should contain a transport header */
+  if (f->transport_hdr != f->payload) {
+    hdr->trans.sport = f->sock->local_port;
+    if (remote_duple) {
+      hdr->trans.dport = remote_duple->remote_port;
+    } else {
+      hdr->trans.dport = f->sock->remote_port;
+    }
+    hdr->len = short_be(f->transport_len);
+    /* do not perform CRC validation. If you want to, a system needs to be 
+       implemented to calculate the CRC over the total payload of a 
+       fragmented payload */
+    hdr->crc = 0;
+  }
+
+  if (pico_enqueue(self->q_out, f) > 0) {
+    return f->payload_len;
+  } else {
+    return 0;
+  }    
+}
+
+/* Interface: protocol definition */
+struct pico_protocol pico_proto_udp = {
+  .name = "udp",
+  .proto_number = PICO_PROTO_UDP,
+  .layer = PICO_LAYER_TRANSPORT,
+  .process_in = pico_transport_process_in,
+  .process_out = pico_udp_process_out,
+  .push = pico_udp_push,
+  .q_in = &udp_in,
+  .q_out = &udp_out,
+};
+
+
+#define PICO_UDP_MODE_UNICAST 0x01
+#define PICO_UDP_MODE_MULTICAST 0x02
+#define PICO_UDP_MODE_BROADCAST 0xFF
+
+struct pico_socket_udp
+{
+  struct pico_socket sock;
+  int mode;
+#ifdef PICO_SUPPORT_MCAST
+  uint8_t mc_ttl; /* Multicasting TTL */
+#endif
+};
+
+#ifdef PICO_SUPPORT_MCAST
+int pico_udp_set_mc_ttl(struct pico_socket *s, uint8_t ttl)
+{
+  struct pico_socket_udp *u;
+  if(!s) {
+    pico_err = PICO_ERR_EINVAL;
+    return -1;
+  }
+  u = (struct pico_socket_udp *) s;
+  u->mc_ttl = ttl;
+  return 0;
+}
+
+int pico_udp_get_mc_ttl(struct pico_socket *s, uint8_t *ttl)
+{
+  struct pico_socket_udp *u;
+  if(!s)
+    return -1;
+  u = (struct pico_socket_udp *) s;
+  *ttl = u->mc_ttl;
+  return 0;
+}
+#endif /* PICO_SUPPORT_MCAST */
+
+struct pico_socket *pico_udp_open(void)
+{
+  struct pico_socket_udp *u = pico_zalloc(sizeof(struct pico_socket_udp));
+  if (!u)
+    return NULL;
+  u->mode = PICO_UDP_MODE_UNICAST;
+
+#ifdef PICO_SUPPORT_MCAST
+  u->mc_ttl = PICO_IP_DEFAULT_MULTICAST_TTL;
+  /* enable multicast loopback by default */
+  u->sock.opt_flags |= (1 << PICO_SOCKET_OPT_MULTICAST_LOOP);
+#endif
+
+  return &u->sock;
+}
+
+int pico_udp_recv(struct pico_socket *s, void *buf, int len, void *src, uint16_t *port)
+{
+  struct pico_frame *f = pico_queue_peek(&s->q_in);
+  if (f) {
+    f->payload = f->transport_hdr + sizeof(struct pico_udp_hdr);
+    f->payload_len = f->transport_len - sizeof(struct pico_udp_hdr);
+//    dbg("expected: %d, got: %d\n", len, f->payload_len);
+    if (src)
+      pico_store_network_origin(src, f);
+    if (port) {
+      struct pico_trans *hdr = (struct pico_trans *)f->transport_hdr;
+      *port = hdr->sport;
+    }
+    if (f->payload_len > len) {
+      memcpy(buf, f->payload, len);
+      f->payload += len;
+      f->payload_len -= len;
+      return len;
+    } else {
+      int ret = f->payload_len;
+      memcpy(buf, f->payload, f->payload_len);
+      f = pico_dequeue(&s->q_in);
+      pico_frame_discard(f);
+      return ret;
+    }
+  } else return 0;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/pico_udp.h	Sat Aug 03 08:50:27 2013 +0000
@@ -0,0 +1,42 @@
+/*********************************************************************
+PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
+See LICENSE and COPYING for usage.
+
+.
+
+*********************************************************************/
+#ifndef _INCLUDE_PICO_UDP
+#define _INCLUDE_PICO_UDP
+#include "pico_addressing.h"
+#include "pico_protocol.h"
+
+extern struct pico_protocol pico_proto_udp;
+
+struct __attribute__((packed)) pico_udp_hdr {
+  struct pico_trans trans;
+  uint16_t len;
+  uint16_t crc;
+};
+#define PICO_UDPHDR_SIZE 8
+
+struct pico_socket *pico_udp_open(void);
+int pico_udp_recv(struct pico_socket *s, void *buf, int len, void *src, uint16_t *port);
+uint16_t pico_udp_checksum_ipv4(struct pico_frame *f);
+
+#ifdef PICO_SUPPORT_MCAST
+int pico_udp_set_mc_ttl(struct pico_socket *s, uint8_t ttl);
+int pico_udp_get_mc_ttl(struct pico_socket *s, uint8_t *ttl);
+#else
+static inline int pico_udp_set_mc_ttl(struct pico_socket *s, uint8_t ttl)
+{
+  pico_err = PICO_ERR_EPROTONOSUPPORT;
+  return -1;
+}
+static inline int pico_udp_get_mc_ttl(struct pico_socket *s, uint8_t *ttl)
+{
+  pico_err = PICO_ERR_EPROTONOSUPPORT;
+  return -1;
+}
+#endif /* PICO_SUPPORT_MCAST */
+
+#endif