Free (GPLv2) TCP/IP stack developed by TASS Belgium
Fork of PicoTCP by
Revision 0:d7f2341ab245, committed 2013-04-25
- Comitter:
- daniele
- Date:
- Thu Apr 25 13:22:51 2013 +0000
- Child:
- 1:cfe8984a32b4
- Commit message:
- Initial import of PicoTCP Free TCP/IP stack
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/include/arch/pico_mbed.h Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,59 @@ +/********************************************************************* +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_mbed.h +Author: Toon Peters +*********************************************************************/ + +#ifndef PICO_SUPPORT_MBED +#define PICO_SUPPORT_MBED + +//#include "mbed.h" +//#include "serial_api.h" + +/* +Debug needs initialization: +* void serial_init (serial_t *obj, PinName tx, PinName rx); +* void serial_baud (serial_t *obj, int baudrate); +* void serial_format (serial_t *obj, int data_bits, SerialParity parity, int stop_bits); +*/ + +#define dbg(...) +#define pico_zalloc(x) calloc(x, 1) +#define pico_free(x) free(x) + +extern uint32_t os_time; + +static inline unsigned long PICO_TIME(void) +{ + return (unsigned long)os_time / 1000; +} + +static inline unsigned long PICO_TIME_MS(void) +{ + return (unsigned long)os_time; +} + +static inline void PICO_IDLE(void) +{ + // TODO needs implementation +} +/* +static inline void PICO_DEBUG(const char * formatter, ... ) +{ + char buffer[256]; + char *ptr; + va_list args; + va_start(args, formatter); + vsnprintf(buffer, 256, formatter, args); + ptr = buffer; + while(*ptr != '\0') + serial_putc(serial_t *obj, (int) (*(ptr++))); + va_end(args); + //TODO implement serial_t +}*/ + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/include/heap.h Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,81 @@ +/********************************************************************* +PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. +See LICENSE and COPYING for usage. + +*********************************************************************/ + +#define DECLARE_HEAP(type, orderby) \ +struct heap_##type { \ + uint32_t size; \ + uint32_t n; \ + type *top; \ +}; \ +typedef struct heap_##type heap_##type; \ +static inline int heap_insert(struct heap_##type *heap, type *el) \ +{ \ + int i; \ + type * newTop; \ + if (++heap->n >= heap->size) { \ + newTop = pico_zalloc((heap->n + 1) * sizeof(type)); \ + if(!newTop) \ + return -1; \ + if (heap->top) {\ + memcpy(newTop,heap->top,heap->n*sizeof(type)); \ + pico_free(heap->top); \ + } \ + heap->top = newTop; \ + heap->size++; \ + } \ + if (heap->n == 1) { \ + memcpy(&heap->top[1], el, sizeof(type)); \ + return 0; \ + } \ + for (i = heap->n; ((i > 1) && (heap->top[i / 2].orderby > el->orderby)); i /= 2) { \ + memcpy(&heap->top[i], &heap->top[i / 2], sizeof(type)); \ + } \ + memcpy(&heap->top[i], el, sizeof(type)); \ + return 0; \ +} \ +static inline int heap_peek(struct heap_##type *heap, type *first) \ +{ \ + type *last; \ + int i, child; \ + if(heap->n == 0) { \ + return -1; \ + } \ + memcpy(first, &heap->top[1], sizeof(type)); \ + last = &heap->top[heap->n--]; \ + for(i = 1; (i * 2) <= heap->n; i = child) { \ + child = 2 * i; \ + if ((child != heap->n) && \ + (heap->top[child + 1]).orderby \ + < (heap->top[child]).orderby) \ + child++; \ + if (last->orderby > \ + heap->top[child].orderby) \ + memcpy(&heap->top[i], &heap->top[child],\ + sizeof(type)); \ + else \ + break; \ + } \ + memcpy(&heap->top[i], last, sizeof(type)); \ + return 0; \ +} \ +static inline type *heap_first(heap_##type *heap) \ +{ \ + if (heap->n == 0) \ + return NULL; \ + return &heap->top[1]; \ +} \ +static inline heap_##type *heap_init(void) \ +{ \ + heap_##type *p = (heap_##type *)pico_zalloc(sizeof(heap_##type)); \ + return p; \ +} \ +static inline void heap_destroy(heap_##type *h) \ +{ \ + pico_free(h->top); \ + pico_free(h); \ +} \ + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/include/pico_addressing.h Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,52 @@ +/********************************************************************* +PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. +See LICENSE and COPYING for usage. + +*********************************************************************/ +#ifndef _INCLUDE_PICO_ADDRESSING +#define _INCLUDE_PICO_ADDRESSING +#include <stdint.h> + + +struct pico_ip4 +{ + uint32_t addr; +}; +#define PICO_SIZE_IP4 4 + + +struct pico_ip6 +{ + uint8_t addr[16]; +}; +#define PICO_SIZE_IP6 16 + +struct pico_eth +{ + uint8_t addr[6]; + uint8_t padding[2]; +}; +#define PICO_SIZE_ETH 6 + +extern const uint8_t PICO_ETHADDR_ALL[]; + + +struct pico_trans +{ + uint16_t sport; + uint16_t dport; + +}; +#define PICO_SIZE_TRANS 8 + + +/* Here are some protocols. */ +#define PICO_PROTO_IPV4 0 +#define PICO_PROTO_ICMP4 1 +#define PICO_PROTO_IGMP2 2 +#define PICO_PROTO_TCP 6 +#define PICO_PROTO_UDP 17 +#define PICO_PROTO_IPV6 41 +#define PICO_PROTO_ICMP6 58 + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/include/pico_arp.h Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,25 @@ +/********************************************************************* +PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. +See LICENSE and COPYING for usage. + +*********************************************************************/ +#ifndef _INCLUDE_PICO_ARP +#define _INCLUDE_PICO_ARP +#include "pico_eth.h" +#include "pico_device.h" + +int pico_arp_receive(struct pico_frame *); + + +struct pico_eth *pico_arp_get(struct pico_frame *f); +int pico_arp_query(struct pico_device *dev, struct pico_ip4 *dst); + +#define PICO_ARP_STATUS_REACHABLE 0x00 +#define PICO_ARP_STATUS_PERMANENT 0x01 +#define PICO_ARP_STATUS_STALE 0x02 + + +struct pico_eth *pico_arp_lookup(struct pico_ip4 *dst); +struct pico_ip4 *pico_arp_reverse_lookup(struct pico_eth *dst); +int pico_arp_create_entry(uint8_t* hwaddr, struct pico_ip4 ipv4, struct pico_device* dev); +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/include/pico_config.h Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,34 @@ +/********************************************************************* +PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. +See LICENSE and COPYING for usage. + +*********************************************************************/ +#ifndef _INCLUDE_PICO_CONFIG +#define _INCLUDE_PICO_CONFIG +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include "pico_constants.h" + + +#define MBED +#define PICO_SUPPORT_CRC +#define PICO_SUPPORT_DEVLOOP +#define PICO_SUPPORT_DHCPC +#define PICO_SUPPORT_DHCPD +#define PICO_SUPPORT_DNS_CLIENT +#define PICO_SUPPORT_HTTP_CLIENT +#define PICO_SUPPORT_HTTP +#define PICO_SUPPORT_HTTP_SERVER +#define PICO_SUPPORT_ICMP4 +#define PICO_SUPPORT_PING +#define PICO_SUPPORT_IGMP2 +#define PICO_SUPPORT_IPFILTER +#define PICO_SUPPORT_IPFRAG +#define PICO_SUPPORT_IPV4 +#define PICO_SUPPORT_MCAST +#define PICO_SUPPORT_NAT +#define PICO_SUPPORT_TCP +#define PICO_SUPPORT_UDP +# include "arch/pico_mbed.h" +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/include/pico_constants.h Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,76 @@ +/********************************************************************* +PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. +See LICENSE and COPYING for usage. + +*********************************************************************/ +#ifndef _INCLUDE_PICO_CONST +#define _INCLUDE_PICO_CONST +/* Included from pico_config.h */ +/** Endian-dependant constants **/ + +extern volatile unsigned long pico_tick; + +#ifdef PICO_BIGENDIAN + +# define PICO_IDETH_IPV4 0x0800 +# define PICO_IDETH_ARP 0x0806 +# define PICO_IDETH_IPV6 0x86DD + +# define PICO_ARP_REQUEST 0x0001 +# define PICO_ARP_REPLY 0x0002 +# define PICO_ARP_HTYPE_ETH 0x0001 + +#define short_be(x) (x) +#define long_be(x) (x) + +#else + +# define PICO_IDETH_IPV4 0x0008 +# define PICO_IDETH_ARP 0x0608 +# define PICO_IDETH_IPV6 0xDD86 + +# define PICO_ARP_REQUEST 0x0100 +# define PICO_ARP_REPLY 0x0200 +# define PICO_ARP_HTYPE_ETH 0x0100 + +static inline uint16_t short_be(uint16_t le) +{ + return ((le & 0xFF) << 8) | ((le >> 8) & 0xFF); +} + +static inline uint32_t long_be(uint32_t le) +{ + uint8_t *b = (uint8_t *)≤ + uint32_t be = 0; + uint32_t b0, b1, b2; + b0 = b[0]; + b1 = b[1]; + b2 = b[2]; + be = b[3] + (b2 << 8) + (b1 << 16) + (b0 << 24); + return be; +} +#endif + + +/* Add well-known host numbers here. (bigendian constants only beyond this point) */ +#define PICO_IP4_ANY (0x00000000U) +#define PICO_IP4_BCAST (0xffffffffU) + +/* defined in modules/pico_ipv6.c */ +#ifdef PICO_SUPPORT_IPV6 +extern const uint8_t PICO_IPV6_ANY[PICO_SIZE_IP6]; +#endif + +static inline uint32_t pico_hash(char *name) +{ + unsigned long hash = 5381; + int c; + while ((c = *name++)) + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + return hash; +} + +/* Debug */ +//#define PICO_SUPPORT_DEBUG_MEMORY +//#define PICO_SUPPORT_DEBUG_TOOLS +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/include/pico_device.h Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,37 @@ +/********************************************************************* +PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. +See LICENSE and COPYING for usage. + +*********************************************************************/ +#ifndef _INCLUDE_PICO_DEVICE +#define _INCLUDE_PICO_DEVICE +#include "pico_queue.h" +#include "pico_frame.h" +#include "pico_addressing.h" +#include "pico_tree.h" +#define MAX_DEVICE_NAME 16 + + +struct pico_ethdev { + struct pico_eth mac; +}; + +struct pico_device { + char name[MAX_DEVICE_NAME]; + uint32_t hash; + uint32_t overhead; + struct pico_ethdev *eth; /* Null if non-ethernet */ + struct pico_queue *q_in; + struct pico_queue *q_out; + int (*send)(struct pico_device *self, void *buf, int len); /* Send function. Return 0 if busy */ + int (*poll)(struct pico_device *self, int loop_score); + void(*destroy)(struct pico_device *self); +}; + +int pico_device_init(struct pico_device *dev, char *name, uint8_t *mac); +void pico_device_destroy(struct pico_device *dev); +int pico_devices_loop(int loop_score, int direction); +struct pico_device* pico_get_device(char* name); +int pico_device_broadcast(struct pico_frame * f); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/include/pico_eth.h Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,21 @@ +/********************************************************************* +PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. +See LICENSE and COPYING for usage. + +*********************************************************************/ +#ifndef _INCLUDE_PICO_ETH +#define _INCLUDE_PICO_ETH +#include "pico_addressing.h" +#include "pico_ipv4.h" +#include "pico_ipv6.h" + + +struct __attribute__((packed)) pico_eth_hdr { + uint8_t daddr[6]; + uint8_t saddr[6]; + uint16_t proto; +}; + +#define PICO_SIZE_ETHHDR 14 + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/include/pico_frame.h Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,95 @@ +/********************************************************************* +PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. +See LICENSE and COPYING for usage. + +*********************************************************************/ +#ifndef _INCLUDE_PICO_FRAME +#define _INCLUDE_PICO_FRAME +#include "pico_config.h" + + +#define PICO_FRAME_FLAG_BCAST (0x01) +#define PICO_FRAME_FLAG_SACKED (0x80) +#define IS_BCAST(f) ((f->flags & PICO_FRAME_FLAG_BCAST) == PICO_FRAME_FLAG_BCAST) + + +struct pico_socket; + + +struct pico_frame { + + /* Connector for queues */ + struct pico_frame *next; + + /* Start of the whole buffer, total frame length. */ + unsigned char *buffer; + uint32_t buffer_len; + + /* For outgoing packets: this is the meaningful buffer. */ + unsigned char *start; + uint32_t len; + + /* Pointer to usage counter */ + uint32_t *usage_count; + + /* Pointer to protocol headers */ + uint8_t *datalink_hdr; + + uint8_t *net_hdr; + int net_len; + uint8_t *transport_hdr; + int transport_len; + uint8_t *app_hdr; + int app_len; + + /* Pointer to the phisical device this packet belongs to. + * Should be valid in both routing directions + */ + struct pico_device *dev; + + unsigned long timestamp; + + /* Failures due to bad datalink addressing. */ + uint16_t failure_count; + + /* Protocol over IP */ + uint8_t proto; + + /* PICO_FRAME_FLAG_* */ + uint8_t flags; + + /* Pointer to payload */ + unsigned char *payload; + int payload_len; + +#ifdef PICO_SUPPORT_IPFRAG + /* Payload fragmentation info (big endian)*/ + uint16_t frag; +#endif + + /* Pointer to socket */ + struct pico_socket *sock; + + /* Pointer to transport info, used to store remote UDP duple (IP + port) */ + void *info; + + /*Priority. "best-effort" priority, the default value is 0. Priority can be in between -10 and +10*/ + int8_t priority; +}; + +/** frame alloc/dealloc/copy **/ +void pico_frame_discard(struct pico_frame *f); +struct pico_frame *pico_frame_copy(struct pico_frame *f); +struct pico_frame *pico_frame_deepcopy(struct pico_frame *f); +struct pico_frame *pico_frame_alloc(int size); +uint16_t pico_checksum(void *inbuf, int len); +uint16_t pico_dualbuffer_checksum(void *b1, int len1, void *b2, int len2); + +static inline int pico_is_digit(char c) +{ + if (c < '0' || c > '9') + return 0; + return 1; +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/include/pico_module_eth.h Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,33 @@ +/********************************************************************* +PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. +See LICENSE and COPYING for usage. + +*********************************************************************/ +#ifndef _PICO_MODULE_IPV4_H +#define _PICO_MODULE_IPV4_H + +struct pico_arp_entry { + struct eth dest; +#ifdef PICO_CONFIG_IPV4 + struct ipv4 addr_ipv4; +#endif + RB_ENTRY(pico_arp_entry) node; +}; + +/* Configured device */ +struct pico_eth_link { + struct pico_device *dev; + struct eth address; + struct eth netmask; + RB_ENTRY(pico_eth_link) node; +}; + +#ifndef IS_MODULE_ETH +# define _mod extern +#else +# define _mod +#endif +_mod struct pico_module pico_module_eth; +#undef _mod + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/include/pico_protocol.h Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,94 @@ +/********************************************************************* +PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. +See LICENSE and COPYING for usage. + +*********************************************************************/ +#ifndef _INCLUDE_PICO_PROTOCOL +#define _INCLUDE_PICO_PROTOCOL +#include <stdint.h> +#include "pico_queue.h" + +#define PICO_LOOP_DIR_IN 1 +#define PICO_LOOP_DIR_OUT 2 + +enum pico_layer { + PICO_LAYER_DATALINK = 2, /* Ethernet only. */ + PICO_LAYER_NETWORK = 3, /* IPv4, IPv6, ARP. Arp is there because it communicates with L2 */ + PICO_LAYER_TRANSPORT = 4, /* UDP, TCP, ICMP */ + PICO_LAYER_SOCKET = 5 /* Socket management */ +}; + +enum pico_err_e { + PICO_ERR_NOERR = 0, + PICO_ERR_EPERM, + PICO_ERR_ENOENT, + /* ... */ + PICO_ERR_EINTR = 4, + PICO_ERR_EIO, + PICO_ERR_ENXIO, + /* ... */ + PICO_ERR_EAGAIN = 11, + PICO_ERR_ENOMEM, + PICO_ERR_EACCESS, + PICO_ERR_EFAULT, + /* ... */ + PICO_ERR_EBUSY = 16, + PICO_ERR_EEXIST = 17, + /* ... */ + PICO_ERR_EINVAL = 22, + /* ... */ + PICO_ERR_EPROTO = 71, + PICO_ERR_EPROTONOSUPPORT = 93, + + /* ... */ + PICO_ERR_EADDRINUSE = 98, + PICO_ERR_EADDRNOTAVAIL, + PICO_ERR_ENETUNREACH, + + /* ... */ + PICO_ERR_ECONNRESET = 104, + + /* ... */ + PICO_ERR_EISCONN = 106, + PICO_ERR_ENOTCONN, + PICO_ERR_ESHUTDOWN, + /* ... */ + PICO_ERR_ETIMEDOUT = 110, + PICO_ERR_ECONNREFUSED = 111, + PICO_ERR_EHOSTDOWN, + PICO_ERR_EHOSTUNREACH, + /* ... */ + PICO_ERR_EOPNOTSUPP = 122, + +}; + +typedef enum pico_err_e pico_err_t; +extern volatile pico_err_t pico_err; + +#define IS_IPV6(f) ((((uint8_t *)(f->net_hdr))[0] & 0xf0) == 0x60) +#define IS_IPV4(f) ((((uint8_t *)(f->net_hdr))[0] & 0xf0) == 0x40) + +#define MAX_PROTOCOL_NAME 16 + +struct pico_protocol { + char name[MAX_PROTOCOL_NAME]; + uint32_t hash; + enum pico_layer layer; + int proto_number; + struct pico_queue *q_in; + struct pico_queue *q_out; + struct pico_frame *(*alloc)(struct pico_protocol *self, int size); /* Frame allocation. */ + int (*push) (struct pico_protocol *self, struct pico_frame *p); /* Push function, for active outgoing pkts from above */ + int (*process_out)(struct pico_protocol *self, struct pico_frame *p); /* Send loop. */ + int (*process_in)(struct pico_protocol *self, struct pico_frame *p); /* Recv loop. */ +}; + +int pico_protocols_loop(int loop_score); +void pico_protocol_init(struct pico_protocol *p); + +int pico_protocol_datalink_loop(int loop_score, int direction); +int pico_protocol_network_loop(int loop_score, int direction); +int pico_protocol_transport_loop(int loop_score, int direction); +int pico_protocol_socket_loop(int loop_score, int direction); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/include/pico_queue.h Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,127 @@ +/********************************************************************* +PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. +See LICENSE and COPYING for usage. + +*********************************************************************/ +#ifndef _INCLUDE_PICO_QUEUE +#define _INCLUDE_PICO_QUEUE +#include <stdint.h> +#include "pico_config.h" +#include "pico_frame.h" + +#ifndef NULL +#define NULL ((void *)0) +#endif + +struct pico_queue { + uint32_t frames; + uint32_t size; + uint32_t max_frames; + uint32_t max_size; + struct pico_frame *head; + struct pico_frame *tail; +#ifdef PICO_RTOS_SUPPORT + void * mutex; +#endif +}; + +#ifdef PICO_RTOS_SUPPORT + extern void waitAndTakeMutex(void ** mutex); + extern void giveMutexBack(void * mutex); +#endif + +#ifdef PICO_SUPPORT_DEBUG_TOOLS +static void debug_q(struct pico_queue *q) +{ + struct pico_frame *p = q->head; + dbg("%d: ", q->frames); + while(p) { + dbg("(%p)-->", p); + p = p->next; + } + dbg("X\n"); +} +#endif + +static inline int pico_enqueue(struct pico_queue *q, struct pico_frame *p) +{ + if ((q->max_frames) && (q->max_frames <= q->frames)) + return -1; + + if ((q->max_size) && (q->max_size < (p->buffer_len + q->size))) + return -1; + +#ifdef PICO_RTOS_SUPPORT + waitAndTakeMutex(&q->mutex); +#endif + + p->next = NULL; + if (!q->head) { + q->head = p; + q->tail = p; + q->size = 0; + q->frames = 0; + } else { + q->tail->next = p; + q->tail = p; + } + q->size += p->buffer_len; + q->frames++; +#ifdef PICO_SUPPORT_DEBUG_TOOLS + debug_q(q); +#endif + +#ifdef PICO_RTOS_SUPPORT + giveMutexBack(q->mutex); +#endif + + return q->size; +} + +static inline struct pico_frame *pico_dequeue(struct pico_queue *q) +{ + struct pico_frame *p = q->head; + if (q->frames < 1) + return NULL; +#ifdef PICO_RTOS_SUPPORT + waitAndTakeMutex(&q->mutex); +#endif + + q->head = p->next; + q->frames--; + q->size -= p->buffer_len; + if (q->head == NULL) + q->tail = NULL; +#ifdef PICO_SUPPORT_DEBUG_TOOLS + debug_q(q); +#endif + p->next = NULL; + +#ifdef PICO_RTOS_SUPPORT + giveMutexBack(q->mutex); +#endif + + return p; +} + +static inline struct pico_frame *pico_queue_peek(struct pico_queue *q) +{ + struct pico_frame *p = q->head; + if (q->frames < 1) + return NULL; +#ifdef PICO_SUPPORT_DEBUG_TOOLS + debug_q(q); +#endif + return p; +} + +static inline void pico_queue_empty(struct pico_queue *q) +{ + struct pico_frame *p = pico_dequeue(q); + while(p) { + pico_free(p); + p = pico_dequeue(q); + } +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/include/pico_socket.h Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,190 @@ +/********************************************************************* +PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. +See LICENSE and COPYING for usage. + +*********************************************************************/ +#ifndef _INCLUDE_PICO_SOCKET +#define _INCLUDE_PICO_SOCKET +#include "pico_queue.h" +#include "pico_addressing.h" +#include "pico_config.h" +#include "pico_protocol.h" + +#define PICO_DEFAULT_SOCKETQ (128 * 1024) +//#define PICO_DEFAULT_SOCKETQ (8192) + + +#define PICO_SHUT_RD 1 +#define PICO_SHUT_WR 2 +#define PICO_SHUT_RDWR 3 + + +struct pico_socket { + struct pico_protocol *proto; + struct pico_protocol *net; + + union { + struct pico_ip4 ip4; + struct pico_ip6 ip6; + } local_addr; + + union { + struct pico_ip4 ip4; + struct pico_ip6 ip6; + } remote_addr; + + uint16_t local_port; + uint16_t remote_port; + + struct pico_queue q_in; + struct pico_queue q_out; + + void (*wakeup)(uint16_t ev, struct pico_socket *s); + + +#ifdef PICO_SUPPORT_TCP + /* For the TCP backlog queue */ + struct pico_socket *backlog; + struct pico_socket *next; + struct pico_socket *parent; + int max_backlog; +#endif + uint16_t ev_pending; + + struct pico_device *dev; + + /* Private field. */ + int id; + uint16_t state; + uint16_t opt_flags; +}; + +struct pico_remote_duple { + union { + struct pico_ip4 ip4; + struct pico_ip6 ip6; + } remote_addr; + + uint16_t remote_port; +}; + + +/* request struct for multicast socket opt */ +struct pico_ip_mreq { + struct pico_ip4 mcast_group_addr; + struct pico_ip4 mcast_link_addr; +}; + +#define PICO_SOCKET_STATE_UNDEFINED 0x0000 +#define PICO_SOCKET_STATE_SHUT_LOCAL 0x0001 +#define PICO_SOCKET_STATE_SHUT_REMOTE 0x0002 +#define PICO_SOCKET_STATE_BOUND 0x0004 +#define PICO_SOCKET_STATE_CONNECTED 0x0008 +#define PICO_SOCKET_STATE_CLOSING 0x0010 +#define PICO_SOCKET_STATE_CLOSED 0x0020 + +# define PICO_SOCKET_STATE_TCP 0xFF00 +# define PICO_SOCKET_STATE_TCP_UNDEF 0x00FF +# define PICO_SOCKET_STATE_TCP_CLOSED 0x0100 +# define PICO_SOCKET_STATE_TCP_LISTEN 0x0200 +# define PICO_SOCKET_STATE_TCP_SYN_SENT 0x0300 +# define PICO_SOCKET_STATE_TCP_SYN_RECV 0x0400 +# define PICO_SOCKET_STATE_TCP_ESTABLISHED 0x0500 +# define PICO_SOCKET_STATE_TCP_CLOSE_WAIT 0x0600 +# define PICO_SOCKET_STATE_TCP_LAST_ACK 0x0700 +# define PICO_SOCKET_STATE_TCP_FIN_WAIT1 0x0800 +# define PICO_SOCKET_STATE_TCP_FIN_WAIT2 0x0900 +# define PICO_SOCKET_STATE_TCP_CLOSING 0x0a00 +# define PICO_SOCKET_STATE_TCP_TIME_WAIT 0x0b00 +# define PICO_SOCKET_STATE_TCP_ARRAYSIZ 0x0c + +# define PICO_TCP_NODELAY 1 + +# define PICO_SOCKET_OPT_TCPNODELAY 0x0000 + +# define PICO_IP_MULTICAST_IF 32 +# define PICO_IP_MULTICAST_TTL 33 +# define PICO_IP_MULTICAST_LOOP 34 +# define PICO_IP_ADD_MEMBERSHIP 35 +# define PICO_IP_DROP_MEMBERSHIP 36 + +# define PICO_SOCKET_OPT_MULTICAST_LOOP 1 + +# define PICO_IP_DEFAULT_MULTICAST_TTL 1 +# define PICO_IP_DEFAULT_MULTICAST_LOOP 1 + +#define PICO_SOCKET_SHUTDOWN_WRITE 0x01 +#define PICO_SOCKET_SHUTDOWN_READ 0x02 +#define TCPSTATE(s) ((s)->state & PICO_SOCKET_STATE_TCP) + +#define PICO_SOCK_EV_RD 1 +#define PICO_SOCK_EV_WR 2 +#define PICO_SOCK_EV_CONN 4 +#define PICO_SOCK_EV_CLOSE 8 +#define PICO_SOCK_EV_FIN 0x10 +#define PICO_SOCK_EV_ERR 0x80 + + +struct pico_socket *pico_socket_open(uint16_t net, uint16_t proto, void (*wakeup)(uint16_t ev, struct pico_socket *s)); + +int pico_socket_read(struct pico_socket *s, void *buf, int len); +int pico_socket_write(struct pico_socket *s, void *buf, int len); + +int pico_socket_sendto(struct pico_socket *s, void *buf, int len, void *dst, uint16_t remote_port); +int pico_socket_recvfrom(struct pico_socket *s, void *buf, int len, void *orig, uint16_t *local_port); + +int pico_socket_send(struct pico_socket *s, void *buf, int len); +int pico_socket_recv(struct pico_socket *s, void *buf, int len); + +int pico_socket_bind(struct pico_socket *s, void *local_addr, uint16_t *port); +int pico_socket_connect(struct pico_socket *s, void *srv_addr, uint16_t remote_port); +int pico_socket_listen(struct pico_socket *s, int backlog); +struct pico_socket *pico_socket_accept(struct pico_socket *s, void *orig, uint16_t *port); +int pico_socket_del(struct pico_socket *s); + +int pico_socket_setoption(struct pico_socket *s, int option, void *value); +int pico_socket_getoption(struct pico_socket *s, int option, void *value); + +int pico_socket_shutdown(struct pico_socket *s, int mode); +int pico_socket_close(struct pico_socket *s); + +struct pico_frame *pico_socket_frame_alloc(struct pico_socket *s, int len); + +#ifdef PICO_SUPPORT_IPV4 +# define is_sock_ipv4(x) (x->net == &pico_proto_ipv4) +#else +# define is_sock_ipv4(x) (0) +#endif + +#ifdef PICO_SUPPORT_IPV6 +# define is_sock_ipv6(x) (x->net == &pico_proto_ipv6) +#else +# define is_sock_ipv6(x) (0) +#endif + +#ifdef PICO_SUPPORT_UDP +# define is_sock_udp(x) (x->net == &pico_proto_udp) +#else +# define is_sock_udp(x) (0) +#endif + +#ifdef PICO_SUPPORT_TCP +# define is_sock_tcp(x) (x->net == &pico_proto_tcp) +#else +# define is_sock_tcp(x) (0) +#endif + +/* Interface towards transport protocol */ +int pico_transport_process_in(struct pico_protocol *self, struct pico_frame *f); +struct pico_socket *pico_socket_clone(struct pico_socket *facsimile); +int pico_socket_add(struct pico_socket *s); +int pico_transport_error(struct pico_frame *f, uint8_t proto, int code); + +/* Socket loop */ +int pico_sockets_loop(int loop_score); + +/* Port check */ +int pico_is_port_free(uint16_t proto, uint16_t port, void *addr, void *net); + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/include/pico_stack.h Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,65 @@ +/********************************************************************* +PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. +See LICENSE and COPYING for usage. + +*********************************************************************/ +#ifndef _INCLUDE_PICO_STACK +#define _INCLUDE_PICO_STACK +#include "pico_config.h" +#include "pico_frame.h" + +#define PICO_MAX_TIMERS 20 + +/* ===== RECEIVING FUNCTIONS (from dev up to socket) ===== */ + +/* TRANSPORT LEVEL */ +/* interface towards network */ +int pico_transport_receive(struct pico_frame *f, uint8_t proto); + +/* NETWORK LEVEL */ +/* interface towards ethernet */ +int pico_network_receive(struct pico_frame *f); + +/* The pico_ethernet_receive() function is used by + * those devices supporting ETH in order to push packets up + * into the stack. + */ +/* DATALINK LEVEL */ +int pico_ethernet_receive(struct pico_frame *f); + +/* LOWEST LEVEL: interface towards devices. */ +/* Device driver will call this function which returns immediately. + * Incoming packet will be processed later on in the dev loop. + */ +int pico_stack_recv(struct pico_device *dev, uint8_t *buffer, int len); + + +/* ===== SENDIING FUNCTIONS (from socket down to dev) ===== */ + +int pico_transport_send(struct pico_frame *f); +int pico_network_send(struct pico_frame *f); +int pico_ethernet_send(struct pico_frame *f); +int pico_sendto_dev(struct pico_frame *f); + +/* ----- Initialization ----- */ +void pico_stack_init(void); + +/* ----- Loop Function. ----- */ +void pico_stack_tick(void); +void pico_stack_loop(void); + +/* ---- Notifications for stack errors */ +int pico_notify_socket_unreachable(struct pico_frame *f); +int pico_notify_proto_unreachable(struct pico_frame *f); +int pico_notify_dest_unreachable(struct pico_frame *f); +int pico_notify_ttl_expired(struct pico_frame *f); + +/* Various. */ +int pico_source_is_local(struct pico_frame *f); +int pico_destination_is_local(struct pico_frame *f); +void pico_store_network_origin(void *src, struct pico_frame *f); +void pico_timer_add(unsigned long expire, void (*timer)(unsigned long, void *), void *arg); +uint32_t pico_rand(void); +void pico_rand_feed(uint32_t feed); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/include/pico_tree.h Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,85 @@ +/********************************************************************* +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_RBTREE_H__ +#define __PICO_RBTREE_H__ + +#include <stdint.h> +#include "pico_config.h" + +// This is used to declare a new tree, leaf root by default +#define PICO_TREE_DECLARE(name,compareFunction) \ +struct pico_tree name =\ +{ \ + &LEAF, \ + compareFunction \ +} + + +struct pico_tree_node +{ + void* keyValue; // generic key + struct pico_tree_node* parent; + struct pico_tree_node* leftChild; + struct pico_tree_node* rightChild; + uint8_t color; +}; + +struct pico_tree +{ + struct pico_tree_node * root; // root of the tree + + // this function directly provides the keys as parameters not the nodes. + int (*compare)(void* keyA, void* keyB); +}; + +extern struct pico_tree_node LEAF; // generic leaf node +/* + * Manipulation functions + */ +void * pico_tree_insert(struct pico_tree * tree, void * key); +void * pico_tree_delete(struct pico_tree * tree, void * key); +void * pico_tree_findKey(struct pico_tree * tree, void * key); +void pico_tree_drop(struct pico_tree * tree); +int pico_tree_empty(struct pico_tree * tree); +struct pico_tree_node * pico_tree_findNode(struct pico_tree * tree, void * key); + +void * pico_tree_first(struct pico_tree * tree); +void * pico_tree_last(struct pico_tree * tree); +/* + * Traverse functions + */ +struct pico_tree_node * pico_tree_lastNode(struct pico_tree_node * node); +struct pico_tree_node * pico_tree_firstNode(struct pico_tree_node * node); +struct pico_tree_node * pico_tree_next(struct pico_tree_node * node); +struct pico_tree_node * pico_tree_prev(struct pico_tree_node * node); + +/* + * For each macros + */ + +#define pico_tree_foreach(idx,tree) \ + for ((idx) = pico_tree_firstNode((tree)->root); \ + (idx) != &LEAF; \ + (idx) = pico_tree_next(idx)) + +#define pico_tree_foreach_reverse(idx,tree) \ + for ((idx) = pico_tree_lastNode((tree)->root); \ + (idx) != &LEAF; \ + (idx) = pico_tree_prev(idx)) + +#define pico_tree_foreach_safe(idx,tree,idx2) \ + for ((idx) = pico_tree_firstNode((tree)->root); \ + ((idx) != &LEAF) && ((idx2) = pico_tree_next(idx), 1); \ + (idx) = (idx2)) + +#define pico_tree_foreach_reverse_safe(idx,tree,idx2) \ + for ((idx) = pico_tree_lastNode((tree)->root); \ + ((idx) != &LEAF) && ((idx2) = pico_tree_prev(idx), 1); \ + (idx) = (idx2)) + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/modules/pico_dev_loop.c Thu Apr 25 13:22:51 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/libraries/picotcp/modules/pico_dev_loop.h Thu Apr 25 13:22:51 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/libraries/picotcp/modules/pico_dev_mbed.cpp Thu Apr 25 13:22:51 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/libraries/picotcp/modules/pico_dev_mbed.h Thu Apr 25 13:22:51 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/libraries/picotcp/modules/pico_dhcp_client.c Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,695 @@ +/********************************************************************* +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; + struct pico_ip4 address; + struct pico_ip4 netmask; + struct pico_ip4 gateway; + 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); + pico_err = PICO_ERR_EBUSY; /* initiation is postponed, not a breaking error */ + return 0; + } + dhcp_client_mutex--; + + if (init_cookie(cli) < 0) + return 0; + + 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 0; /* Element key already exists */ + } + + if (dhclient_send(cli, PICO_DHCP_MSG_DISCOVER) < 0) + return 0; + + return cli->xid; +} + +/* 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 */ +uint32_t pico_dhcp_initiate_negotiation(struct pico_device *device, void (*callback)(void *cli, int code)) +{ + struct pico_dhcp_client_cookie *cli; + + if(!device || !callback){ + pico_err = PICO_ERR_EINVAL; + return 0; + } + cli = pico_zalloc(sizeof(struct pico_dhcp_client_cookie)); + if(!cli){ + pico_err = PICO_ERR_ENOMEM; + return 0; + } + + cli->device = device; + cli->cb = callback; + + 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; +} + +/************* + * 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_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 = 0.5*cli->lease_time; + if(T2_set != 1) + cli->T2 = 0.875*cli->lease_time; + + + + 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; + address.addr = long_be(0x00000000); + + if(cli->link_added == 0){ + pico_socket_close(cli->socket); + 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); + + 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) +{ + + dhclient_send(cli, PICO_DHCP_MSG_REQUEST); + cli->state = DHCPSTATE_RENEWING; + + 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; + + /* 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) { + 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; +} + +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/libraries/picotcp/modules/pico_dhcp_client.h Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,34 @@ +/********************************************************************* +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" + + +uint32_t pico_dhcp_initiate_negotiation(struct pico_device *device, void (*callback)(void* cli, int code)); +void pico_dhcp_process_incoming_message(uint8_t *data, int len); +struct pico_ip4 pico_dhcp_get_address(void *cli); +struct pico_ip4 pico_dhcp_get_gateway(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/libraries/picotcp/modules/pico_dhcp_common.c Thu Apr 25 13:22:51 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/libraries/picotcp/modules/pico_dhcp_common.h Thu Apr 25 13:22:51 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/libraries/picotcp/modules/pico_dhcp_server.c Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,290 @@ +/********************************************************************* +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; + + 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 */ + if (opt_type == PICO_DHCPOPT_MSGTYPE) { + /* server simple state machine */ + uint8_t msg_type = opt_data[0]; + 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; + } + } + opt_len = 20; + opt_type = dhcp_get_next_option(NULL, opt_data, &opt_len, &nextopt); + } +} + +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/libraries/picotcp/modules/pico_dhcp_server.h Thu Apr 25 13:22:51 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/libraries/picotcp/modules/pico_dns_client.c Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,764 @@ +/********************************************************************* +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) { + compression = short_be(*(uint16_t *)a_suf); + switch (compression >> 14) + { + case PICO_DNS_POINTER: + while (compression >> 14 == PICO_DNS_POINTER) { + dns_dbg("DNS: pointer\n"); + a_suf += sizeof(uint16_t); + compression = short_be(*(uint16_t *)a_suf); + } + 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) { + dns_dbg("DNS: length %u | ip %08X\n", short_be(answer_suf.rdlength), long_be(*(uint32_t *)a_rdata)); + answer = pico_zalloc(16); + pico_ipv4_to_string(answer, *(uint32_t *)a_rdata); + 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/libraries/picotcp/modules/pico_dns_client.h Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,22 @@ +/********************************************************************* +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 + +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/libraries/picotcp/modules/pico_http_client.c Thu Apr 25 13:22:51 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/libraries/picotcp/modules/pico_http_client.h Thu Apr 25 13:22:51 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/libraries/picotcp/modules/pico_http_server.c Thu Apr 25 13:22:51 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/libraries/picotcp/modules/pico_http_server.h Thu Apr 25 13:22:51 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/libraries/picotcp/modules/pico_http_util.c Thu Apr 25 13:22:51 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/libraries/picotcp/modules/pico_http_util.h Thu Apr 25 13:22:51 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/libraries/picotcp/modules/pico_icmp4.c Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,315 @@ +/********************************************************************* +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; + /* Ugly, but the best way to get ICMP data size here. */ + f->transport_len = f->buffer_len - PICO_SIZE_IP4HDR; + if (f->dev->eth) + f->transport_len -= PICO_SIZE_ETHHDR; + pico_icmp4_checksum(f); + f->net_hdr = f->transport_hdr - PICO_SIZE_IP4HDR; + f->start = f->net_hdr; + f->len = f->buffer_len; + if (f->dev->eth) + f->len -= PICO_SIZE_ETHHDR; + 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); +} + +#ifdef PICO_SUPPORT_IPFILTER +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); +} +#endif + +/***********************/ +/* 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); + 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); + /* XXX cb */ + } 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/libraries/picotcp/modules/pico_icmp4.h Thu Apr 25 13:22:51 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/libraries/picotcp/modules/pico_igmp2.c Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,637 @@ +/********************************************************************* +PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. +See LICENSE and COPYING for usage. + +. + +Authors: Simon Maes, Brecht Van Cauwenberghe +*********************************************************************/ + +#include "pico_stack.h" +#include "pico_ipv4.h" +#include "pico_igmp2.h" +#include "pico_config.h" +#include "pico_eth.h" +#include "pico_addressing.h" +#include "pico_frame.h" +#include "pico_tree.h" + +#define NO_ACTIVE_TIMER (0) + + +/*================= RB_TREE FUNCTIONALITY ================*/ + +struct mgroup_info { + struct pico_ip4 mgroup_addr; + struct pico_ip4 src_interface; + unsigned long active_timer_starttime; + /* Connector for trees */ + uint16_t delay; + uint8_t membership_state; + uint8_t Last_Host_flag; +}; + +struct timer_callback_info { + unsigned long timer_starttime; + struct pico_frame *f; +}; + +static int mgroup_cmp(void *ka,void *kb) +{ + struct mgroup_info *a=ka, *b=kb; + if (a->mgroup_addr.addr < b->mgroup_addr.addr) { + return -1; + } + else if (a->mgroup_addr.addr > b->mgroup_addr.addr) { + return 1; + } + else { + /* a and b are identical */ + return 0; + } +} + +PICO_TREE_DECLARE(KEYTable,mgroup_cmp); + +static struct mgroup_info *pico_igmp2_find_mgroup(struct pico_ip4 *mgroup_addr) +{ + struct mgroup_info test = {{0}}; + test.mgroup_addr.addr = mgroup_addr->addr; + /* returns NULL if test can not be found */ + return pico_tree_findKey(&KEYTable,&test); +} + +static int pico_igmp2_del_mgroup(struct mgroup_info *info) +{ + if(!info){ + pico_err = PICO_ERR_EINVAL; + return -1; + } + else { + // RB_REMOVE returns pointer to removed element, NULL to indicate error· + if(pico_tree_delete(&KEYTable,info)) + pico_free(info); + else { + pico_err = PICO_ERR_EEXIST; + return -1;// Do not free, error on removing element from tree + } + } + return 0; +} + +/*========================================================*/ + +struct igmp2_packet_params { + struct pico_ip4 group_address; + struct pico_ip4 src_interface; + struct pico_frame *f; + uint8_t event; + uint8_t max_resp_time; + unsigned long timer_starttime; +}; + +static int pico_igmp2_process_event(struct igmp2_packet_params *params); +static void generate_event_timer_expired(long unsigned int empty, void *info); + +#ifdef PICO_UNIT_TEST_IGMP2 +#define igmp2_dbg dbg +static int pico_igmp2_process_event(struct igmp2_packet_params *params); +static int pico_igmp2_analyse_packet(struct pico_frame *f, struct igmp2_packet_params *params); +static int pico_igmp2_process_in(struct pico_protocol *self, struct pico_frame *f); + + +int test_pico_igmp2_process_in(struct pico_protocol *self, struct pico_frame *f){ + pico_igmp2_process_in(self, f); + return 0; +} +int test_pico_igmp2_set_membershipState(struct pico_ip4 *mgroup_addr ,uint8_t state){ + struct mgroup_info *info = pico_igmp2_find_mgroup(mgroup_addr); + info->membership_state = state; + igmp2_dbg("DEBUG_IGMP2:STATE = %s\n", (info->membership_state == 0 ? "Non-Member" : (info->membership_state == 1 ? "Delaying MEMBER" : "Idle MEMBER"))); + return 0; +} +uint8_t test_pico_igmp2_get_membershipState(struct pico_ip4 *mgroup_addr){ + struct mgroup_info *info = pico_igmp2_find_mgroup(mgroup_addr); + igmp2_dbg("DEBUG_IGMP2:STATE = %s\n", (info->membership_state == 0 ? "Non-Member" : (info->membership_state == 1 ? "Delaying Member" : "Idle Member"))); + return info->membership_state; +} +int test_pico_igmp2_process_event(struct igmp2_packet_params *params) { + pico_igmp2_process_event(params); + return 0; +} + +int test_pico_igmp2_analyse_packet(struct pico_frame *f, struct igmp2_packet_params *params){ + pico_igmp2_analyse_packet(f, params); + return 0; +} +#else +#define igmp2_dbg(...) do{}while(0) +#endif + + +/* Queues */ +static struct pico_queue igmp_in = {}; +static struct pico_queue igmp_out = {}; + +static int pico_igmp2_analyse_packet(struct pico_frame *f, struct igmp2_packet_params *params){ + struct pico_igmp2_hdr *hdr = (struct pico_igmp2_hdr *) f->transport_hdr; + switch (hdr->type){ + case PICO_IGMP2_TYPE_MEM_QUERY: + params->event = PICO_IGMP2_EVENT_QUERY_RECV; + break; + case PICO_IGMP2_TYPE_V1_MEM_REPORT: + params->event = PICO_IGMP2_EVENT_REPORT_RECV; + break; + case PICO_IGMP2_TYPE_V2_MEM_REPORT: + params->event = PICO_IGMP2_EVENT_REPORT_RECV; + break; + default: + pico_frame_discard(f); + pico_err = PICO_ERR_EINVAL; + return -1; + } + params->group_address.addr = hdr->group_address; + params->max_resp_time = hdr->max_resp_time; + params->f = f; + return 0; +} + +static int check_igmp2_checksum(struct pico_frame *f){ + struct pico_igmp2_hdr *igmp2_hdr = (struct pico_igmp2_hdr *) f->transport_hdr; + uint16_t header_checksum; + + if (!igmp2_hdr) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + header_checksum = igmp2_hdr->crc; + igmp2_hdr->crc=0; + + if(header_checksum == short_be(pico_checksum(igmp2_hdr, sizeof(struct pico_igmp2_hdr)))){ + igmp2_hdr->crc = header_checksum; + return 0; + }else{ + igmp2_hdr->crc = header_checksum; + pico_err = PICO_ERR_EFAULT; + return -1; + } +} + +static int pico_igmp2_checksum(struct pico_frame *f) +{ + struct pico_igmp2_hdr *igmp2_hdr = (struct pico_igmp2_hdr *) f->transport_hdr; + if (!igmp2_hdr) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + igmp2_hdr->crc = 0; + igmp2_hdr->crc = short_be(pico_checksum(igmp2_hdr, sizeof(struct pico_igmp2_hdr))); + //igmp2_dbg("CHECKSUM = %04X\n",igmp2_hdr->crc); + return 0; +} + + +static int pico_igmp2_process_in(struct pico_protocol *self, struct pico_frame *f) +{ + struct igmp2_packet_params params; + + igmp2_dbg("pico_igmp2_process_in\n"); + + if (check_igmp2_checksum(f) == 0) { + if (!pico_igmp2_analyse_packet(f,¶ms)) { + pico_igmp2_process_event(¶ms); + } + }else{ + igmp2_dbg("failed for igmp2 checksum\n"); + pico_frame_discard(f); + } + return 0; +} + +static int pico_igmp2_process_out(struct pico_protocol *self, struct pico_frame *f) { + // TODO impmement this function + return 0; +} + +/* Interface: protocol definition */ +struct pico_protocol pico_proto_igmp2 = { + .name = "igmp2", + .proto_number = PICO_PROTO_IGMP2, + .layer = PICO_LAYER_TRANSPORT, + .process_in = pico_igmp2_process_in, + .process_out = pico_igmp2_process_out, + .q_in = &igmp_in, + .q_out = &igmp_out, +}; + + +/*====================== API CALLS ======================*/ + +int pico_igmp2_join_group(struct pico_ip4 *group_address, struct pico_ipv4_link *link) { + struct igmp2_packet_params params = {{0}}; + + params.event = PICO_IGMP2_EVENT_JOIN_GROUP ; + params.group_address.addr = group_address->addr; + params.src_interface.addr = link->address.addr; + + return pico_igmp2_process_event(¶ms); +} + +int pico_igmp2_leave_group(struct pico_ip4 *group_address, struct pico_ipv4_link *link) { + struct igmp2_packet_params params = {{0}}; + + params.event = PICO_IGMP2_EVENT_LEAVE_GROUP ; + params.group_address.addr = group_address->addr; + params.src_interface.addr = link->address.addr; + + return pico_igmp2_process_event(¶ms); +} + +/*================== GENERAL FUNCTIONS ==================*/ + +static int start_timer(struct igmp2_packet_params *params,const uint16_t delay){ + + struct mgroup_info *info = pico_igmp2_find_mgroup(&(params->group_address)); + struct timer_callback_info *timer_info= pico_zalloc(sizeof(struct timer_callback_info)); + timer_info->timer_starttime = PICO_TIME_MS(); + info->delay = delay; + timer_info->f = params->f; + + info->active_timer_starttime = timer_info->timer_starttime; + + pico_timer_add(delay, &generate_event_timer_expired, timer_info); + return 0; +} + +static int stop_timer(struct pico_ip4 *group_address){ + + struct mgroup_info *info = pico_igmp2_find_mgroup(group_address); + info->active_timer_starttime = NO_ACTIVE_TIMER; + return 0; +} + + +static int reset_timer(struct igmp2_packet_params *params) +{ + uint8_t ret = 0; + uint16_t delay = pico_rand() % (params->max_resp_time*100); + + ret |= stop_timer(&(params->group_address)); + ret |= start_timer(params, delay); + return ret; +} + +static int send_membership_report(struct pico_frame *f){ + uint8_t ret = 0; + struct pico_igmp2_hdr *igmp2_hdr = (struct pico_igmp2_hdr *) f->transport_hdr; + + struct pico_ip4 dst = {0}; + struct pico_ip4 group_address = {0}; + group_address.addr = igmp2_hdr->group_address; + dst.addr = igmp2_hdr->group_address; + + igmp2_dbg("send_membership_report on group %x\n",group_address.addr); + pico_ipv4_frame_push(f,&dst,PICO_PROTO_IGMP2); + ret |= stop_timer(&group_address); + return ret; +} + +static int send_leave(struct pico_frame *f) +{ + uint8_t ret = 0; + struct pico_igmp2_hdr *igmp2_hdr = (struct pico_igmp2_hdr *) f->transport_hdr; + struct pico_ip4 group_address = {0}; + struct pico_ip4 dst = {0}; + + igmp2_dbg("send leave\n"); + group_address.addr = igmp2_hdr->group_address; + dst.addr = PICO_IGMP2_ALL_ROUTER_GROUP; + + pico_ipv4_frame_push(f,&dst,PICO_PROTO_IGMP2); + ret |= stop_timer(&group_address); + return ret; +} + +static int create_igmp2_frame(struct pico_frame **f, struct pico_ip4 src, struct pico_ip4 *mcast_addr, uint8_t type){ + uint8_t ret = 0; + struct pico_igmp2_hdr *igmp2_hdr = NULL; + struct pico_ipv4_hdr *ipv4_hdr; + + *f = pico_proto_ipv4.alloc(&pico_proto_ipv4, sizeof(struct pico_igmp2_hdr)); + ipv4_hdr = (struct pico_ipv4_hdr *) (*f)->net_hdr; + + // Fill IPV4 header + ipv4_hdr->src.addr = src.addr; + ipv4_hdr->ttl = 1; + + // Fill The IGMP2_HDR + igmp2_hdr = (struct pico_igmp2_hdr *) (*f)->transport_hdr; + + igmp2_hdr->type = type; + igmp2_hdr->max_resp_time = PICO_IGMP2_DEFAULT_MAX_RESPONSE_TIME; + igmp2_hdr->group_address = mcast_addr->addr; + + ret |= pico_igmp2_checksum(*f); + return ret; +} + +/*================== TIMER CALLBACKS ====================*/ + +static void generate_event_timer_expired(long unsigned int empty, void *data) { + struct timer_callback_info *info = (struct timer_callback_info *) data; + struct igmp2_packet_params params = {{0}}; + struct pico_frame* f = (struct pico_frame*)info->f; + struct pico_igmp2_hdr *igmp2_hdr = (struct pico_igmp2_hdr *) f->transport_hdr; + + params.event = PICO_IGMP2_EVENT_TIMER_EXPIRED; + params.group_address.addr = igmp2_hdr->group_address; + params.timer_starttime = info->timer_starttime; + params.f = info->f; + + pico_igmp2_process_event(¶ms); + pico_free(info); +} + +/* ------------------ */ +/* HOST STATE MACHINE */ +/* ------------------ */ + +/* state callbacks prototype */ +typedef int (*callback)(struct igmp2_packet_params *); + + +/*------------ ACTIONS ------------*/ +/* +#ACTION1 STSLIFS: stop timer, send leave if flag set +#ACTION2 SRSFST: send report, set flag, start timer +#ACTION3 SLIFS: send leave if flag set +#ACTION4 ST: start timer +#ACTION5 STCL: stop timer, clear flag +#ACTION6 SRSF: send report, set flag +#ACTION7 RTIMRTCT: reset timer if Max resp time < current time +*/ + +static int action1(struct igmp2_packet_params *params) +{ + uint8_t ret = 0; + struct mgroup_info *info = pico_igmp2_find_mgroup(&(params->group_address)); + struct pico_frame *f = NULL; + + igmp2_dbg("DEBUG_IGMP2:EVENT = Leave Group\n"); + igmp2_dbg("DEBUG_IGMP2:ACTION = STSLIFS\n"); + ret |= stop_timer(&(params->group_address)); + + if (PICO_IGMP2_HOST_LAST == info->Last_Host_flag) { + ret |= create_igmp2_frame(&f, params->src_interface, &(params->group_address), PICO_IGMP2_TYPE_LEAVE_GROUP); + ret |= send_leave(f); + } + + /*Check if action is completed successfully, if so then adjust Membership State*/ + if( 0 == ret) { + igmp2_dbg("DEBUG_IGMP2:NEW STATE = Non-Member\n"); + /*del element from tree*/ + pico_igmp2_del_mgroup(info); + return 0; + }else{ + pico_err = PICO_ERR_EFAULT; + return -1; + } +} + +static int action2(struct igmp2_packet_params *params) +{ + uint8_t ret = 0; + struct pico_frame *f = NULL; + struct mgroup_info *info = pico_zalloc(sizeof(struct mgroup_info)); + struct pico_frame *copy_frame; + + igmp2_dbg("DEBUG_IGMP2:EVENT = Join Group\n"); + igmp2_dbg("DEBUG_IGMP2:ACTION = SRSFST\n"); + + /*insert in tree*/ + info->mgroup_addr.addr = params->group_address.addr; + info->src_interface.addr = params->src_interface.addr; + info->membership_state = PICO_IGMP2_STATES_NON_MEMBER; + info->Last_Host_flag = PICO_IGMP2_HOST_LAST; + info->active_timer_starttime = NO_ACTIVE_TIMER; + + pico_tree_insert(&KEYTable,info); + /*---------------*/ + + ret |= create_igmp2_frame(&f, params->src_interface, &(params->group_address), PICO_IGMP2_TYPE_V2_MEM_REPORT); + + copy_frame = pico_frame_copy(f); + if (copy_frame == NULL) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + ret |= send_membership_report(copy_frame); + info->delay = (pico_rand() %( PICO_IGMP2_UNSOLICITED_REPORT_INTERVAL*100)); + params->f = f; + ret |= start_timer(params, info->delay); + /*Check if action is completed successfully, if so then adjust Membership State*/ + if( 0 == ret) { + struct mgroup_info *info = pico_igmp2_find_mgroup(&(params->group_address)); + info->membership_state = PICO_IGMP2_STATES_DELAYING_MEMBER; + igmp2_dbg("DEBUG_IGMP2:NEW STATE = Delaying Member\n"); + return 0; + }else{ + pico_err = PICO_ERR_EFAULT; + return -1; + } +} + +static int action3(struct igmp2_packet_params *params) +{ + struct pico_frame *f = NULL; + struct mgroup_info *info; + uint8_t ret = 0; + + igmp2_dbg("DEBUG_IGMP2:EVENT = Leave Group\n"); + igmp2_dbg("DEBUG_IGMP2:ACTION = SLIFS\n"); + + info = pico_igmp2_find_mgroup(&(params->group_address)); + if (PICO_IGMP2_HOST_LAST == info->Last_Host_flag) { + ret |= create_igmp2_frame(&f, params->src_interface, &(params->group_address), PICO_IGMP2_TYPE_LEAVE_GROUP); + send_leave(f); + } + + /*Check if action is completed successfully, if so then adjust Membership State*/ + if( 0 == ret) { + igmp2_dbg("DEBUG_IGMP2:NEW STATE = Non-Member\n"); + /*del element from tree*/ + pico_igmp2_del_mgroup(info); + return 0; + }else{ + pico_err = PICO_ERR_ENOENT; + return -1; + } +} + +static int action4(struct igmp2_packet_params *params) +{ + uint8_t ret = 0; + struct mgroup_info *info = pico_igmp2_find_mgroup(&(params->group_address)); + + igmp2_dbg("DEBUG_IGMP2:EVENT = Query Received\n"); + igmp2_dbg("DEBUG_IGMP2:ACTION = ST\n"); + + ret |= create_igmp2_frame(&(params->f), info->src_interface, &(params->group_address), PICO_IGMP2_TYPE_V2_MEM_REPORT); + + info->delay = (pico_rand() % (params->max_resp_time*100) ); + ret |= start_timer(params, info->delay); + + /*Check if action is completed successfully, if so then adjust Membership State*/ + if( 0 == ret) { + info->membership_state = PICO_IGMP2_STATES_DELAYING_MEMBER; + igmp2_dbg("DEBUG_IGMP2:NEW STATE = %s\n", (info->membership_state == 0 ? "Non-Member" : (info->membership_state == 1 ? "Delaying Member" : "Idle Member"))); + return 0; + }else{ + pico_err = PICO_ERR_ENOENT; + return -1; + } +} + +static int action5(struct igmp2_packet_params *params) +{ + uint8_t ret = 0; + struct mgroup_info *info = pico_igmp2_find_mgroup(&(params->group_address)); + + igmp2_dbg("DEBUG_IGMP2:EVENT = Report Received\n"); + igmp2_dbg("DEBUG_IGMP2:ACTION = STCL\n"); + + ret |= stop_timer(&(params->group_address)); + info->Last_Host_flag = PICO_IGMP2_HOST_LAST; + + /*Check if action is completed successfully, if so then adjust Membership State*/ + if( 0 == ret) { + info->membership_state = PICO_IGMP2_STATES_IDLE_MEMBER; + igmp2_dbg("DEBUG_IGMP2:NEW STATE = %s\n", (info->membership_state == 0 ? "Non-Member" : (info->membership_state == 1 ? "Delayed Member" : "Idle Member"))); + return 0; + }else{ + pico_err = PICO_ERR_ENOENT; + return -1; + } +} + +static int action6(struct igmp2_packet_params *params) +{ + uint8_t ret = 0; + struct mgroup_info *info = pico_igmp2_find_mgroup(&(params->group_address)); + + igmp2_dbg("DEBUG_IGMP2:EVENT = Timer Expired\n"); + igmp2_dbg("DEBUG_IGMP2:ACTION = SRSF\n"); + + if ( info->active_timer_starttime == params->timer_starttime) { + ret |= send_membership_report(params->f); + } + else { + pico_frame_discard(params->f); + } + + //Check if action is completed successfully, if so then adjust Membership State + if( 0 == ret) { + info->membership_state = PICO_IGMP2_STATES_IDLE_MEMBER; + igmp2_dbg("DEBUG_IGMP2:NEW STATE = %s\n", (info->membership_state == 0 ? "Non-Member" : (info->membership_state == 1 ? "Delaying Member" : "Idle Member"))); + return 0; + }else{ + pico_err = PICO_ERR_ENOENT; + return -1; + } +} + +static int action7(struct igmp2_packet_params *params) +{ + uint8_t ret = 0; + struct mgroup_info *info = pico_igmp2_find_mgroup(&(params->group_address)); + unsigned long current_time_left = ((unsigned long)info->delay - (PICO_TIME_MS()-(unsigned long)info->active_timer_starttime)); + + igmp2_dbg("DEBUG_IGMP2:EVENT = Query Received\n"); + igmp2_dbg("DEBUG_IGMP2:ACTION = RTIMRTCT\n"); + + if ( ((unsigned long) (params->max_resp_time*100)) < current_time_left) { + ret |= create_igmp2_frame(&(params->f), params->src_interface, &(params->group_address), PICO_IGMP2_TYPE_V2_MEM_REPORT); + ret |= reset_timer(params); + } + /*Check if action is completed successfully, if so then adjust Membership State*/ + if( 0 == ret) { + info->membership_state = PICO_IGMP2_STATES_DELAYING_MEMBER; + igmp2_dbg("DEBUG_IGMP2:NEW STATE = %s\n", (info->membership_state == 0 ? "Non-Member" : (info->membership_state == 1 ? "Delaying Member" : "Idle Member"))); + return 0; + }else{ + pico_err = PICO_ERR_ENOENT; + return -1; + } +} + +static int ignore_and_discardframe(struct igmp2_packet_params *params){ + igmp2_dbg("ignore and discard frame igmp2\n"); + pico_frame_discard(params->f); + return 0; +} + +static int generate_err1(struct igmp2_packet_params *params){ + igmp2_dbg("ERROR: STATE = Non-Member, EVENT = Leave Group"); + pico_err = PICO_ERR_ENOENT; + return -1; +} + +static int generate_err2(struct igmp2_packet_params *params){ + igmp2_dbg("ERROR: STATE = Delaying Member, EVENT = Join Group"); + pico_err = PICO_ERR_EEXIST; + return -1; +} + +static int generate_err3(struct igmp2_packet_params *params){ + igmp2_dbg("ERROR: STATE = Idle Member, EVENT = Join Group"); + pico_err = PICO_ERR_EEXIST; + return -1; +} + +static int generate_err4(struct igmp2_packet_params *params){ + igmp2_dbg("ERROR: STATE = Non-Member, EVENT = Report Received"); + pico_err = PICO_ERR_ENOENT; + return -1; +} + +/* finite state machine table */ +const callback host_membership_diagram_table[3][5] = +{ /* event |Leave Group |Join Group |Query Received |Report Received |Timer Expired */ +/* state Non-Member */ { generate_err1, action2, ignore_and_discardframe, generate_err4, ignore_and_discardframe }, +/* state Delaying Member */ { action1, generate_err2, action7, action5, action6 }, +/* state Idle Member */ { action3, generate_err3, action4, ignore_and_discardframe, ignore_and_discardframe } +}; + +static int pico_igmp2_process_event(struct igmp2_packet_params *params) { + struct pico_tree_node * index; + uint8_t ret = 0; + struct mgroup_info *info = pico_igmp2_find_mgroup(&(params->group_address)); + igmp2_dbg("pico_igmp2_process_event , params->group_address = %x\n",params->group_address.addr); + if (NULL == info) { + if(params->event == PICO_IGMP2_EVENT_QUERY_RECV){ + pico_tree_foreach(index,&KEYTable){ + info = index->keyValue; + params->src_interface.addr = info->src_interface.addr; + params->group_address.addr = info->mgroup_addr.addr; + igmp2_dbg("FOR EACH params->group_address = %x\n",params->group_address.addr); + + igmp2_dbg("DEBUG_IGMP2:STATE = %s\n", (info->membership_state == 0 ? "Non-Member" : (info->membership_state == 1 ? "Delaying Member" : "Idle Member"))); + ret |= host_membership_diagram_table[info->membership_state][params->event](params); + } + } + else{//first time this group enters the state diagram + igmp2_dbg("DEBUG_IGMP2:STATE = Non-Member\n"); + ret |= host_membership_diagram_table[PICO_IGMP2_STATES_NON_MEMBER][params->event](params); + } + }else { + igmp2_dbg("DEBUG_IGMP2:STATE = %s\n", (info->membership_state == 0 ? "Non-Member" : (info->membership_state == 1 ? "Delaying Member" : "Idle Member"))); + ret |= host_membership_diagram_table[info->membership_state][params->event](params); + } + + if( 0 == ret) { + return 0; + }else{ + igmp2_dbg("ERROR: pico_igmp2_process_event FAILED!\n"); + return -1; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/modules/pico_igmp2.h Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,58 @@ +/********************************************************************* +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_IGMP2 +#define _INCLUDE_PICO_IGMP2 + +extern struct pico_protocol pico_proto_igmp2; + +struct __attribute__((packed)) pico_igmp2_hdr { + uint8_t type; + uint8_t max_resp_time; + uint16_t crc; + uint32_t group_address; +}; + + +// HOST FLAG DEFS +#define PICO_IGMP2_HOST_LAST (0x1) +#define PICO_IGMP2_HOST_NOT_LAST (0x0) + +// EVENT DEFS +#define PICO_IGMP2_EVENT_LEAVE_GROUP (0x0) +#define PICO_IGMP2_EVENT_JOIN_GROUP (0x1) +#define PICO_IGMP2_EVENT_QUERY_RECV (0x2) +#define PICO_IGMP2_EVENT_REPORT_RECV (0x3) +#define PICO_IGMP2_EVENT_TIMER_EXPIRED (0x4) + +// MEMBERSHIP DEFS +#define PICO_IGMP2_STATES_NON_MEMBER (0x0) +#define PICO_IGMP2_STATES_DELAYING_MEMBER (0x1) +#define PICO_IGMP2_STATES_IDLE_MEMBER (0x2) + +// +#define PICO_IGMP2_TYPE_MEM_QUERY (0x11) +#define PICO_IGMP2_TYPE_V1_MEM_REPORT (0x12) +#define PICO_IGMP2_TYPE_V2_MEM_REPORT (0x16) +#define PICO_IGMP2_TYPE_LEAVE_GROUP (0x17) + + +//ALL_ROUTER_GROUP 224.0.0.2 +#define PICO_IGMP2_ALL_ROUTER_GROUP (0x020000E0) + +//ALL_HOST_GROUP 224.0.0.1 +#define PICO_IGMP2_ALL_HOST_GROUP (0x010000E0) + +#define PICO_IGMP2_DEFAULT_MAX_RESPONSE_TIME (100) +#define PICO_IGMP2_UNSOLICITED_REPORT_INTERVAL (100) + +int pico_igmp2_join_group(struct pico_ip4 *group_address, struct pico_ipv4_link *link); +int pico_igmp2_leave_group(struct pico_ip4 *group_address, struct pico_ipv4_link *link); + +#endif /* _INCLUDE_PICO_IGMP2 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/modules/pico_ipfilter.c Thu Apr 25 13:22:51 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/libraries/picotcp/modules/pico_ipfilter.h Thu Apr 25 13:22:51 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/libraries/picotcp/modules/pico_ipv4.c Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,1260 @@ +/********************************************************************* +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_igmp2.h" +#include "pico_tree.h" + +#ifdef PICO_SUPPORT_IPV4 + +#ifdef PICO_SUPPORT_MCAST +# define mcast_dbg(...) do{}while(0) +# 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 = *((uint32_t *) &buf[0]); + + 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; +} + +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, PICO_SIZE_IP4HDR)); + 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) - PICO_SIZE_IP4HDR; + 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) - PICO_SIZE_IP4HDR; + 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) - PICO_SIZE_IP4HDR); + 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) - PICO_SIZE_IP4HDR); + 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); + + reassembly_dbg("REASSEMBLY: copy IP header information len = %lu\n", PICO_SIZE_IP4HDR); + f_frag = pico_tree_first(pfrag->t); + f_frag_hdr = (struct pico_ipv4_hdr *)f_frag->net_hdr; + data_len = short_be(f_frag_hdr->len) - PICO_SIZE_IP4HDR; + memcpy(f_new->net_hdr, f_frag->net_hdr, PICO_SIZE_IP4HDR); + 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) - PICO_SIZE_IP4HDR; + 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) - PICO_SIZE_IP4HDR; + 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, PICO_SIZE_IP4HDR)); + /* 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, PICO_SIZE_IP4HDR)); + 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_is_group_member(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; + +#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_unicast(hdr->src.addr)) { + 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_unicast(hdr->dst.addr) ) { +#ifdef PICO_SUPPORT_MCAST + /* Receiving UDP multicast datagram TODO set f->flags? */ + if (hdr->proto == PICO_PROTO_IGMP2) { + mcast_dbg("MCAST: received IGMP message\n"); + pico_transport_receive(f, PICO_PROTO_IGMP2); + } else if (pico_ipv4_mcast_is_group_member(f) && (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 + size; + 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 +struct pico_mcast_group { + struct pico_ipv4_link *mcast_link; + struct pico_ip4 mcast_addr; + uint16_t reference_count; +}; + +static int mcast_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 void pico_ipv4_mcast_print_groups(struct pico_ipv4_link *mcast_link) +{ + struct pico_mcast_group __attribute__((unused)) *g = NULL; + struct pico_tree_node * index; + uint16_t i = 0; + + mcast_dbg("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); + mcast_dbg("+ MULTICAST list interface %-16s +\n", mcast_link->dev->name); + mcast_dbg("+--------------------------------------------------------+\n"); + mcast_dbg("+ nr | interface | host group | reference count +\n"); + mcast_dbg("+--------------------------------------------------------+\n"); + + pico_tree_foreach(index, mcast_link->mcast_head){ + g = index->keyValue; + mcast_dbg("+ %04d | %16s | %08X | %05u +\n", i, g->mcast_link->dev->name, g->mcast_addr.addr, g->reference_count); + i++; + } + mcast_dbg("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); +} + +int pico_ipv4_mcast_join_group(struct pico_ip4 *mcast_addr, struct pico_ipv4_link *mcast_link) +{ + struct pico_mcast_group *g, test = {0}; + struct pico_ipv4_link *link; + + if (pico_ipv4_is_unicast(mcast_addr->addr)) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + /* RFC 1112, section 7.1 suggests to also check on validity of mcast_link */ + + if (mcast_link) + link = mcast_link; + else + link = mcast_default_link; + + test.mcast_addr = *mcast_addr; + + g = pico_tree_findKey(link->mcast_head, &test); + + if (g) { + g->reference_count++; + } else { + g = pico_zalloc(sizeof(struct pico_mcast_group)); + if (!g) { + pico_err = PICO_ERR_ENOMEM; + return -1; + } + g->mcast_link = link; + g->mcast_addr = *mcast_addr; + g->reference_count = 1; + + pico_tree_insert(link->mcast_head,g); + + + if (mcast_addr->addr != PICO_MCAST_ALL_HOSTS) { + dbg("MCAST: sent IGMP host membership report\n"); + pico_igmp2_join_group(mcast_addr, link); + } + } + + pico_ipv4_mcast_print_groups(link); + return 0; +} + +int pico_ipv4_mcast_leave_group(struct pico_ip4 *mcast_addr, struct pico_ipv4_link *mcast_link) +{ + + struct pico_mcast_group *g, test = {0}; + struct pico_ipv4_link *link; + + if (pico_ipv4_is_unicast(mcast_addr->addr)) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + /* RFC 1112, section 7.1 suggests to also check on validity of mcast_link */ + + if (mcast_link) + link = mcast_link; + else + link = mcast_default_link; + + test.mcast_addr = *mcast_addr; + + g = pico_tree_findKey(link->mcast_head,&test); + if (g) { + g->reference_count--; + if (g->reference_count < 1) { + if (mcast_addr->addr != PICO_MCAST_ALL_HOSTS) { + dbg("MCAST: sent IGMP leave group\n"); + pico_igmp2_leave_group(mcast_addr, link); + } + + pico_tree_delete(link->mcast_head,g); + } + } else { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + pico_ipv4_mcast_print_groups(link); + return 0; +} + +static int pico_ipv4_mcast_is_group_member(struct pico_frame *f) +{ + struct pico_ipv4_link *link; + struct pico_tree_node * index; + struct pico_mcast_group *g, 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->mcast_head,&test); + if (g) { + if (f->dev == link->dev) { + mcast_dbg("MCAST: IP %08X is group member of current link %s\n", hdr->dst.addr, f->dev->name); + return 1; + } else { + mcast_dbg("MCAST: IP %08X is group member of different link %s\n", hdr->dst.addr, link->dev->name); + } + } + } + mcast_dbg("MCAST: IP %08X is not a group member of current link %s\n", hdr->dst.addr, f->dev->name); + return 0; +} + +#else + +int pico_ipv4_mcast_join_group(struct pico_ip4 *mcast_addr, struct pico_ipv4_link *mcast_link) +{ + pico_err = PICO_ERR_EPROTONOSUPPORT; + return -1; +} +int pico_ipv4_mcast_leave_group(struct pico_ip4 *mcast_addr, struct pico_ipv4_link *mcast_link) +{ + pico_err = PICO_ERR_EPROTONOSUPPORT; + return -1; +} + +#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; + static uint16_t ipv4_progressive_id = 0x91c0; + + 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) { + pico_err = PICO_ERR_EHOSTUNREACH; + if (pico_ipv4_is_unicast(dst->addr)) { + dbg("Route to %08x not found.\n", long_be(dst->addr)); + goto drop; + } +#ifdef PICO_SUPPORT_MCAST + link = mcast_default_link; + if(pico_udp_get_mc_ttl(f->sock, &ttl) < 0) + ttl = PICO_IP_DEFAULT_MULTICAST_TTL; +#else + goto drop; +#endif + } else { + link = route->link; + } + + hdr->vhl = 0x45; + hdr->len = short_be(f->transport_len + PICO_SIZE_IP4HDR); + 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) + PICO_SIZE_IP4HDR); + /* 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_unicast(hdr->dst.addr)) { + struct pico_frame *cpy; + /* Sending UDP multicast datagram, am I member? If so, loopback copy */ + if (pico_ipv4_mcast_is_group_member(f)) { + 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->mcast_head = pico_zalloc(sizeof(struct pico_tree)); + if (!new->mcast_head) { + pico_free(new); + dbg("IPv4: Out of memory!\n"); + pico_err = PICO_ERR_ENOMEM; + return -1; + } + + new->mcast_head->root = &LEAF; + new->mcast_head->compare = mcast_cmp; +#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; + 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); + if (!mcast_default_link) { + 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_group(&mcast_all_hosts, new); + } 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_mcast_group *g = NULL; + struct pico_tree_node * index, * _tmp; + + pico_tree_foreach_safe(index,found->mcast_head, _tmp) + { + g = index->keyValue; + pico_tree_delete(found->mcast_head,g); + pico_free(g); + } + } while(0); +#endif + + pico_tree_delete(&Tree_dev_link, found); + return 0; +} + + +struct pico_ipv4_link *pico_ipv4_link_get(struct pico_ip4 *address) +{ + struct pico_ipv4_link test, *found = NULL; + test.address.addr = address->addr; + + found = pico_tree_findKey(&Tree_dev_link, &test); + if (!found) + return NULL; + else + return found; +} + + +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/libraries/picotcp/modules/pico_ipv4.h Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,80 @@ +/********************************************************************* +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; + struct pico_tree * mcast_head; +}; + +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_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_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_group(struct pico_ip4 *mcast_addr, struct pico_ipv4_link *mcast_link); +int pico_ipv4_mcast_leave_group(struct pico_ip4 *mcast_addr, struct pico_ipv4_link *mcast_link); + +#endif /* _INCLUDE_PICO_IPV4 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/modules/pico_ipv6.h Thu Apr 25 13:22:51 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/libraries/picotcp/modules/pico_nat.c Thu Apr 25 13:22:51 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, PICO_SIZE_IP4HDR)); + + 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, PICO_SIZE_IP4HDR)); + + 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/libraries/picotcp/modules/pico_nat.h Thu Apr 25 13:22:51 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/libraries/picotcp/modules/pico_simple_http.c Thu Apr 25 13:22:51 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/libraries/picotcp/modules/pico_simple_http.h Thu Apr 25 13:22:51 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/libraries/picotcp/modules/pico_tcp.c Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,2156 @@ +/********************************************************************* +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 1000 +#define PICO_TCP_RTO_MAX 120000 +#define PICO_TCP_IW 2 + +#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(...) do{}while(0) +//#define tcp_dbg dbg + +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) +{ + if (f->payload_len <= 0) { + tcp_dbg("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TRIED TO ENQUEUE INVALID SEGMENT!\n"); + //abort(); + return -1; + } + + if ((tq->size + f->payload_len) > tq->max_size) + return 0; + if (pico_tree_insert(&tq->pool,f) != 0) + return 0; + tq->size += f->payload_len; + if (f->payload_len > 0) + tq->frames++; + return f->payload_len; +} + +static void pico_discard_segment(struct pico_tcp_queue *tq, struct pico_frame *f) +{ + struct pico_frame *f1; + 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); +} + +/* 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 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); + } 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 = (uint32_t *)(opt + i); + i += 4; + end = (uint32_t *)(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; + 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("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("TCP Window scale: bad len received (%d).\n", len); + i += len - 2; + break; + } + t->recv_wnd_scale = opt[i++]; + tcp_dbg("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("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("TCP option mss: bad len received.\n"); + i += len - 2; + break; + } + t->mss_ok = 1; + mss = (uint16_t *)(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("TCP option timestamp: bad len received.\n"); + i += len - 2; + break; + } + t->ts_ok = 1; + tsval = (uint32_t *)(opt + i); + i += sizeof(uint32_t); + tsecr = (uint32_t *)(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("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; + } + + 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++; + 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; + + /* enable 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(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_RTO_MIN << 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); + + tcp_dbg("TCP SEND_RST >>>>>>>>>>>>>>> START\n"); + /* 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; + + 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; + } else { + /* non-synchronized state */ + } + + 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 */ + //(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); + + 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); + + 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); + + if (seq_compare(SEQN(f), t->rcv_nxt) >= 0) { + struct pico_frame *cpy = pico_frame_copy(f); + struct pico_frame *nxt; + /* Enqueue: try to put into RCV buffer */ + if (pico_enqueue_segment(&t->tcpq_in, cpy) <= 0) { + pico_frame_discard(cpy); + return -1; + } + if (seq_compare(SEQN(f), t->rcv_nxt) == 0) { /* Exactly what we expected */ + 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. Possible 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 AVG: %u RTTVAR: %u RTO: %u ======================----\n", 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); + 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; + + 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\n", SEQN(f), f->payload_len, t->rto); + 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 */ + if(una && (una->timestamp != 0)) { + 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("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("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)) { + memcpy(new->sock.remote_addr.ip6.addr, ((struct pico_ipv6_hdr *)(f->net_hdr))->src, PICO_SIZE_IP6); + memcpy(new->sock.remote_addr.ip6.addr, ((struct pico_ipv6_hdr *)(f->net_hdr))->src, PICO_SIZE_IP6); + } +#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, &tcp_nosync_rst, &tcp_nosync_rst, &tcp_first_ack, &tcp_nosync_rst, &tcp_nosync_rst, &tcp_nosync_rst, &tcp_rst }, + { PICO_SOCKET_STATE_TCP_ESTABLISHED, &tcp_send_rst, &tcp_send_rst, &tcp_ack, &tcp_data_in, &tcp_closewait, &tcp_closewait, &tcp_rst }, + { PICO_SOCKET_STATE_TCP_CLOSE_WAIT, &tcp_send_rst, &tcp_send_rst, &tcp_ack, &tcp_send_rst, &tcp_send_rst, &tcp_send_rst, &tcp_rst }, + { PICO_SOCKET_STATE_TCP_LAST_ACK, &tcp_send_rst, &tcp_send_rst, &tcp_lastackwait, &tcp_send_rst, &tcp_send_rst, &tcp_send_rst, &tcp_rst }, + { PICO_SOCKET_STATE_TCP_FIN_WAIT1, &tcp_send_rst, &tcp_send_rst, &tcp_finwaitack, &tcp_data_in, &tcp_rcvfin, &tcp_finack, &tcp_rst }, + { PICO_SOCKET_STATE_TCP_FIN_WAIT2, &tcp_send_rst, &tcp_send_rst, &tcp_ack, &tcp_data_in, &tcp_finwaitfin, &tcp_finack, &tcp_rst }, + { PICO_SOCKET_STATE_TCP_CLOSING, &tcp_send_rst, &tcp_send_rst, &tcp_closewaitack, &tcp_send_rst, &tcp_send_rst, &tcp_send_rst, &tcp_rst }, + { PICO_SOCKET_STATE_TCP_TIME_WAIT, &tcp_send_rst, &tcp_send_rst, &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)...\n", t->backoff); + 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("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); + + /* 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("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; + add_retransmission_timer(t, pico_tick + t->rto); + } else { + // no packets in queue ?? + } + + 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 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("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("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("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("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("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("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("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/libraries/picotcp/modules/pico_tcp.h Thu Apr 25 13:22:51 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/libraries/picotcp/modules/pico_udp.c Thu Apr 25 13:22:51 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/libraries/picotcp/modules/pico_udp.h Thu Apr 25 13:22:51 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/stack/pico_arp.c Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,317 @@ +/********************************************************************* +PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. +See LICENSE and COPYING for usage. + +. + +Authors: Daniele Lacamera +*********************************************************************/ + + +#include "pico_config.h" +#include "pico_arp.h" +#include "pico_tree.h" +#include "pico_ipv4.h" +#include "pico_device.h" +#include "pico_stack.h" + +const uint8_t PICO_ETHADDR_ALL[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +#define PICO_ARP_TIMEOUT 600000 +#define PICO_ARP_RETRY 300 + +#ifdef DEBUG_ARP + #define arp_dbg dbg +#else + #define arp_dbg(...) do{}while(0) +#endif + +static struct pico_queue pending; +static int pending_timer_on = 0; + +void check_pending(unsigned long now, void *_unused) +{ + struct pico_frame *f = pico_dequeue(&pending); + if (!f) { + pending_timer_on = 0; + return; + } + if(pico_ethernet_send(f) > 0) + pico_frame_discard(f); + pico_timer_add(PICO_ARP_RETRY, &check_pending, NULL); +} + + +struct +__attribute__ ((__packed__)) +pico_arp_hdr +{ + uint16_t htype; + uint16_t ptype; + uint8_t hsize; + uint8_t psize; + uint16_t opcode; + uint8_t s_mac[PICO_SIZE_ETH]; + struct pico_ip4 src; + uint8_t d_mac[PICO_SIZE_ETH]; + struct pico_ip4 dst; +}; + + +#define PICO_SIZE_ARPHDR ((sizeof(struct pico_arp_hdr))) + +/* Arp Entries for the tables. */ +struct pico_arp { +/* CAREFUL MAN! ARP entry MUST begin with a pico_eth structure, + * due to in-place casting!!! */ + struct pico_eth eth; + struct pico_ip4 ipv4; + int arp_status; + uint32_t timestamp; + struct pico_device *dev; +}; + + + +/*****************/ +/** ARP TREE **/ +/*****************/ + +/* Routing destination */ + +static int arp_compare(void * ka, void * kb) +{ + struct pico_arp *a = ka, *b = kb; + if (a->ipv4.addr < b->ipv4.addr) + return -1; + else if (a->ipv4.addr > b->ipv4.addr) + return 1; + return 0; +} + +PICO_TREE_DECLARE(arp_tree, arp_compare); + +/*********************/ +/** END ARP TREE **/ +/*********************/ + +struct pico_eth *pico_arp_lookup(struct pico_ip4 *dst) +{ + struct pico_arp search, *found; + search.ipv4.addr = dst->addr; + found = pico_tree_findKey(&arp_tree,&search); + if (found && (found->arp_status != PICO_ARP_STATUS_STALE)) + return &found->eth; + return NULL; +} + +struct pico_ip4 *pico_arp_reverse_lookup(struct pico_eth *dst) +{ + struct pico_arp* search; + struct pico_tree_node * index; + pico_tree_foreach(index,&arp_tree){ + search = index->keyValue; + if(memcmp(&(search->eth.addr), &dst->addr, 6) == 0) + return &search->ipv4; + } + return NULL; +} + +struct pico_eth *pico_arp_get(struct pico_frame *f) { + struct pico_eth *a4; + struct pico_ip4 gateway; + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr; + struct pico_ipv4_link *l; + + l = pico_ipv4_link_get(&hdr->dst); + if(l){ + //address belongs to ourself + return &l->dev->eth->mac; + } + + gateway = pico_ipv4_route_get_gateway(&hdr->dst); + /* check if dst is local (gateway = 0), or if to use gateway */ + if (gateway.addr != 0) + a4 = pico_arp_lookup(&gateway); /* check if gateway ip mac in cache */ + else + a4 = pico_arp_lookup(&hdr->dst); /* check if local ip mac in cache */ + if (!a4) { + if (++f->failure_count < 4) { + dbg ("================= ARP REQUIRED: %d =============\n\n", f->failure_count); + /* check if dst is local (gateway = 0), or if to use gateway */ + if (gateway.addr != 0) + pico_arp_query(f->dev, &gateway); /* arp to gateway */ + else + pico_arp_query(f->dev, &hdr->dst); /* arp to dst */ + + pico_enqueue(&pending, f); + if (!pending_timer_on) { + pending_timer_on++; + pico_timer_add(PICO_ARP_RETRY, &check_pending, NULL); + } + } else { + dbg("ARP: Destination Unreachable\n"); + pico_notify_dest_unreachable(f); + pico_frame_discard(f); + } + } + return a4; +} + +#ifdef DEBUG_ARP +void dbg_arp(void) +{ + struct pico_arp *a; + struct pico_tree_node * index; + + pico_tree_foreach(index,&arp_tree) { + a = index->keyValue; + arp_dbg("ARP to %08x, mac: %02x:%02x:%02x:%02x:%02x:%02x\n", a->ipv4.addr,a->eth.addr[0],a->eth.addr[1],a->eth.addr[2],a->eth.addr[3],a->eth.addr[4],a->eth.addr[5] ); + } +} +#endif + +void arp_expire(unsigned long now, void *_stale) +{ + struct pico_arp *stale = (struct pico_arp *) _stale; + stale->arp_status = PICO_ARP_STATUS_STALE; + arp_dbg("ARP: Setting arp_status to STALE\n"); + pico_arp_query(stale->dev, &stale->ipv4); + +} + +void pico_arp_add_entry(struct pico_arp *entry) +{ + entry->arp_status = PICO_ARP_STATUS_REACHABLE; + entry->timestamp = PICO_TIME(); + + pico_tree_insert(&arp_tree,entry); + arp_dbg("ARP ## reachable.\n"); + pico_timer_add(PICO_ARP_TIMEOUT, arp_expire, entry); +} + +int pico_arp_create_entry(uint8_t* hwaddr, struct pico_ip4 ipv4, struct pico_device* dev) +{ + struct pico_arp* arp = pico_zalloc(sizeof(struct pico_arp)); + if(!arp){ + pico_err = PICO_ERR_ENOMEM; + return -1; + } + memcpy(arp->eth.addr, hwaddr, 6); + arp->ipv4.addr = ipv4.addr; + arp->dev = dev; + + pico_arp_add_entry(arp); + + return 0; +} + +int pico_arp_receive(struct pico_frame *f) +{ + struct pico_arp_hdr *hdr; + struct pico_arp search, *found, *new = NULL; + int ret = -1; + hdr = (struct pico_arp_hdr *) f->net_hdr; + + if (!hdr) + goto end; + + + /* Populate a new arp entry */ + search.ipv4.addr = hdr->src.addr; + memcpy(search.eth.addr, hdr->s_mac, PICO_SIZE_ETH); + + /* Search for already existing entry */ + + found = pico_tree_findKey(&arp_tree,&search); + if (!found) { + new = pico_zalloc(sizeof(struct pico_arp)); + if (!new) + goto end; + new->ipv4.addr = hdr->src.addr; + } + else if (found->arp_status == PICO_ARP_STATUS_STALE) { + /* Replace if stale */ + new = found; + + pico_tree_delete(&arp_tree,new); + } + + ret = 0; + + if (new) { + memcpy(new->eth.addr, hdr->s_mac, PICO_SIZE_ETH); + new->dev = f->dev; + pico_arp_add_entry(new); + } + + if (hdr->opcode == PICO_ARP_REQUEST) { + struct pico_ip4 me; + struct pico_eth_hdr *eh = (struct pico_eth_hdr *)f->datalink_hdr; + struct pico_device *link_dev; + me.addr = hdr->dst.addr; + + link_dev = pico_ipv4_link_find(&me); + if (link_dev != f->dev) + goto end; + + hdr->opcode = PICO_ARP_REPLY; + memcpy(hdr->d_mac, hdr->s_mac, PICO_SIZE_ETH); + memcpy(hdr->s_mac, f->dev->eth->mac.addr, PICO_SIZE_ETH); + hdr->dst.addr = hdr->src.addr; + hdr->src.addr = me.addr; + + /* Prepare eth header for arp reply */ + memcpy(eh->daddr, eh->saddr, PICO_SIZE_ETH); + memcpy(eh->saddr, f->dev->eth->mac.addr, PICO_SIZE_ETH); + f->start = f->datalink_hdr; + f->len = PICO_SIZE_ETHHDR + PICO_SIZE_ARPHDR; + f->dev->send(f->dev, f->start, f->len); + } + +#ifdef DEBUG_ARG + dbg_arp(); +#endif + +end: + pico_frame_discard(f); + return ret; +} + +int pico_arp_query(struct pico_device *dev, struct pico_ip4 *dst) +{ + struct pico_frame *q = pico_frame_alloc(PICO_SIZE_ETHHDR + PICO_SIZE_ARPHDR); + struct pico_eth_hdr *eh; + struct pico_arp_hdr *ah; + struct pico_ip4 *src; + int ret; + + src = pico_ipv4_source_find(dst); + if (!src) + return -1; + + arp_dbg("QUERY: %08x\n", dst->addr); + + if (!q) + return -1; + eh = (struct pico_eth_hdr *)q->start; + ah = (struct pico_arp_hdr *) (q->start + PICO_SIZE_ETHHDR); + + /* Fill eth header */ + memcpy(eh->saddr, dev->eth->mac.addr, PICO_SIZE_ETH); + memcpy(eh->daddr, PICO_ETHADDR_ALL, PICO_SIZE_ETH); + eh->proto = PICO_IDETH_ARP; + + /* Fill arp header */ + ah->htype = PICO_ARP_HTYPE_ETH; + ah->ptype = PICO_IDETH_IPV4; + ah->hsize = PICO_SIZE_ETH; + ah->psize = PICO_SIZE_IP4; + ah->opcode = PICO_ARP_REQUEST; + memcpy(ah->s_mac, dev->eth->mac.addr, PICO_SIZE_ETH); + ah->src.addr = src->addr; + ah->dst.addr = dst->addr; + arp_dbg("Sending arp query.\n"); + ret = dev->send(dev, q->start, q->len); + pico_frame_discard(q); + return ret; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/stack/pico_device.c Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,240 @@ +/********************************************************************* +PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. +See LICENSE and COPYING for usage. + +. + +Authors: Daniele Lacamera +*********************************************************************/ + + +#include "pico_config.h" +#include "pico_device.h" +#include "pico_stack.h" +#include "pico_protocol.h" +#include "pico_tree.h" + + +static int pico_dev_cmp(void *ka, void *kb) +{ + struct pico_device *a = ka, *b = kb; + if (a->hash < b->hash) + return -1; + if (a->hash > b->hash) + return 1; + return 0; +} + +PICO_TREE_DECLARE(Device_tree,pico_dev_cmp); + +int pico_device_init(struct pico_device *dev, char *name, uint8_t *mac) +{ + int len = strlen(name); + if(len>MAX_DEVICE_NAME) + len = MAX_DEVICE_NAME; + memcpy(dev->name, name, len); + dev->hash = pico_hash(dev->name); + + pico_tree_insert(&Device_tree,dev); + dev->q_in = pico_zalloc(sizeof(struct pico_queue)); + dev->q_out = pico_zalloc(sizeof(struct pico_queue)); + + if (mac) { + dev->eth = pico_zalloc(sizeof(struct pico_ethdev)); + memcpy(dev->eth->mac.addr, mac, PICO_SIZE_ETH); + } else { + dev->eth = NULL; + } + + if (!dev->q_in || !dev->q_out || (mac && !dev->eth)) + return -1; + return 0; +} + +void pico_device_destroy(struct pico_device *dev) +{ + if (dev->destroy) + dev->destroy(dev); + + if (dev->q_in) { + pico_queue_empty(dev->q_in); + pico_free(dev->q_in); + } + if (dev->q_out) { + pico_queue_empty(dev->q_out); + pico_free(dev->q_out); + } + + if (dev->eth) + pico_free(dev->eth); + + pico_tree_delete(&Device_tree,dev); + pico_free(dev); +} + +static int devloop(struct pico_device *dev, int loop_score, int direction) +{ + struct pico_frame *f; + + /* If device supports polling, give control. Loop score is managed internally, + * remaining loop points are returned. */ + if (dev->poll) { + loop_score = dev->poll(dev, loop_score); + } + + if (direction == PICO_LOOP_DIR_OUT) { + + while(loop_score > 0) { + if (dev->q_out->frames <= 0) + break; + + /* Device dequeue + send */ + f = pico_dequeue(dev->q_out); + if (f) { + if (dev->eth) { + int ret = pico_ethernet_send(f); + if (0 == ret) { + loop_score--; + continue; + } if (ret < 0) { + if (!pico_source_is_local(f)) { + dbg("Destination unreachable -------> SEND ICMP\n"); + pico_notify_dest_unreachable(f); + } else { + dbg("Destination unreachable -------> LOCAL\n"); + } + pico_frame_discard(f); + continue; + } + } else { + dev->send(dev, f->start, f->len); + } + pico_frame_discard(f); + loop_score--; + } + } + + } else if (direction == PICO_LOOP_DIR_IN) { + + while(loop_score > 0) { + if (dev->q_in->frames <= 0) + break; + + /* Receive */ + f = pico_dequeue(dev->q_in); + if (f) { + if (dev->eth) { + f->datalink_hdr = f->buffer; + pico_ethernet_receive(f); + } else { + f->net_hdr = f->buffer; + pico_network_receive(f); + } + loop_score--; + } + } + } + + return loop_score; +} + + +#define DEV_LOOP_MIN 16 + +int pico_devices_loop(int loop_score, int direction) +{ + struct pico_device *start; + static struct pico_device *next = NULL, *next_in = NULL, *next_out = NULL; + static struct pico_tree_node * next_node, * in_node, * out_node; + + if (next_in == NULL) { + in_node = pico_tree_firstNode(Device_tree.root); + next_in = in_node->keyValue; + } + if (next_out == NULL) { + out_node = pico_tree_firstNode(Device_tree.root); + next_out = out_node->keyValue; + } + + if (direction == PICO_LOOP_DIR_IN) + { + next_node = in_node; + next = next_in; + } + else if (direction == PICO_LOOP_DIR_OUT) + { + next_node = out_node; + next = next_out; + } + + /* init start node */ + start = next; + + /* round-robin all devices, break if traversed all devices */ + while (loop_score > DEV_LOOP_MIN && next != NULL) { + loop_score = devloop(next, loop_score, direction); + + next_node = pico_tree_next(next_node); + next = next_node->keyValue; + + if (next == NULL) + { + next_node = pico_tree_firstNode(Device_tree.root); + next = next_node->keyValue; + } + if (next == start) + break; + } + + if (direction == PICO_LOOP_DIR_IN) + { + in_node = next_node; + next_in = next; + } + else if (direction == PICO_LOOP_DIR_OUT) + { + out_node = next_node; + next_out = next; + } + + return loop_score; +} + +struct pico_device* pico_get_device(char* name) +{ + struct pico_device *dev; + struct pico_tree_node * index; + pico_tree_foreach(index, &Device_tree){ + dev = index->keyValue; + if(strcmp(name, dev->name) == 0) + return dev; + } + return NULL; +} + +int pico_device_broadcast(struct pico_frame * f) +{ + struct pico_tree_node * index; + int ret = -1; + + pico_tree_foreach(index,&Device_tree) + { + struct pico_device * dev = index->keyValue; + if(dev != f->dev) + { + struct pico_frame * copy = pico_frame_copy(f); + + if(!copy) + return -1; + copy->dev = dev; + copy->dev->send(copy->dev, copy->start, copy->len); + pico_frame_discard(copy); + } + else + { + ret = f->dev->send(f->dev, f->start, f->len); + } + } + + return ret; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/stack/pico_frame.c Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,192 @@ +/********************************************************************* +PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. +See LICENSE and COPYING for usage. + +. + +Authors: Daniele Lacamera +*********************************************************************/ + + +#include "pico_config.h" +#include "pico_frame.h" + +#ifdef PICO_SUPPORT_DEBUG_MEMORY +static int n_frames_allocated; +#endif + +/** frame alloc/dealloc/copy **/ +void pico_frame_discard(struct pico_frame *f) +{ + (*f->usage_count)--; + if (*f->usage_count <= 0) { + pico_free(f->usage_count); +#ifdef PICO_SUPPORT_DEBUG_MEMORY + dbg("Discarded buffer @%p, caller: %p\n", f->buffer, __builtin_return_address(3)); + dbg("DEBUG MEMORY: %d frames in use.\n", --n_frames_allocated); +#endif + pico_free(f->buffer); + if (f->info) + pico_free(f->info); + } +#ifdef PICO_SUPPORT_DEBUG_MEMORY + else { + dbg("Removed frame @%p(copy), usage count now: %d\n", f, *f->usage_count); + } +#endif + pico_free(f); +} + +struct pico_frame *pico_frame_copy(struct pico_frame *f) +{ + struct pico_frame *new = pico_zalloc(sizeof(struct pico_frame)); + if (!new) + return NULL; + memcpy(new, f, sizeof(struct pico_frame)); + *(new->usage_count) += 1; +#ifdef PICO_SUPPORT_DEBUG_MEMORY + dbg("Copied frame @%p, into %p, usage count now: %d\n", f, new, *new->usage_count); +#endif + new->next = NULL; + return new; +} + + +struct pico_frame *pico_frame_alloc(int size) +{ + struct pico_frame *p = pico_zalloc(sizeof(struct pico_frame)); + if (!p) + return NULL; + p->buffer = pico_zalloc(size); + if (!p->buffer) { + pico_free(p); + return NULL; + } + p->usage_count = pico_zalloc(sizeof(uint32_t)); + if (!p->usage_count) { + pico_free(p->buffer); + pico_free(p); + return NULL; + } + p->buffer_len = size; + + + /* By default, frame content is the full buffer. */ + p->start = p->buffer; + p->len = p->buffer_len; + *p->usage_count = 1; +#ifdef PICO_SUPPORT_DEBUG_MEMORY + dbg("Allocated buffer @%p, len= %d caller: %p\n", p->buffer, p->buffer_len, __builtin_return_address(2)); + dbg("DEBUG MEMORY: %d frames in use.\n", ++n_frames_allocated); +#endif + return p; +} + +struct pico_frame *pico_frame_deepcopy(struct pico_frame *f) +{ + struct pico_frame *new = pico_frame_alloc(f->buffer_len); + int addr_diff; + unsigned char *buf; + uint32_t *uc; + if (!new) + return NULL; + + /* Save the two key pointers... */ + buf = new->buffer; + uc = new->usage_count; + + /* Overwrite all fields with originals */ + memcpy(new, f, sizeof(struct pico_frame)); + + /* ...restore the two key pointers */ + new->buffer = buf; + new->usage_count = uc; + + /* Update in-buffer pointers with offset */ + addr_diff = (int)new->buffer - (int)f->buffer; + new->net_hdr += addr_diff; + new->transport_hdr += addr_diff; + new->app_hdr += addr_diff; + new->start += addr_diff; + new->payload += addr_diff; + +#ifdef PICO_SUPPORT_DEBUG_MEMORY + dbg("Deep-Copied frame @%p, into %p, usage count now: %d\n", f, new, *new->usage_count); +#endif + new->next = NULL; + return new; +} + +/** + * Calculate checksum of a given string + */ +uint16_t pico_checksum(void *inbuf, int len) +{ + uint8_t *buf = (uint8_t *) inbuf; + uint16_t tmp = 0; + uint32_t sum = 0, carry=0; + int i=0; + for(i=0; i<len; i++){ + if (i%2){ + sum+=buf[i]; + }else{ + tmp = buf[i]; + sum+=( tmp << 8); + } + } + carry = (sum&0xFFFF0000) >>16; + sum = (sum&0x0000FFFF); + return (uint16_t) ~(sum + carry) ; +} + +uint16_t pico_dualbuffer_checksum(void *inbuf1, int len1, void *inbuf2, int len2) +{ + uint8_t *b1 = (uint8_t *) inbuf1; + uint8_t *b2 = (uint8_t *) inbuf2; + uint32_t sum = 0, carry=0; + int i=0, j=0; + for(i=0; i<len1; i++){ + if (j%2){ + sum+=b1[i]; + }else{ + sum+=( b1[i] << 8); + } + j++; + } + + for(i=0; i<len2; i++){ + if (j%2){ + sum+=b2[i]; + }else{ + sum+=( b2[i] << 8); + } + j++; + } + carry = (sum&0xFFFF0000) >>16; + sum = (sum&0x0000FFFF); + return (uint16_t) (~(sum + carry)) ; +} + +uint16_t pico_dualbuffer_checksum_broken(void *inbuf1, int len1, void *inbuf2, int len2) +{ + uint16_t *b1 = (uint16_t *) inbuf1; + uint16_t *b2 = (uint16_t *) inbuf2; + uint32_t sum = 0; + int i=0, j=0; + for(i=0; i<(len1>>1); i++){ + sum += short_be(b1[i]); + j++; + } + for(i=0; i<(len2>>1); i++){ + sum += short_be(b2[i]); + j++; + } + sum = (sum & 0xFFFF) + (sum >> 16); + sum += (sum >> 16); + + // Take the bitwise complement of sum + sum = ~sum; + return (uint16_t) (sum) ; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/stack/pico_protocol.c Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,350 @@ +/********************************************************************* +PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. +See LICENSE and COPYING for usage. + +. + +Authors: Daniele Lacamera +*********************************************************************/ + + +#include "pico_protocol.h" +#include "pico_tree.h" + +static int pico_proto_cmp(void *ka, void *kb) +{ + struct pico_protocol *a = ka, *b=kb; + if (a->hash < b->hash) + return -1; + if (a->hash > b->hash) + return 1; + return 0; +} + +PICO_TREE_DECLARE(Datalink_proto_tree,pico_proto_cmp); +PICO_TREE_DECLARE(Network_proto_tree,pico_proto_cmp); +PICO_TREE_DECLARE(Transport_proto_tree,pico_proto_cmp); +PICO_TREE_DECLARE(Socket_proto_tree,pico_proto_cmp); + +static int proto_loop(struct pico_protocol *proto, int loop_score, int direction) +{ + struct pico_frame *f; + + if (direction == PICO_LOOP_DIR_IN) { + + while(loop_score >0) { + if (proto->q_in->frames <= 0) + break; + + f = pico_dequeue(proto->q_in); + if ((f) &&(proto->process_in(proto, f) > 0)) { + loop_score--; + } + } + + } else if (direction == PICO_LOOP_DIR_OUT) { + + while(loop_score >0) { + if (proto->q_out->frames <= 0) + break; + + f = pico_dequeue(proto->q_out); + if ((f) &&(proto->process_out(proto, f) > 0)) { + loop_score--; + } + } + } + + return loop_score; +} + +#define DL_LOOP_MIN 1 + +int pico_protocol_datalink_loop(int loop_score, int direction) +{ + struct pico_protocol *start; + static struct pico_protocol *next = NULL, *next_in = NULL, *next_out = NULL; + static struct pico_tree_node * next_node, * in_node, * out_node; + + if (next_in == NULL) { + in_node = pico_tree_firstNode(Datalink_proto_tree.root); + if (in_node) + next_in = in_node->keyValue; + } + if (next_out == NULL) { + out_node = pico_tree_firstNode(Datalink_proto_tree.root); + if (out_node) + next_out = out_node->keyValue; + } + + if (direction == PICO_LOOP_DIR_IN) + { + next_node = in_node; + next = next_in; + } + else if (direction == PICO_LOOP_DIR_OUT) + { + next_node = out_node; + next = next_out; + } + + /* init start node */ + start = next; + + /* round-robin all datalink protocols, break if traversed all protocols */ + while (loop_score > DL_LOOP_MIN && next != NULL) { + loop_score = proto_loop(next, loop_score, direction); + + //next = RB_NEXT(pico_protocol_tree, &Datalink_proto_tree, next); + next_node = pico_tree_next(next_node); + next = next_node->keyValue; + + if (next == NULL) + { + next_node = pico_tree_firstNode(Datalink_proto_tree.root); + next = next_node->keyValue; + } + if (next == start) + break; + } + + if (direction == PICO_LOOP_DIR_IN) + { + in_node = next_node; + next_in = next; + } + else if (direction == PICO_LOOP_DIR_OUT) + { + out_node = next_node; + next_out = next; + } + + return loop_score; +} + + +#define NW_LOOP_MIN 1 + +int pico_protocol_network_loop(int loop_score, int direction) +{ + struct pico_protocol *start; + static struct pico_protocol *next = NULL, *next_in = NULL, *next_out = NULL; + static struct pico_tree_node * next_node, * in_node, * out_node; + + if (next_in == NULL) { + in_node = pico_tree_firstNode(Network_proto_tree.root); + if (in_node) + next_in = in_node->keyValue; + } + if (next_out == NULL) { + out_node = pico_tree_firstNode(Network_proto_tree.root); + if (out_node) + next_out = out_node->keyValue; + } + if (direction == PICO_LOOP_DIR_IN) + { + next_node = in_node; + next = next_in; + } + else if (direction == PICO_LOOP_DIR_OUT) + { + next_node = out_node; + next = next_out; + } + + /* init start node */ + start = next; + + /* round-robin all network protocols, break if traversed all protocols */ + while (loop_score > NW_LOOP_MIN && next != NULL) { + loop_score = proto_loop(next, loop_score, direction); + + next_node = pico_tree_next(next_node); + next = next_node->keyValue; + + if (next == NULL) + { + next_node = pico_tree_firstNode(Network_proto_tree.root); + next = next_node->keyValue; + } + if (next == start) + break; + } + + if (direction == PICO_LOOP_DIR_IN) + { + in_node = next_node; + next_in = next; + } + else if (direction == PICO_LOOP_DIR_OUT) + { + out_node = next_node; + next_out = next; + } + + return loop_score; +} + +#define TP_LOOP_MIN 1 + +int pico_protocol_transport_loop(int loop_score, int direction) +{ + struct pico_protocol *start; + static struct pico_protocol *next = NULL, *next_in = NULL, *next_out = NULL; + static struct pico_tree_node * next_node, * in_node, * out_node; + + if (next_in == NULL) { + in_node = pico_tree_firstNode(Transport_proto_tree.root); + if (in_node) + next_in = in_node->keyValue; + } + if (next_out == NULL) { + out_node = pico_tree_firstNode(Transport_proto_tree.root); + if (out_node) + next_out = out_node->keyValue; + } + + if (direction == PICO_LOOP_DIR_IN) + { + next_node = in_node; + next = next_in; + } + else if (direction == PICO_LOOP_DIR_OUT) + { + next_node = out_node; + next = next_out; + } + + /* init start node */ + start = next; + + /* round-robin all transport protocols, break if traversed all protocols */ + while (loop_score > DL_LOOP_MIN && next != NULL) { + loop_score = proto_loop(next, loop_score, direction); + + //next = RB_NEXT(pico_protocol_tree, &Transport_proto_tree, next); + next_node = pico_tree_next(next_node); + next = next_node->keyValue; + + if (next == NULL) + { + next_node = pico_tree_firstNode(Transport_proto_tree.root); + next = next_node->keyValue; + } + if (next == start) + break; + } + + if (direction == PICO_LOOP_DIR_IN) + { + in_node = next_node; + next_in = next; + } + else if (direction == PICO_LOOP_DIR_OUT) + { + out_node = next_node; + next_out = next; + } + + return loop_score; +} + + +#define SOCK_LOOP_MIN 1 + +int pico_protocol_socket_loop(int loop_score, int direction) +{ + struct pico_protocol *start; + static struct pico_protocol *next = NULL, *next_in = NULL, *next_out = NULL; + static struct pico_tree_node * next_node, * in_node, * out_node; + + if (next_in == NULL) { + in_node = pico_tree_firstNode(Socket_proto_tree.root); + if(in_node) + next_in = in_node->keyValue; + } + if (next_out == NULL) { + out_node = pico_tree_firstNode(Socket_proto_tree.root); + if(out_node) + next_out = out_node->keyValue; + } + + if (direction == PICO_LOOP_DIR_IN) + { + next_node = in_node; + next = next_in; + } + else if (direction == PICO_LOOP_DIR_OUT) + { + next_node = out_node; + next = next_out; + } + + /* init start node */ + start = next; + + /* round-robin all transport protocols, break if traversed all protocols */ + while (loop_score > SOCK_LOOP_MIN && next != NULL) { + loop_score = proto_loop(next, loop_score,direction); + + next_node = pico_tree_next(next_node); + next = next_node->keyValue; + + if (next == NULL) + { + next_node = pico_tree_firstNode(next_node); + next = next_node->keyValue; + } + if (next == start) + break; + } + + if (direction == PICO_LOOP_DIR_IN) + { + in_node = next_node; + next_in = next; + } + else if (direction == PICO_LOOP_DIR_OUT) + { + out_node = next_node; + next_out = next; + } + + return loop_score; +} + +int pico_protocols_loop(int loop_score) +{ +/* + loop_score = pico_protocol_datalink_loop(loop_score); + loop_score = pico_protocol_network_loop(loop_score); + loop_score = pico_protocol_transport_loop(loop_score); + loop_score = pico_protocol_socket_loop(loop_score); +*/ + return loop_score; +} + +void pico_protocol_init(struct pico_protocol *p) +{ + if (!p) + return; + + p->hash = pico_hash(p->name); + switch (p->layer) { + case PICO_LAYER_DATALINK: + pico_tree_insert(&Datalink_proto_tree, p); + break; + case PICO_LAYER_NETWORK: + pico_tree_insert(&Network_proto_tree,p); + break; + case PICO_LAYER_TRANSPORT: + pico_tree_insert(&Transport_proto_tree,p); + break; + case PICO_LAYER_SOCKET: + pico_tree_insert(&Socket_proto_tree,p); + break; + } + dbg("Protocol %s registered (layer: %d).\n", p->name, p->layer); + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/stack/pico_socket.c Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,1550 @@ +/********************************************************************* +PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. +See LICENSE and COPYING for usage. + + +Authors: Daniele Lacamera +*********************************************************************/ + + +#include "pico_config.h" +#include "pico_queue.h" +#include "pico_socket.h" +#include "pico_ipv4.h" +#include "pico_ipv6.h" +#include "pico_udp.h" +#include "pico_tcp.h" +#include "pico_stack.h" +#include "pico_icmp4.h" +#include "pico_nat.h" +#include "pico_tree.h" +#include "pico_device.h" + +#if defined (PICO_SUPPORT_IPV4) || defined (PICO_SUPPORT_IPV6) +#if defined (PICO_SUPPORT_TCP) || defined (PICO_SUPPORT_UDP) + + +#define PROTO(s) ((s)->proto->proto_number) + +#ifdef PICO_SUPPORT_TCP +# define IS_NAGLE_ENABLED(s) (s->opt_flags & (1 << PICO_SOCKET_OPT_TCPNODELAY)) +#endif + +#define PICO_SOCKET_MTU 1480 /* Ethernet MTU(1500) - IP header size(20) */ + +#ifdef PICO_SUPPORT_IPV4 +# define IS_SOCK_IPV4(s) ((s->net == &pico_proto_ipv4)) +#else +# define IS_SOCK_IPV4(s) (0) +#endif + +#ifdef PICO_SUPPORT_IPV6 +# define IS_SOCK_IPV6(s) ((s->net == &pico_proto_ipv6)) +#else +# define IS_SOCK_IPV6(s) (0) +#endif + +#ifdef PICO_SUPPORT_IPFRAG +# define frag_dbg(...) do{}while(0) +#endif + +static struct pico_sockport *sp_udp = NULL ,*sp_tcp = NULL; + +struct pico_frame *pico_socket_frame_alloc(struct pico_socket *s, int len); + +static int socket_cmp(void * ka, void * kb) +{ + struct pico_socket *a = ka, *b = kb; + int a_is_ip6 = is_sock_ipv6(a); + int b_is_ip6 = is_sock_ipv6(b); + + int diff; + + /* First, order by network ver */ + if (a_is_ip6 < b_is_ip6) + return -1; + if (a_is_ip6 > b_is_ip6) + return 1; + + /* If either socket is PICO_IPV4_INADDR_ANY mode, skip local address comparison */ + + /* At this point, sort by local host */ + + if (0) { +#ifdef PICO_SUPPORT_IPV6 + } else if (a_is_ip6) { + if ((memcmp(a->local_addr.ip6.addr, PICO_IP6_ANY, PICO_SIZE_IP6)==0) || memcmp((b->local_addr.ip6.addr, PICO_IP6_ANY, PICO_SIZE_IP6) == 0)) + diff = 0; + else + diff = memcmp(a->local_addr.ip6.addr, b->local_addr.ip6.addr, PICO_SIZE_IP6); +#endif + } else { + if ((a->local_addr.ip4.addr == PICO_IP4_ANY) || (b->local_addr.ip4.addr == PICO_IP4_ANY)) + diff = 0; + else + diff = a->local_addr.ip4.addr - b->local_addr.ip4.addr; + } + + if (diff) + return diff; + + + /* Sort by remote host */ + if (a_is_ip6) + diff = memcmp(a->remote_addr.ip6.addr, b->remote_addr.ip6.addr, PICO_SIZE_IP6); + else + diff = a->remote_addr.ip4.addr - b->remote_addr.ip4.addr; + + if (diff) + return diff; + + /* And finally by remote port. The two sockets are coincident if the quad is the same. */ + return b->remote_port - a->remote_port; +} + +struct pico_sockport +{ + struct pico_tree socks; // how you make the connection ? + uint16_t number; + uint16_t proto; +}; + +#define INIT_SOCKPORT { {&LEAF , socket_cmp}, 0, 0 } + +int sockport_cmp(void * ka, void * kb) +{ + struct pico_sockport *a = ka, *b = kb; + if (a->number < b->number) + return -1; + if (a->number > b->number) + return 1; + return 0; +} + +PICO_TREE_DECLARE(UDPTable,sockport_cmp); +PICO_TREE_DECLARE(TCPTable,sockport_cmp); + +static struct pico_sockport *pico_get_sockport(uint16_t proto, uint16_t port) +{ + struct pico_sockport test = INIT_SOCKPORT; + test.number = port; + + if (proto == PICO_PROTO_UDP) + return pico_tree_findKey(&UDPTable,&test); + + else if (proto == PICO_PROTO_TCP) + return pico_tree_findKey(&TCPTable,&test); + + else return NULL; +} + +int pico_is_port_free(uint16_t proto, uint16_t port, void *addr, void *net) +{ + struct pico_sockport *sp; + struct pico_ip4 ip; + sp = pico_get_sockport(proto, port); + + if (!net) + net = &pico_proto_ipv4; + + /** IPv6 (wip) ***/ + if (net != &pico_proto_ipv4) { + dbg("IPV6!!!!!\n"); + return (!sp); + } + + /* IPv4 */ +#ifdef PICO_SUPPORT_NAT + if (pico_ipv4_nat_find(port,NULL, 0,proto) == 0) { + dbg("In use by nat....\n"); + return 0; + } +#endif + if (addr) + ip.addr = ((struct pico_ip4 *)addr)->addr; + else + ip.addr = PICO_IPV4_INADDR_ANY; + + if (ip.addr == PICO_IPV4_INADDR_ANY) { + if (!sp) return 1; + else { + dbg("In use, and asked for ANY\n"); + return 0; + } + } + if (sp) { + struct pico_ip4 *s_local; + struct pico_tree_node *idx; + struct pico_socket *s; + pico_tree_foreach(idx, &sp->socks) { + s = idx->keyValue; + if (s->net == &pico_proto_ipv4) { + s_local = (struct pico_ip4*) &s->local_addr; + if ((s_local->addr == PICO_IPV4_INADDR_ANY) || (s_local->addr == ip.addr)) + return 0; + } + } + } + return 1; +} + +static int pico_check_socket(struct pico_socket *s) +{ + struct pico_sockport *test; + struct pico_socket *found; + struct pico_tree_node * index; + + test = pico_get_sockport(PROTO(s), s->local_port); + + if (!test) { + return -1; + } + + pico_tree_foreach(index,&test->socks){ + found = index->keyValue; + if (s == found) { + return 0; + } + } + + return -1; +} + + +int pico_socket_add(struct pico_socket *s) +{ + struct pico_sockport *sp = pico_get_sockport(PROTO(s), s->local_port); + if (!sp) { + //dbg("Creating sockport..%04x\n", s->local_port); /* In comment due to spam during test */ + sp = pico_zalloc(sizeof(struct pico_sockport)); + + if (!sp) { + pico_err = PICO_ERR_ENOMEM; + return -1; + } + sp->proto = PROTO(s); + sp->number = s->local_port; + sp->socks.root = &LEAF; + sp->socks.compare = socket_cmp; + + if (PROTO(s) == PICO_PROTO_UDP) + { + pico_tree_insert(&UDPTable,sp); + } + else if (PROTO(s) == PICO_PROTO_TCP) + { + pico_tree_insert(&TCPTable,sp); + } + } + + pico_tree_insert(&sp->socks,s); + s->state |= PICO_SOCKET_STATE_BOUND; + +#if DEBUG_SOCKET_TREE + { + struct pico_tree_node * index; + //RB_FOREACH(s, socket_tree, &sp->socks) { + pico_tree_foreach(index,&sp->socks){ + s = index->keyValue; + dbg(">>>> List Socket lc=%hu rm=%hu\n", short_be(s->local_port), short_be(s->remote_port)); + } + + } +#endif + return 0; +} + +static void socket_garbage_collect(unsigned long now, void *arg) +{ + struct pico_socket *s = (struct pico_socket *) arg; + pico_free(s); +} + +int pico_socket_del(struct pico_socket *s) +{ + struct pico_sockport *sp = pico_get_sockport(PROTO(s), s->local_port); + + if (!sp) { + pico_err = PICO_ERR_ENXIO; + return -1; + } + pico_tree_delete(&sp->socks,s); + + if(pico_tree_empty(&sp->socks)){ + if (PROTO(s) == PICO_PROTO_UDP) + { + pico_tree_delete(&UDPTable,sp); + } + else if (PROTO(s) == PICO_PROTO_TCP) + { + pico_tree_delete(&TCPTable,sp); + } + + if(sp_tcp == sp) sp_tcp = NULL; + + if(sp_udp == sp) sp_udp = NULL; + + pico_free(sp); + + } + + s->state = PICO_SOCKET_STATE_CLOSED; + pico_timer_add(3000, socket_garbage_collect, s); + + + return 0; +} + +static int pico_socket_alter_state(struct pico_socket *s, uint16_t more_states, uint16_t less_states, uint16_t tcp_state) +{ + struct pico_sockport *sp; + if (more_states & PICO_SOCKET_STATE_BOUND) + return pico_socket_add(s); + + if (less_states & PICO_SOCKET_STATE_BOUND) + return pico_socket_del(s); + + sp = pico_get_sockport(PROTO(s), s->local_port); + if (!sp) { + pico_err = PICO_ERR_ENXIO; + return -1; + } + + s->state |= more_states; + s->state &= (~less_states); + if (tcp_state) { + s->state &= 0x00FF; + s->state |= tcp_state; + } + + return 0; +} + +static int pico_socket_deliver(struct pico_protocol *p, struct pico_frame *f, uint16_t localport) +{ + struct pico_frame *cpy = NULL; + struct pico_sockport *sp = NULL; + struct pico_socket *s = NULL, *found = NULL; + struct pico_tree_node *index = NULL; + struct pico_trans *tr = (struct pico_trans *) f->transport_hdr; + #ifdef PICO_SUPPORT_IPV4 + struct pico_ipv4_hdr *ip4hdr; + #endif + #ifdef PICO_SUPPORT_IPV6 + struct pico_ipv6_hdr *ip6hdr; + #endif + + if (!tr) + return -1; + + sp = pico_get_sockport(p->proto_number, localport); + + if (!sp) { + dbg("No such port %d\n",short_be(localport)); + return -1; + } + + #ifdef PICO_SUPPORT_TCP + if (p->proto_number == PICO_PROTO_TCP) { + pico_tree_foreach(index,&sp->socks){ + s = index->keyValue; + /* 4-tuple identification of socket (port-IP) */ + #ifdef PICO_SUPPORT_IPV4 + if (IS_IPV4(f)) { + struct pico_ip4 s_local, s_remote, p_src, p_dst; + ip4hdr = (struct pico_ipv4_hdr*)(f->net_hdr); + s_local.addr = s->local_addr.ip4.addr; + s_remote.addr = s->remote_addr.ip4.addr; + p_src.addr = ip4hdr->src.addr; + p_dst.addr = ip4hdr->dst.addr; + if ( (s->remote_port == tr->sport) && /* remote port check */ + (s_remote.addr == p_src.addr) && /* remote addr check */ + ((s_local.addr == PICO_IPV4_INADDR_ANY) || (s_local.addr == p_dst.addr))) { /* Either local socket is ANY, or matches dst */ + found = s; + break; + } else if ( (s->remote_port == 0) && /* not connected... listening */ + ((s_local.addr == PICO_IPV4_INADDR_ANY) || (s_local.addr == p_dst.addr))) { /* Either local socket is ANY, or matches dst */ + /* listen socket */ + found = s; + } + } + #endif + #ifdef PICO_SUPPORT_IPV6 /* XXX TODO make compare for ipv6 addresses */ + if (IS_IPV6(f)) { + ip6hdr = (struct pico_ipv6_hdr*)(f->net_hdr); + if ( (s->remote_port == localport) ) { // && (((struct pico_ip6) s->remote_addr.ip6).addr == ((struct pico_ip6)(ip6hdr->src)).addr) ) { + found = s; + break; + } else if (s->remote_port == 0) { + /* listen socket */ + found = s; + } + } + #endif + } /* FOREACH */ + if (found != NULL) { + pico_tcp_input(found,f); + if ((found->ev_pending) && found->wakeup) { + found->wakeup(found->ev_pending, found); + } + return 0; + } else { + dbg("SOCKET> mmm something wrong (prob sockport)\n"); + return -1; + } + } /* TCP CASE */ +#endif + +#ifdef PICO_SUPPORT_UDP + if (p->proto_number == PICO_PROTO_UDP) { + pico_tree_foreach(index, &sp->socks) { + s = index->keyValue; + if (IS_IPV4(f)) { /* IPV4 */ + struct pico_ip4 s_local, p_dst; + ip4hdr = (struct pico_ipv4_hdr*)(f->net_hdr); + s_local.addr = s->local_addr.ip4.addr; + p_dst.addr = ip4hdr->dst.addr; + if ((pico_ipv4_is_broadcast(p_dst.addr)) || !pico_ipv4_is_unicast(p_dst.addr)) { + struct pico_device *dev = pico_ipv4_link_find(&s->local_addr.ip4); + if ((s_local.addr == PICO_IPV4_INADDR_ANY) || /* If our local ip is ANY, or.. */ + (dev == f->dev) ) { /* the source of the bcast packet is a neighbor... */ + cpy = pico_frame_copy(f); + if (!cpy) + return -1; + if (pico_enqueue(&s->q_in, cpy) > 0) { + if (s->wakeup) + s->wakeup(PICO_SOCK_EV_RD, s); + } + } + } else if ((s_local.addr == PICO_IPV4_INADDR_ANY) || (s_local.addr == p_dst.addr)) + { /* Either local socket is ANY, or matches dst */ + cpy = pico_frame_copy(f); + if (!cpy) + return -1; + if (pico_enqueue(&s->q_in, cpy) > 0) { + if (s->wakeup) + s->wakeup(PICO_SOCK_EV_RD, s); + } + } + } else { + /*... IPv6 */ + } + } /* FOREACH */ + pico_frame_discard(f); + if (s) + return 0; + else + return -1; + } +#endif + return -1; +} + +struct pico_socket *pico_socket_open(uint16_t net, uint16_t proto, void (*wakeup)(uint16_t ev, struct pico_socket *)) +{ + + struct pico_socket *s = NULL; + +#ifdef PICO_SUPPORT_UDP + if (proto == PICO_PROTO_UDP) { + s = pico_udp_open(); + s->proto = &pico_proto_udp; + } +#endif + +#ifdef PICO_SUPPORT_TCP + if (proto == PICO_PROTO_TCP) { + s = pico_tcp_open(); + s->proto = &pico_proto_tcp; + /*check if Nagle enabled */ + if (!IS_NAGLE_ENABLED(s)) + dbg("ERROR Nagle should be enabled here\n\n"); + } +#endif + + if (!s) { + pico_err = PICO_ERR_EPROTONOSUPPORT; + return NULL; + } + +#ifdef PICO_SUPPORT_IPV4 + if (net == PICO_PROTO_IPV4) + s->net = &pico_proto_ipv4; +#endif + +#ifdef PICO_SUPPORT_IPV6 + if (net == PICO_PROTO_IPV6) + s->net = &pico_proto_ipv6; +#endif + + s->q_in.max_size = PICO_DEFAULT_SOCKETQ; + s->q_out.max_size = PICO_DEFAULT_SOCKETQ; + s->wakeup = wakeup; + + if (!s->net) { + pico_free(s); + pico_err = PICO_ERR_ENETUNREACH; + return NULL; + } + return s; +} + + +struct pico_socket *pico_socket_clone(struct pico_socket *facsimile) +{ + struct pico_socket *s = NULL; + +#ifdef PICO_SUPPORT_UDP + if (facsimile->proto->proto_number == PICO_PROTO_UDP) { + s = pico_udp_open(); + s->proto = &pico_proto_udp; + } +#endif + +#ifdef PICO_SUPPORT_TCP + if (facsimile->proto->proto_number == PICO_PROTO_TCP) { + s = pico_tcp_open(); + s->proto = &pico_proto_tcp; + } +#endif + + if (!s) { + pico_err = PICO_ERR_EPROTONOSUPPORT; + return NULL; + } + s->local_port = facsimile->local_port; + s->remote_port = facsimile->remote_port; + s->state = facsimile->state; + +#ifdef PICO_SUPPORT_IPV4 + if (facsimile->net == &pico_proto_ipv4) { + s->net = &pico_proto_ipv4; + memcpy(&s->local_addr, &facsimile->local_addr, sizeof(struct pico_ip4)); + memcpy(&s->remote_addr, &facsimile->remote_addr, sizeof(struct pico_ip4)); + } +#endif + +#ifdef PICO_SUPPORT_IPV6 + if (net == &pico_proto_ipv6) { + s->net = &pico_proto_ipv6; + memcpy(&s->local_addr, &facsimile->local_addr, sizeof(struct pico_ip6)); + memcpy(&s->remote_addr, &facsimile->remote_addr, sizeof(struct pico_ip6)); + } +#endif + s->q_in.max_size = PICO_DEFAULT_SOCKETQ; + s->q_out.max_size = PICO_DEFAULT_SOCKETQ; + s->wakeup = NULL; + if (!s->net) { + pico_free(s); + pico_err = PICO_ERR_ENETUNREACH; + return NULL; + } + return s; +} + +int pico_socket_read(struct pico_socket *s, void *buf, int len) +{ + if (!s || buf == NULL) { + pico_err = PICO_ERR_EINVAL; + return -1; + } else { + /* check if exists in tree */ + /* See task #178 */ + if (pico_check_socket(s) != 0) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + } + + if ((s->state & PICO_SOCKET_STATE_BOUND) == 0) { + pico_err = PICO_ERR_EIO; + return -1; + } +#ifdef PICO_SUPPORT_UDP + if (PROTO(s) == PICO_PROTO_UDP) + return pico_udp_recv(s, buf, len, NULL, NULL); +#endif + +#ifdef PICO_SUPPORT_TCP + if (PROTO(s) == PICO_PROTO_TCP){ + /* check if in shutdown state and if no more data in tcpq_in */ + if ((s->state & PICO_SOCKET_STATE_SHUT_REMOTE) && pico_tcp_queue_in_is_empty(s) ) { + pico_err = PICO_ERR_ESHUTDOWN; + return -1; + } else { + return pico_tcp_read(s, buf, len); + } + } +#endif + return 0; +} + +int pico_socket_write(struct pico_socket *s, void *buf, int len) +{ + if (!s || buf == NULL) { + pico_err = PICO_ERR_EINVAL; + return -1; + } else { + /* check if exists in tree */ + /* See task #178 */ + if (pico_check_socket(s) != 0) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + } + + if ((s->state & PICO_SOCKET_STATE_BOUND) == 0) { + pico_err = PICO_ERR_EIO; + return -1; + } + if ((s->state & PICO_SOCKET_STATE_CONNECTED) == 0) { + pico_err = PICO_ERR_ENOTCONN; + return -1; + } else if (s->state & PICO_SOCKET_STATE_SHUT_LOCAL) { /* check if in shutdown state */ + pico_err = PICO_ERR_ESHUTDOWN; + return -1; + } else { + return pico_socket_sendto(s, buf, len, &s->remote_addr, s->remote_port); + } +} + +uint16_t pico_socket_high_port(uint16_t proto) +{ + uint16_t port; + if (0 || +#ifdef PICO_SUPPORT_TCP + (proto == PICO_PROTO_TCP) || +#endif +#ifdef PICO_SUPPORT_TCP + (proto == PICO_PROTO_UDP) || +#endif + 0) { + do { + uint32_t rand = pico_rand(); + port = (uint16_t) (rand & 0xFFFFU); + port = (uint16_t)(port % (65535 - 1024)) + 1024U; + if (pico_is_port_free(proto, port, NULL, NULL)) { + return short_be(port); + } + } while(1); + } + else return 0U; +} + + +int pico_socket_sendto(struct pico_socket *s, void *buf, int len, void *dst, uint16_t remote_port) +{ + struct pico_frame *f; + struct pico_remote_duple *remote_duple = NULL; + int header_offset = 0; + int total_payload_written = 0; +#ifdef PICO_SUPPORT_IPV4 + struct pico_ip4 *src4; +#endif + +#ifdef PICO_SUPPORT_IPV6 + struct pico_ip6 *src6; +#endif + if (len == 0) { + return 0; + } else if (len < 0) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + if (buf == NULL || s == NULL) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + if (!dst || !remote_port) { + pico_err = PICO_ERR_EADDRNOTAVAIL; + return -1; + } + + if ((s->state & PICO_SOCKET_STATE_CONNECTED) != 0) { + if (remote_port != s->remote_port) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + } + +#ifdef PICO_SUPPORT_IPV4 + if (IS_SOCK_IPV4(s)) { + if ((s->state & PICO_SOCKET_STATE_CONNECTED)) { + if (s->remote_addr.ip4.addr != ((struct pico_ip4 *)dst)->addr ) { + pico_err = PICO_ERR_EADDRNOTAVAIL; + return -1; + } + } else { + src4 = pico_ipv4_source_find(dst); + if (!src4) { + pico_err = PICO_ERR_EHOSTUNREACH; + return -1; + } + if (src4->addr != PICO_IPV4_INADDR_ANY) + s->local_addr.ip4.addr = src4->addr; +# ifdef PICO_SUPPORT_UDP + /* socket remote info could change in a consecutive call, make persistent */ + if (PROTO(s) == PICO_PROTO_UDP) { + remote_duple = pico_zalloc(sizeof(struct pico_remote_duple)); + remote_duple->remote_addr.ip4.addr = ((struct pico_ip4 *)dst)->addr; + remote_duple->remote_port = remote_port; + } +# endif + } + } +#endif + +#ifdef PICO_SUPPORT_IPV6 + if (IS_SOCK_IPV6(s)) { + if (s->state & PICO_SOCKET_STATE_CONNECTED) { + if (memcmp(&s->remote_addr, dst, PICO_SIZE_IP6)) + return -1; + } else { + src6 = pico_ipv6_source_find(dst); + if (!src6) { + pico_err = PICO_ERR_EHOSTUNREACH; + return -1; + } + memcpy(&s->local_addr, src6, PICO_SIZE_IP6); + memcpy(&s->remote_addr, dst, PICO_SIZE_IP6); +# ifdef PICO_SUPPORT_UDP + if (PROTO(s) == PICO_PROTO_UDP) { + remote_duple = pico_zalloc(sizeof(struct pico_remote_duple)); + remote_duple->remote_addr.ip6.addr = ((struct pico_ip6 *)dst)->addr; + remote_duple->remote_port = remote_port; + } +# endif + } + } +#endif + + if ((s->state & PICO_SOCKET_STATE_BOUND) == 0) { + s->local_port = pico_socket_high_port(s->proto->proto_number); + if (s->local_port == 0) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + } + if ((s->state & PICO_SOCKET_STATE_CONNECTED) == 0) { + s->remote_port = remote_port; + } + +#ifdef PICO_SUPPORT_TCP + if (PROTO(s) == PICO_PROTO_TCP) + header_offset = pico_tcp_overhead(s); +#endif + +#ifdef PICO_SUPPORT_UDP + if (PROTO(s) == PICO_PROTO_UDP) + header_offset = sizeof(struct pico_udp_hdr); +#endif + + while (total_payload_written < len) { + int transport_len = (len - total_payload_written) + header_offset; + if (transport_len > PICO_SOCKET_MTU) + transport_len = PICO_SOCKET_MTU; +#ifdef PICO_SUPPORT_IPFRAG + else { + if (total_payload_written) + transport_len -= header_offset; /* last fragment, do not allocate memory for transport header */ + } +#endif /* PICO_SUPPORT_IPFRAG */ + + f = pico_socket_frame_alloc(s, transport_len); + if (!f) { + pico_err = PICO_ERR_ENOMEM; + return -1; + } + f->payload += header_offset; + f->payload_len -= header_offset; + f->sock = s; + if (remote_duple) { + f->info = pico_zalloc(sizeof(struct pico_remote_duple)); + memcpy(f->info, remote_duple, sizeof(struct pico_remote_duple)); + } + +#ifdef PICO_SUPPORT_IPFRAG +# ifdef PICO_SUPPORT_UDP + if (PROTO(s) == PICO_PROTO_UDP && ((len + header_offset) > PICO_SOCKET_MTU)) { + /* hacking way to identify fragmentation frames: payload != transport_hdr -> first frame */ + if (!total_payload_written) { + frag_dbg("FRAG: first fragmented frame %p | len = %u offset = 0\n", f, f->payload_len); + /* transport header length field contains total length + header length */ + f->transport_len = len + header_offset; + f->frag = short_be(PICO_IPV4_MOREFRAG); + } else { + /* no transport header in fragmented IP */ + f->payload = f->transport_hdr; + f->payload_len += header_offset; + /* set offset in octets */ + f->frag = short_be((total_payload_written + header_offset) / 8); + if (total_payload_written + f->payload_len < len) { + frag_dbg("FRAG: intermediate fragmented frame %p | len = %u offset = %u\n", f, f->payload_len, short_be(f->frag)); + f->frag |= short_be(PICO_IPV4_MOREFRAG); + } else { + frag_dbg("FRAG: last fragmented frame %p | len = %u offset = %u\n", f, f->payload_len, short_be(f->frag)); + f->frag &= short_be(PICO_IPV4_FRAG_MASK); + } + } + } else { + f->frag = short_be(PICO_IPV4_DONTFRAG); + } +# endif /* PICO_SUPPORT_UDP */ +#endif /* PICO_SUPPORT_IPFRAG */ + + if (f->payload_len <= 0) { + pico_frame_discard(f); + pico_free(remote_duple); + return total_payload_written; + } + + memcpy(f->payload, buf + total_payload_written, f->payload_len); + //dbg("Pushing segment, hdr len: %d, payload_len: %d\n", header_offset, f->payload_len); + + if (s->proto->push(s->proto, f) > 0) { + total_payload_written += f->payload_len; + } else { + pico_frame_discard(f); + pico_err = PICO_ERR_EAGAIN; + break; + } + } + pico_free(remote_duple); + return total_payload_written; +} + +int pico_socket_send(struct pico_socket *s, void *buf, int len) +{ + if (!s || buf == NULL) { + pico_err = PICO_ERR_EINVAL; + return -1; + } else { + /* check if exists in tree */ + /* See task #178 */ + if (pico_check_socket(s) != 0) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + } + + if ((s->state & PICO_SOCKET_STATE_CONNECTED) == 0) { + pico_err = PICO_ERR_ENOTCONN; + return -1; + } + return pico_socket_sendto(s, buf, len, &s->remote_addr, s->remote_port); +} + +int pico_socket_recvfrom(struct pico_socket *s, void *buf, int len, void *orig, uint16_t *remote_port) +{ + if (!s || buf == NULL) { /// || orig == NULL || remote_port == NULL) { + pico_err = PICO_ERR_EINVAL; + return -1; + } else { + /* check if exists in tree */ + if (pico_check_socket(s) != 0) { + pico_err = PICO_ERR_EINVAL; + /* See task #178 */ + return -1; + } + } + + if ((s->state & PICO_SOCKET_STATE_BOUND) == 0) { + pico_err = PICO_ERR_EADDRNOTAVAIL; + return -1; + } +#ifdef PICO_SUPPORT_UDP + if (PROTO(s) == PICO_PROTO_UDP) { + return pico_udp_recv(s, buf, len, orig, remote_port); + } +#endif +#ifdef PICO_SUPPORT_TCP + if (PROTO(s) == PICO_PROTO_TCP) { + /* check if in shutdown state and if tcpq_in empty */ + if ((s->state & PICO_SOCKET_STATE_SHUT_REMOTE) && pico_tcp_queue_in_is_empty(s)) { + pico_err = PICO_ERR_ESHUTDOWN; + return -1; + } else { + //dbg("socket tcp recv\n"); + return pico_tcp_read(s, buf, len); + } + } +#endif + //dbg("socket return 0\n"); + return 0; +} + +int pico_socket_recv(struct pico_socket *s, void *buf, int len) +{ + return pico_socket_recvfrom(s, buf, len, NULL, NULL); +} + + +int pico_socket_bind(struct pico_socket *s, void *local_addr, uint16_t *port) +{ + if (!s || !local_addr || !port) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + if (!is_sock_ipv6(s)) { + struct pico_ip4 *ip = (struct pico_ip4 *)local_addr; + if (ip->addr != PICO_IPV4_INADDR_ANY) { + if (!pico_ipv4_link_find(local_addr)) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + } + } else { + /*... IPv6 */ + } + + + /* When given port = 0, get a random high port to bind to. */ + if (*port == 0) { + *port = pico_socket_high_port(PROTO(s)); + if (*port == 0) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + } + + if (pico_is_port_free(PROTO(s), *port, local_addr, s->net) == 0) { + pico_err = PICO_ERR_EADDRINUSE; + return -1; + } + s->local_port = *port; + + if (is_sock_ipv6(s)) { + struct pico_ip6 *ip = (struct pico_ip6 *) local_addr; + memcpy(s->local_addr.ip6.addr, ip, PICO_SIZE_IP6); + /* XXX: port ipv4 functionality to ipv6 */ + /* Check for port already in use */ + if (pico_is_port_free(PROTO(s), *port, &local_addr, s->net)) { + pico_err = PICO_ERR_EADDRINUSE; + return -1; + } + } else if (is_sock_ipv4(s)) { + struct pico_ip4 *ip = (struct pico_ip4 *) local_addr; + s->local_addr.ip4.addr = ip->addr; + } + return pico_socket_alter_state(s, PICO_SOCKET_STATE_BOUND, 0, 0); +} + +int pico_socket_connect(struct pico_socket *s, void *remote_addr, uint16_t remote_port) +{ + int ret = -1; + pico_err = PICO_ERR_EPROTONOSUPPORT; + if (!s || remote_addr == NULL || remote_port == 0) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + s->remote_port = remote_port; + + if (s->local_port == 0) { + s->local_port = pico_socket_high_port(PROTO(s)); + if (!s->local_port) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + } + + if (is_sock_ipv6(s)) { + struct pico_ip6 *ip = (struct pico_ip6 *) remote_addr; + memcpy(s->remote_addr.ip6.addr, ip, PICO_SIZE_IP6); + } else if (is_sock_ipv4(s)) { + struct pico_ip4 *local, *ip = (struct pico_ip4 *) remote_addr; + s->remote_addr.ip4.addr = ip->addr; + local = pico_ipv4_source_find(ip); + if (local) { + s->local_addr.ip4.addr = local->addr; + } else { + pico_err = PICO_ERR_EHOSTUNREACH; + return -1; + } + } + + pico_socket_alter_state(s, PICO_SOCKET_STATE_BOUND, 0, 0); + +#ifdef PICO_SUPPORT_UDP + if (PROTO(s) == PICO_PROTO_UDP) { + pico_socket_alter_state(s, PICO_SOCKET_STATE_CONNECTED, 0, 0); + pico_err = PICO_ERR_NOERR; + ret = 0; + } +#endif + +#ifdef PICO_SUPPORT_TCP + if (PROTO(s) == PICO_PROTO_TCP) { + if (pico_tcp_initconn(s) == 0) { + pico_socket_alter_state(s, PICO_SOCKET_STATE_CONNECTED | PICO_SOCKET_STATE_TCP_SYN_SENT, 0, 0); + pico_err = PICO_ERR_NOERR; + ret = 0; + } else { + pico_err = PICO_ERR_EHOSTUNREACH; + } + } +#endif + + return ret; +} + +#ifdef PICO_SUPPORT_TCP + +int pico_socket_listen(struct pico_socket *s, int backlog) +{ + if (!s || backlog < 1) { + pico_err = PICO_ERR_EINVAL; + return -1; + } else { + /* check if exists in tree */ + /* See task #178 */ + if (pico_check_socket(s) != 0) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + } + + if (PROTO(s) == PICO_PROTO_UDP) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + if ((s->state & PICO_SOCKET_STATE_BOUND) == 0) { + pico_err = PICO_ERR_EISCONN; + return -1; + } + + if (backlog < 1) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + if (PROTO(s) == PICO_PROTO_TCP) + pico_socket_alter_state(s, PICO_SOCKET_STATE_TCP_SYN_SENT, 0, PICO_SOCKET_STATE_TCP_LISTEN); + s->max_backlog = backlog; + + return 0; +} + +struct pico_socket *pico_socket_accept(struct pico_socket *s, void *orig, uint16_t *port) +{ + if (!s || !orig || !port) { + pico_err = PICO_ERR_EINVAL; + return NULL; + } + + pico_err = PICO_ERR_EINVAL; + + if ((s->state & PICO_SOCKET_STATE_BOUND) == 0) { + return NULL; + } + + if (PROTO(s) == PICO_PROTO_UDP) { + return NULL; + } + + if (TCPSTATE(s) == PICO_SOCKET_STATE_TCP_LISTEN) { + struct pico_sockport *sp = pico_get_sockport(PICO_PROTO_TCP, s->local_port); + struct pico_socket *found; + /* If at this point no incoming connection socket is found, + * the accept call is valid, but no connection is established yet. + */ + pico_err = PICO_ERR_EAGAIN; + if (sp) { + struct pico_tree_node * index; + //RB_FOREACH(found, socket_tree, &sp->socks) { + pico_tree_foreach(index,&sp->socks){ + found = index->keyValue; + if (s == found->parent) { + found->parent = NULL; + pico_err = PICO_ERR_NOERR; + memcpy(orig, &found->remote_addr, sizeof(struct pico_ip4)); + *port = found->remote_port; + return found; + } + } + } + } + return NULL; +} + +#else + +int pico_socket_listen(struct pico_socket *s, int backlog) +{ + pico_err = PICO_ERR_EINVAL; + return -1; +} + +struct pico_socket *pico_socket_accept(struct pico_socket *s, void *orig, uint16_t *local_port) +{ + pico_err = PICO_ERR_EINVAL; + return NULL; +} + +#endif + +#define PICO_SOCKET_SETOPT_EN(socket,index) (socket->opt_flags |= (1 << index)) +#define PICO_SOCKET_SETOPT_DIS(socket,index) (socket->opt_flags &= ~(1 << index)) + +int pico_socket_setoption(struct pico_socket *s, int option, void *value) // XXX no check against proto (vs setsockopt) or implicit by socket? +{ + if (s == NULL) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + pico_err = PICO_ERR_NOERR; + + switch (option) + { +#ifdef PICO_SUPPORT_TCP + case PICO_TCP_NODELAY: + if (s->proto->proto_number == PICO_PROTO_TCP) { + /* disable Nagle's algorithm */ + PICO_SOCKET_SETOPT_DIS(s,PICO_SOCKET_OPT_TCPNODELAY); + } else { + pico_err = PICO_ERR_EINVAL; + } + break; +#endif + + +#ifdef PICO_SUPPORT_MCAST + case PICO_IP_MULTICAST_IF: + pico_err = PICO_ERR_EOPNOTSUPP; + return -1; + break; + + case PICO_IP_MULTICAST_TTL: + if (s->proto->proto_number == PICO_PROTO_UDP) { + return pico_udp_set_mc_ttl(s, *((uint8_t *) value)); + } + break; + + case PICO_IP_MULTICAST_LOOP: + if (s->proto->proto_number == PICO_PROTO_UDP) { + switch (*(uint8_t *) value) + { + case 0: + /* do not loop back multicast datagram */ + PICO_SOCKET_SETOPT_DIS(s,PICO_SOCKET_OPT_MULTICAST_LOOP); + break; + + case 1: + /* do loop back multicast datagram */ + PICO_SOCKET_SETOPT_EN(s,PICO_SOCKET_OPT_MULTICAST_LOOP); + break; + + default: + pico_err = PICO_ERR_EINVAL; + return -1; + } + } + break; + + case PICO_IP_ADD_MEMBERSHIP: + if (s->proto->proto_number == PICO_PROTO_UDP) { + struct pico_ip_mreq *mreq = (struct pico_ip_mreq *) value; + struct pico_ipv4_link *mcast_link; + if (!mreq->mcast_link_addr.addr) { + mcast_link = NULL; /* use default multicast link */ + } else { + mcast_link = pico_ipv4_link_get(&mreq->mcast_link_addr); + if (!mcast_link) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + } + return pico_ipv4_mcast_join_group(&mreq->mcast_group_addr, mcast_link); + } + break; + + case PICO_IP_DROP_MEMBERSHIP: + if (s->proto->proto_number == PICO_PROTO_UDP) { + struct pico_ip_mreq *mreq = (struct pico_ip_mreq *) value; + struct pico_ipv4_link *mcast_link; + if (!mreq->mcast_link_addr.addr) { + mcast_link = NULL; /* use default multicast link */ + } else { + mcast_link = pico_ipv4_link_get(&mreq->mcast_link_addr); + if (!mcast_link) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + } + return pico_ipv4_mcast_leave_group(&mreq->mcast_group_addr, mcast_link); + } + break; +#endif /* PICO_SUPPORT_MCAST */ + + default: + pico_err = PICO_ERR_EINVAL; + return -1; + } + + if (pico_err != PICO_ERR_NOERR) + return -1; + else + return 0; +} + +#define PICO_SOCKET_GETOPT(socket,index) ((socket->opt_flags & (1 << index)) != 0) + +int pico_socket_getoption(struct pico_socket *s, int option, void *value) +{ + if (!s || !value) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + switch (option) + { +#ifdef PICO_SUPPORT_TCP + case PICO_TCP_NODELAY: + if (s->proto->proto_number == PICO_PROTO_TCP) + /* state Nagle's algorithm */ + *(int *)value = PICO_SOCKET_GETOPT(s,PICO_SOCKET_OPT_TCPNODELAY); + else + *(int *)value = 0; + break; +#endif + +#ifdef PICO_SUPPORT_MCAST + case PICO_IP_MULTICAST_IF: + pico_err = PICO_ERR_EOPNOTSUPP; + return -1; + break; + + case PICO_IP_MULTICAST_TTL: + if (s->proto->proto_number == PICO_PROTO_UDP) { + pico_udp_get_mc_ttl(s, (uint8_t *) value); + } else { + *(uint8_t *)value = 0; + pico_err = PICO_ERR_EINVAL; + return -1; + } + break; + + case PICO_IP_MULTICAST_LOOP: + if (s->proto->proto_number == PICO_PROTO_UDP) { + *(uint8_t *)value = PICO_SOCKET_GETOPT(s,PICO_SOCKET_OPT_MULTICAST_LOOP); + } else { + *(uint8_t *)value = 0; + pico_err = PICO_ERR_EINVAL; + return -1; + } + break; +#endif /* PICO_SUPPORT_MCAST */ + + default: + pico_err = PICO_ERR_EINVAL; + return -1; + } + + return 0; +} + + +int pico_socket_shutdown(struct pico_socket *s, int mode) +{ + if (!s) { + pico_err = PICO_ERR_EINVAL; + return -1; + } else { + /* check if exists in tree */ + /* See task #178 */ + if (pico_check_socket(s) != 0) { + pico_free(s); /* close socket after bind or connect failed */ + return 0; + } + } + +#ifdef PICO_SUPPORT_UDP + if (PROTO(s) == PICO_PROTO_UDP) { + if (mode & PICO_SHUT_RDWR) + pico_socket_alter_state(s, PICO_SOCKET_STATE_CLOSED, PICO_SOCKET_STATE_CLOSING |PICO_SOCKET_STATE_BOUND | PICO_SOCKET_STATE_CONNECTED, 0); + else if (mode & PICO_SHUT_RD) + pico_socket_alter_state(s, PICO_SOCKET_STATE_BOUND, 0, 0); + } +#endif +#ifdef PICO_SUPPORT_TCP + if (PROTO(s) == PICO_PROTO_TCP) { + if (mode & PICO_SHUT_WR) + pico_socket_alter_state(s, PICO_SOCKET_STATE_SHUT_LOCAL, 0, 0); + else if (mode & PICO_SHUT_RD) + pico_socket_alter_state(s, PICO_SOCKET_STATE_SHUT_REMOTE, 0, 0); + else if (mode & PICO_SHUT_RDWR) + pico_socket_alter_state(s, PICO_SOCKET_STATE_SHUT_LOCAL | PICO_SOCKET_STATE_SHUT_REMOTE, 0, 0); + } +#endif + return 0; +} + +int pico_socket_close(struct pico_socket *s) +{ + return pico_socket_shutdown(s, PICO_SHUT_RDWR); +} + +#ifdef PICO_SUPPORT_CRC +static inline int pico_transport_crc_check(struct pico_frame *f) +{ + struct pico_ipv4_hdr *net_hdr = (struct pico_ipv4_hdr *) f->net_hdr; + struct pico_udp_hdr *udp_hdr = NULL; + uint16_t checksum_invalid = 1; + + switch (net_hdr->proto) + { + case PICO_PROTO_TCP: + checksum_invalid = short_be(pico_tcp_checksum_ipv4(f)); + //dbg("TCP CRC validation == %u\n", checksum_invalid); + if (checksum_invalid) { + //dbg("TCP CRC: validation failed!\n"); + pico_frame_discard(f); + return 0; + } + break; + + case PICO_PROTO_UDP: + udp_hdr = (struct pico_udp_hdr *) f->transport_hdr; + if (short_be(udp_hdr->crc)) { + checksum_invalid = short_be(pico_udp_checksum_ipv4(f)); + //dbg("UDP CRC validation == %u\n", checksum_invalid); + if (checksum_invalid) { + //dbg("UDP CRC: validation failed!\n"); + pico_frame_discard(f); + return 0; + } + } + break; + + default: + // Do nothing + break; + } + return 1; +} +#else +static inline int pico_transport_crc_check(struct pico_frame *f) +{ + return 1; +} +#endif /* PICO_SUPPORT_CRC */ + +int pico_transport_process_in(struct pico_protocol *self, struct pico_frame *f) +{ + struct pico_trans *hdr = (struct pico_trans *) f->transport_hdr; + int ret = 0; + + if (!hdr) { + pico_err = PICO_ERR_EFAULT; + return -1; + } + + ret = pico_transport_crc_check(f); + if (ret < 1) + return ret; + else + ret = 0; + + if ((hdr) && (pico_socket_deliver(self, f, hdr->dport) == 0)) + return ret; + + if (!IS_BCAST(f)) { + dbg("Socket not found... \n"); + pico_notify_socket_unreachable(f); +#ifdef PICO_SUPPORT_TCP + /* if tcp protocol send RST segment */ + //if (self->proto_number == PICO_PROTO_TCP) + // pico_tcp_reply_rst(f); +#endif + ret = -1; + pico_err = PICO_ERR_ENOENT; + } + pico_frame_discard(f); + return ret; +} + +#define SL_LOOP_MIN 1 + + +int pico_sockets_loop(int loop_score) +{ + static struct pico_tree_node *index_udp, * index_tcp; + + struct pico_sockport *start; + struct pico_socket *s; + +#ifdef PICO_SUPPORT_UDP + struct pico_frame *f; + + if (sp_udp == NULL) + { + index_udp = pico_tree_firstNode(UDPTable.root); + sp_udp = index_udp->keyValue; + } + + /* init start node */ + start = sp_udp; + + /* round-robin all transport protocols, break if traversed all protocols */ + while (loop_score > SL_LOOP_MIN && sp_udp != NULL) { + struct pico_tree_node * index; + + pico_tree_foreach(index,&sp_udp->socks){ + s = index->keyValue; + f = pico_dequeue(&s->q_out); + while (f && (loop_score > 0)) { + pico_proto_udp.push(&pico_proto_udp, f); + loop_score -= 1; + f = pico_dequeue(&s->q_out); + } + } + + index_udp = pico_tree_next(index_udp); + sp_udp = index_udp->keyValue; + + if (sp_udp == NULL) + { + index_udp = pico_tree_firstNode(UDPTable.root); + sp_udp = index_udp->keyValue; + } + if (sp_udp == start) + break; + } +#endif + +#ifdef PICO_SUPPORT_TCP + if (sp_tcp == NULL) + { + index_tcp = pico_tree_firstNode(TCPTable.root); + sp_tcp = index_tcp->keyValue; + } + + /* init start node */ + start = sp_tcp; + + while (loop_score > SL_LOOP_MIN && sp_tcp != NULL) { + struct pico_tree_node * index; + pico_tree_foreach(index, &sp_tcp->socks){ + s = index->keyValue; + loop_score = pico_tcp_output(s, loop_score); + if ((s->ev_pending) && s->wakeup) { + s->wakeup(s->ev_pending, s); + } + if (loop_score <= 0) { + loop_score = 0; + break; + } + } + + /* check if RB_FOREACH ended, if not, break to keep the cur sp_tcp */ + if (s != NULL) + break; + + index_tcp = pico_tree_next(index_tcp); + sp_tcp = index_tcp->keyValue; + + if (sp_tcp == NULL) + { + index_tcp = pico_tree_firstNode(TCPTable.root); + sp_tcp = index_tcp->keyValue; + } + if (sp_tcp == start) + break; + } +#endif + + return loop_score; +} + + +struct pico_frame *pico_socket_frame_alloc(struct pico_socket *s, int len) +{ + struct pico_frame *f = NULL; + +#ifdef PICO_SUPPORT_IPV6 + if (IS_SOCK_IPV6(s)) + f = pico_proto_ipv6.alloc(&pico_proto_ipv6, len); +#endif + +#ifdef PICO_SUPPORT_IPV4 + if (IS_SOCK_IPV4(s)) + f = pico_proto_ipv4.alloc(&pico_proto_ipv4, len); +#endif + if (!f) { + pico_err = PICO_ERR_ENOMEM; + return f; + } + f->payload = f->transport_hdr; + f->payload_len = len; + f->sock = s; + return f; +} + +int pico_transport_error(struct pico_frame *f, uint8_t proto, int code) +{ + int ret = -1; + struct pico_trans *trans = (struct pico_trans*) f->transport_hdr; + struct pico_sockport *port = NULL; + struct pico_socket *s = NULL; + switch (proto) { + + +#ifdef PICO_SUPPORT_UDP + case PICO_PROTO_UDP: + port = pico_get_sockport(proto, trans->sport); + break; +#endif + +#ifdef PICO_SUPPORT_TCP + case PICO_PROTO_TCP: + port = pico_get_sockport(proto, trans->sport); + break; +#endif + + default: + /* Protocol not available */ + ret = -1; + } + if (port) { + struct pico_tree_node * index; + ret = 0; + + pico_tree_foreach(index,&port->socks) { + s = index->keyValue; + if (trans->dport == s->remote_port) { + if (s->wakeup) { + //dbg("SOCKET ERROR FROM ICMP NOTIFICATION. (icmp code= %d)\n\n", code); + switch(code) { + case PICO_ICMP_UNREACH_PROTOCOL: + pico_err = PICO_ERR_EPROTO; + break; + + case PICO_ICMP_UNREACH_PORT: + pico_err = PICO_ERR_ECONNREFUSED; + break; + + case PICO_ICMP_UNREACH_NET: + case PICO_ICMP_UNREACH_NET_PROHIB: + case PICO_ICMP_UNREACH_NET_UNKNOWN: + pico_err = PICO_ERR_ENETUNREACH; + break; + + default: + pico_err = PICO_ERR_EHOSTUNREACH; + } + s->wakeup(PICO_SOCK_EV_ERR, s); + } + break; + } + } + } + pico_frame_discard(f); + return ret; +} +#endif +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/stack/pico_stack.c Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,695 @@ +/********************************************************************* +PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. +See LICENSE and COPYING for usage. + +. + +Authors: Daniele Lacamera +*********************************************************************/ + + +#include "pico_config.h" +#include "pico_frame.h" +#include "pico_device.h" +#include "pico_protocol.h" +#include "pico_stack.h" +#include "pico_addressing.h" +#include "pico_dns_client.h" + +#include "pico_eth.h" +#include "pico_arp.h" +#include "pico_ipv4.h" +#include "pico_ipv6.h" +#include "pico_icmp4.h" +#include "pico_igmp2.h" +#include "pico_udp.h" +#include "pico_tcp.h" +#include "pico_socket.h" +#include "heap.h" + +#define IS_LIMITED_BCAST(f) ( ((struct pico_ipv4_hdr *) f->net_hdr)->dst.addr == PICO_IP4_BCAST ) + +#ifdef PICO_SUPPORT_MCAST +# define PICO_SIZE_MCAST 3 + const uint8_t PICO_ETHADDR_MCAST[6] = {0x01, 0x00, 0x5e, 0x00, 0x00, 0x00}; +#endif + +volatile unsigned long pico_tick; +volatile pico_err_t pico_err; + +static uint32_t _rand_seed; + +void pico_rand_feed(uint32_t feed) +{ + if (!feed) + return; + _rand_seed *= 1664525; + _rand_seed += 1013904223; + _rand_seed ^= ~(feed); +} + +uint32_t pico_rand(void) +{ + pico_rand_feed(pico_tick); + return _rand_seed; +} + +/* NOTIFICATIONS: distributed notifications for stack internal errors. + */ + +int pico_notify_socket_unreachable(struct pico_frame *f) +{ + if (0) {} +#ifdef PICO_SUPPORT_ICMP4 + else if (IS_IPV4(f)) { + pico_icmp4_port_unreachable(f); + } +#endif +#ifdef PICO_SUPPORT_ICMP6 + else if (IS_IPV6(f)) { + pico_icmp6_port_unreachable(f); + } +#endif + + return 0; +} + +int pico_notify_proto_unreachable(struct pico_frame *f) +{ + if (0) {} +#ifdef PICO_SUPPORT_ICMP4 + else if (IS_IPV4(f)) { + pico_icmp4_proto_unreachable(f); + } +#endif +#ifdef PICO_SUPPORT_ICMP6 + else if (IS_IPV6(f)) { + pico_icmp6_proto_unreachable(f); + } +#endif + return 0; +} + +int pico_notify_dest_unreachable(struct pico_frame *f) +{ + if (0) {} +#ifdef PICO_SUPPORT_ICMP4 + else if (IS_IPV4(f)) { + pico_icmp4_dest_unreachable(f); + } +#endif +#ifdef PICO_SUPPORT_ICMP6 + else if (IS_IPV6(f)) { + pico_icmp6_dest_unreachable(f); + } +#endif + return 0; +} + +int pico_notify_ttl_expired(struct pico_frame *f) +{ + if (0) {} +#ifdef PICO_SUPPORT_ICMP4 + else if (IS_IPV4(f)) { + pico_icmp4_ttl_expired(f); + } +#endif +#ifdef PICO_SUPPORT_ICMP6 + else if (IS_IPV6(f)) { + pico_icmp6_ttl_expired(f); + } +#endif + return 0; +} + + +/* Transport layer */ +int pico_transport_receive(struct pico_frame *f, uint8_t proto) +{ + int ret = -1; + switch (proto) { + +#ifdef PICO_SUPPORT_ICMP4 + case PICO_PROTO_ICMP4: + ret = pico_enqueue(pico_proto_icmp4.q_in, f); + break; +#endif + +#ifdef PICO_SUPPORT_IGMP2 + case PICO_PROTO_IGMP2: + ret = pico_enqueue(pico_proto_igmp2.q_in, f); + break; +#endif + +#ifdef PICO_SUPPORT_UDP + case PICO_PROTO_UDP: + ret = pico_enqueue(pico_proto_udp.q_in, f); + break; +#endif + +#ifdef PICO_SUPPORT_TCP + case PICO_PROTO_TCP: + ret = pico_enqueue(pico_proto_tcp.q_in, f); + break; +#endif + + default: + /* Protocol not available */ + dbg("pkt: no such protocol (%d)\n", proto); + pico_notify_proto_unreachable(f); + pico_frame_discard(f); + ret = -1; + } + return ret; +} + +int pico_transport_send(struct pico_frame *f) +{ + if (!f || !f->sock || !f->sock->proto) { + pico_frame_discard(f); + return -1; + } + return f->sock->proto->push(f->sock->net, f); +} + +int pico_network_receive(struct pico_frame *f) +{ + if (0) {} +#ifdef PICO_SUPPORT_IPV4 + else if (IS_IPV4(f)) { + pico_enqueue(pico_proto_ipv4.q_in, f); + } +#endif +#ifdef PICO_SUPPORT_IPV6 + else if (IS_IPV6(f)) { + pico_enqueue(pico_proto_ipv6.q_in, f); + } +#endif + else { + dbg("Network not found.\n"); + pico_frame_discard(f); + return -1; + } + return f->buffer_len; +} + + +/* Network layer: interface towards socket for frame sending */ +int pico_network_send(struct pico_frame *f) +{ + if (!f || !f->sock || !f->sock->net) { + pico_frame_discard(f); + return -1; + } + return f->sock->net->push(f->sock->net, f); +} + +int pico_destination_is_local(struct pico_frame *f) +{ + if (0) { } +#ifdef PICO_SUPPORT_IPV4 + else if (IS_IPV4(f)) { + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *)f->net_hdr; + if (pico_ipv4_link_find(&hdr->dst)) + return 1; + } +#endif +#ifdef PICO_SUPPORT_IPV6 + else if (IS_IPV6(f)) { + } +#endif + return 0; +} + +int pico_source_is_local(struct pico_frame *f) +{ + if (0) { } +#ifdef PICO_SUPPORT_IPV4 + else if (IS_IPV4(f)) { + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *)f->net_hdr; + if (hdr->src.addr == PICO_IPV4_INADDR_ANY) + return 1; + if (pico_ipv4_link_find(&hdr->src)) + return 1; + } +#endif +#ifdef PICO_SUPPORT_IPV6 + else if (IS_IPV6(f)) { + /* XXX */ + } +#endif + return 0; + + +} + + +/* DATALINK LEVEL: interface from network to the device + * and vice versa. + */ + +/* The pico_ethernet_receive() function is used by + * those devices supporting ETH in order to push packets up + * into the stack. + */ +int pico_ethernet_receive(struct pico_frame *f) +{ + struct pico_eth_hdr *hdr; + if (!f || !f->dev || !f->datalink_hdr) + goto discard; + hdr = (struct pico_eth_hdr *) f->datalink_hdr; + if ( (memcmp(hdr->daddr, f->dev->eth->mac.addr, PICO_SIZE_ETH) != 0) && +#ifdef PICO_SUPPORT_MCAST + (memcmp(hdr->daddr, PICO_ETHADDR_MCAST, PICO_SIZE_MCAST) != 0) && +#endif + (memcmp(hdr->daddr, PICO_ETHADDR_ALL, PICO_SIZE_ETH) != 0) ) + goto discard; + + f->net_hdr = f->datalink_hdr + sizeof(struct pico_eth_hdr); + if (hdr->proto == PICO_IDETH_ARP) + return pico_arp_receive(f); + if ((hdr->proto == PICO_IDETH_IPV4) || (hdr->proto == PICO_IDETH_IPV6)) + return pico_network_receive(f); +discard: + pico_frame_discard(f); + return -1; +} + +static int destination_is_bcast(struct pico_frame *f) +{ + if (!f) + return 0; + + if (IS_IPV6(f)) + return 0; +#ifdef PICO_SUPPORT_IPV4 + else { + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr; + return pico_ipv4_is_broadcast(hdr->dst.addr); + } +#endif + return 0; +} + +#ifdef PICO_SUPPORT_MCAST +static int destination_is_mcast(struct pico_frame *f) +{ + if (!f) + return 0; + + if (IS_IPV6(f)) + return 0; +#ifdef PICO_SUPPORT_IPV4 + else { + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr; + return !pico_ipv4_is_unicast(hdr->dst.addr); + } +#endif + return 0; +} + +static struct pico_eth *pico_ethernet_mcast_translate(struct pico_frame *f, uint8_t *pico_mcast_mac) +{ + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr; + + /* place 23 lower bits of IP in lower 23 bits of MAC */ + pico_mcast_mac[5] = (long_be(hdr->dst.addr) & 0x000000FF); + pico_mcast_mac[4] = (long_be(hdr->dst.addr) & 0x0000FF00) >> 8; + pico_mcast_mac[3] = (long_be(hdr->dst.addr) & 0x007F0000) >> 16; + + return (struct pico_eth *)pico_mcast_mac; +} + + +#endif /* PICO_SUPPORT_MCAST */ + +/* This is called by dev loop in order to ensure correct ethernet addressing. + * Returns 0 if the destination is unknown, and -1 if the packet is not deliverable + * due to ethernet addressing (i.e., no arp association was possible. + * + * Only IP packets must pass by this. ARP will always use direct dev->send() function, so + * we assume IP is used. + */ +int pico_ethernet_send(struct pico_frame *f) +{ + struct pico_eth *dstmac = NULL; + + if (IS_IPV6(f)) { + /*TODO: Neighbor solicitation */ + dstmac = NULL; + } + + else if (IS_IPV4(f)) { + if (IS_BCAST(f) || destination_is_bcast(f)) { + dstmac = (struct pico_eth *) PICO_ETHADDR_ALL; + } +#ifdef PICO_SUPPORT_MCAST + else if (destination_is_mcast(f)) { + uint8_t pico_mcast_mac[6] = {0x01, 0x00, 0x5e, 0x00, 0x00, 0x00}; + dstmac = pico_ethernet_mcast_translate(f, pico_mcast_mac); + } +#endif + else { + dstmac = pico_arp_get(f); + if (!dstmac) + return 0; + } + /* This sets destination and source address, then pushes the packet to the device. */ + if (dstmac && (f->start > f->buffer) && ((f->start - f->buffer) >= PICO_SIZE_ETHHDR)) { + struct pico_eth_hdr *hdr; + f->start -= PICO_SIZE_ETHHDR; + f->len += PICO_SIZE_ETHHDR; + f->datalink_hdr = f->start; + hdr = (struct pico_eth_hdr *) f->datalink_hdr; + memcpy(hdr->saddr, f->dev->eth->mac.addr, PICO_SIZE_ETH); + memcpy(hdr->daddr, dstmac, PICO_SIZE_ETH); + hdr->proto = PICO_IDETH_IPV4; + if(!memcmp(hdr->daddr, hdr->saddr, PICO_SIZE_ETH)){ + dbg("sending out packet destined for our own mac\n"); + return pico_ethernet_receive(f); + }else if(IS_LIMITED_BCAST(f)){ + return pico_device_broadcast(f); + }else { + return f->dev->send(f->dev, f->start, f->len); + /* Frame is discarded after this return by the caller */ + } + } else { + return -1; + } + } /* End IPV4 ethernet addressing */ + return -1; +} + +void pico_store_network_origin(void *src, struct pico_frame *f) +{ + #ifdef PICO_SUPPORT_IPV4 + struct pico_ip4 *ip4; + #endif + + #ifdef PICO_SUPPORT_IPV6 + struct pico_ip6 *ip6; + #endif + + #ifdef PICO_SUPPORT_IPV4 + if (IS_IPV4(f)) { + struct pico_ipv4_hdr *hdr; + hdr = (struct pico_ipv4_hdr *) f->net_hdr; + ip4 = (struct pico_ip4 *) src; + ip4->addr = hdr->src.addr; + } + #endif + #ifdef PICO_SUPPORT_IPV6 + if (IS_IPV6(f)) { + struct pico_ipv6_hdr *hdr; + hdr = (struct pico_ipv6_hdr *) f->net_hdr; + ip6 = (struct pico_ip6 *) src; + memcpy(ip6->addr, hdr->src.addr, PICO_SIZE_IP6); + } + #endif +} + + +/* LOWEST LEVEL: interface towards devices. */ +/* Device driver will call this function which returns immediately. + * Incoming packet will be processed later on in the dev loop. + */ +int pico_stack_recv(struct pico_device *dev, uint8_t *buffer, int len) +{ + struct pico_frame *f; + int ret; + if (len <= 0) + return -1; + f = pico_frame_alloc(len); + if (!f) + return -1; + + /* Association to the device that just received the frame. */ + f->dev = dev; + + /* Setup the start pointer, lenght. */ + f->start = f->buffer; + f->len = f->buffer_len; + if (f->len > 8) { + int mid_frame = (f->buffer_len >> 2)<<1; + mid_frame -= (mid_frame % 4); + pico_rand_feed(*(uint32_t*)(f->buffer + mid_frame)); + } + memcpy(f->buffer, buffer, len); + ret = pico_enqueue(dev->q_in, f); + if (ret <= 0) { + pico_frame_discard(f); + } + return ret; +} + +int pico_sendto_dev(struct pico_frame *f) +{ + if (!f->dev) { + pico_frame_discard(f); + return -1; + } else { + if (f->len > 8) { + int mid_frame = (f->buffer_len >> 2)<<1; + mid_frame -= (mid_frame % 4); + pico_rand_feed(*(uint32_t*)(f->buffer + mid_frame)); + } + return pico_enqueue(f->dev->q_out, f); + } +} + +struct pico_timer +{ + unsigned long expire; + void *arg; + void (*timer)(unsigned long timestamp, void *arg); +}; + +typedef struct pico_timer pico_timer; + +DECLARE_HEAP(pico_timer, expire); + +static heap_pico_timer *Timers; + +void pico_check_timers(void) +{ + struct pico_timer timer; + struct pico_timer *t = heap_first(Timers); + pico_tick = PICO_TIME_MS(); + while((t) && (t->expire < pico_tick)) { + t->timer(pico_tick, t->arg); + heap_peek(Timers, &timer); + t = heap_first(Timers); + } +} + + +#define PROTO_DEF_NR 11 +#define PROTO_DEF_AVG_NR 4 +#define PROTO_DEF_SCORE 32 +#define PROTO_MIN_SCORE 32 +#define PROTO_MAX_SCORE 128 +#define PROTO_LAT_IND 3 /* latecy indication 0-3 (lower is better latency performance), x1, x2, x4, x8 */ +#define PROTO_MAX_LOOP (PROTO_MAX_SCORE<<PROTO_LAT_IND) /* max global loop score, so per tick */ + +static int calc_score(int *score, int *index, int avg[][PROTO_DEF_AVG_NR], int *ret) +{ + int temp, i, j, sum; + int max_total = PROTO_MAX_LOOP, total = 0; + + //dbg("USED SCORES> "); + + for (i = 0; i < PROTO_DEF_NR; i++) { + + /* if used looped score */ + if (ret[i] < score[i]) { + temp = score[i] - ret[i]; /* remaining loop score */ + + //dbg("%3d - ",temp); + + if (index[i] >= PROTO_DEF_AVG_NR) + index[i] = 0; /* reset index */ + j = index[i]; + avg[i][j] = temp; + + index[i]++; + + if (ret[i] == 0 && (score[i]<<1 <= PROTO_MAX_SCORE) && ((total+(score[i]<<1)) < max_total) ) { /* used all loop score -> increase next score directly */ + score[i] <<= 1; + total += score[i]; + continue; + } + + sum = 0; + for (j = 0; j < PROTO_DEF_AVG_NR; j++) + sum += avg[i][j]; /* calculate sum */ + + sum >>= 2; /* divide by 4 to get average used score */ + + /* criterion to increase next loop score */ + if (sum > (score[i] - (score[i]>>2)) && (score[i]<<1 <= PROTO_MAX_SCORE) && ((total+(score[i]<<1)) < max_total)) { /* > 3/4 */ + score[i] <<= 1; /* double loop score */ + total += score[i]; + continue; + } + + /* criterion to decrease next loop score */ + if (sum < (score[i]>>2) && (score[i]>>1 >= PROTO_MIN_SCORE)) { /* < 1/4 */ + score[i] >>= 1; /* half loop score */ + total += score[i]; + continue; + } + + /* also add non-changed scores */ + total += score[i]; + } + else if (ret[i] == score[i]) { + /* no used loop score - gradually decrease */ + + // dbg("%3d - ",0); + + if (index[i] >= PROTO_DEF_AVG_NR) + index[i] = 0; /* reset index */ + j = index[i]; + avg[i][j] = 0; + + index[i]++; + + sum = 0; + for (j = 0; j < PROTO_DEF_AVG_NR; j++) + sum += avg[i][j]; /* calculate sum */ + + sum >>= 2; /* divide by 4 to get average used score */ + + if ((sum == 0) && (score[i]>>1 >= PROTO_MIN_SCORE)) { + score[i] >>= 1; /* half loop score */ + total += score[i]; + for (j = 0; j < PROTO_DEF_AVG_NR; j++) + avg[i][j] = score[i]; + } + + } + } + + //dbg("\n"); + + return 0; +} + + +void pico_stack_tick(void) +{ + static int score[PROTO_DEF_NR] = {PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE}; + static int index[PROTO_DEF_NR] = {0,0,0,0,0,0}; + static int avg[PROTO_DEF_NR][PROTO_DEF_AVG_NR]; + static int ret[PROTO_DEF_NR] = {0}; + + pico_check_timers(); + + //dbg("LOOP_SCORES> %3d - %3d - %3d - %3d - %3d - %3d - %3d - %3d - %3d - %3d - %3d\n",score[0],score[1],score[2],score[3],score[4],score[5],score[6],score[7],score[8],score[9],score[10]); + + //score = pico_protocols_loop(100); + + ret[0] = pico_devices_loop(score[0],PICO_LOOP_DIR_IN); + pico_rand_feed(ret[0]); + + ret[1] = pico_protocol_datalink_loop(score[1], PICO_LOOP_DIR_IN); + pico_rand_feed(ret[1]); + + ret[2] = pico_protocol_network_loop(score[2], PICO_LOOP_DIR_IN); + pico_rand_feed(ret[2]); + + ret[3] = pico_protocol_transport_loop(score[3], PICO_LOOP_DIR_IN); + pico_rand_feed(ret[3]); + + + ret[5] = score[5]; +#if defined (PICO_SUPPORT_IPV4) || defined (PICO_SUPPORT_IPV6) +#if defined (PICO_SUPPORT_TCP) || defined (PICO_SUPPORT_UDP) + ret[5] = pico_sockets_loop(score[5]); // swapped + pico_rand_feed(ret[5]); +#endif +#endif + + ret[4] = pico_protocol_socket_loop(score[4], PICO_LOOP_DIR_IN); + pico_rand_feed(ret[4]); + + + ret[6] = pico_protocol_socket_loop(score[6], PICO_LOOP_DIR_OUT); + pico_rand_feed(ret[6]); + + ret[7] = pico_protocol_transport_loop(score[7], PICO_LOOP_DIR_OUT); + pico_rand_feed(ret[7]); + + ret[8] = pico_protocol_network_loop(score[8], PICO_LOOP_DIR_OUT); + pico_rand_feed(ret[8]); + + ret[9] = pico_protocol_datalink_loop(score[9], PICO_LOOP_DIR_OUT); + pico_rand_feed(ret[9]); + + ret[10] = pico_devices_loop(score[10],PICO_LOOP_DIR_OUT); + pico_rand_feed(ret[10]); + + /* calculate new loop scores for next iteration */ + calc_score(score, index,(int (*)[]) avg, ret); +} + +void pico_stack_loop(void) +{ + while(1) { + pico_stack_tick(); + PICO_IDLE(); + } +} + +void pico_timer_add(unsigned long expire, void (*timer)(unsigned long, void *), void *arg) +{ + pico_timer t; + t.expire = PICO_TIME_MS() + expire; + t.arg = arg; + t.timer = timer; + heap_insert(Timers, &t); + if (Timers->n > PICO_MAX_TIMERS) { + dbg("Warning: I have %d timers\n", Timers->n); + } +} + +void pico_stack_init(void) +{ + +#ifdef PICO_SUPPORT_IPV4 + pico_protocol_init(&pico_proto_ipv4); +#endif + +#ifdef PICO_SUPPORT_IPV6 + pico_protocol_init(&pico_proto_ipv6); +#endif + +#ifdef PICO_SUPPORT_ICMP4 + pico_protocol_init(&pico_proto_icmp4); +#endif + +#ifdef PICO_SUPPORT_IGMP2 + pico_protocol_init(&pico_proto_igmp2); +#endif + +#ifdef PICO_SUPPORT_UDP + pico_protocol_init(&pico_proto_udp); +#endif + +#ifdef PICO_SUPPORT_TCP + pico_protocol_init(&pico_proto_tcp); +#endif + +#ifdef PICO_SUPPORT_DNS_CLIENT + pico_dns_client_init(); +#endif + + pico_rand_feed(123456); + + /* Initialize timer heap */ + Timers = heap_init(); + pico_stack_tick(); + pico_stack_tick(); + pico_stack_tick(); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/picotcp/stack/pico_tree.c Thu Apr 25 13:22:51 2013 +0000 @@ -0,0 +1,486 @@ +/********************************************************************* +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_tree.h" +#include "pico_config.h" + +#define RED 0 +#define BLACK 1 + +// By default the null leafs are black +struct pico_tree_node LEAF = { + NULL, // key + &LEAF, &LEAF, &LEAF, // parent, left,right + BLACK // color +}; + +#define IS_LEAF(x) (x == &LEAF) +#define IS_NOT_LEAF(x) (x != &LEAF) +#define INIT_LEAF (&LEAF) + +#define AM_I_LEFT_CHILD(x) (x == x->parent->leftChild) +#define AM_I_RIGHT_CHILD(x) (x == x->parent->rightChild) + +#define PARENT(x) (x->parent) +#define GRANPA(x) (x->parent->parent) + +/* + * Local Functions + */ +static struct pico_tree_node * create_node(void *key); +static void rotateToLeft(struct pico_tree* tree, struct pico_tree_node* node); +static void rotateToRight(struct pico_tree* root, struct pico_tree_node* node); +static void fix_insert_collisions(struct pico_tree* tree, struct pico_tree_node* node); +static void fix_delete_collisions(struct pico_tree* tree, struct pico_tree_node * node); +static void switchNodes(struct pico_tree* tree, struct pico_tree_node* nodeA, struct pico_tree_node* nodeB); + +/* + * Exported functions + */ + +struct pico_tree_node * pico_tree_firstNode(struct pico_tree_node * node) +{ + while(IS_NOT_LEAF(node->leftChild)) + node = node->leftChild; + + return node; +} + +struct pico_tree_node * pico_tree_lastNode(struct pico_tree_node * node) +{ + while(IS_NOT_LEAF(node->rightChild)) + node = node->rightChild; + + return node; +} + +struct pico_tree_node * pico_tree_next(struct pico_tree_node * node) +{ + if(IS_NOT_LEAF(node->rightChild)) + { + node = node->rightChild; + while(IS_NOT_LEAF(node->leftChild)) + node = node->leftChild; + } + else + { + if (IS_NOT_LEAF(node->parent) && AM_I_LEFT_CHILD(node)) + node = node->parent; + else { + while (IS_NOT_LEAF(node->parent) && AM_I_RIGHT_CHILD(node)) + node = node->parent; + + node = node->parent; + } + } + + return node; +} + +struct pico_tree_node * pico_tree_prev(struct pico_tree_node * node) +{ + if (IS_NOT_LEAF(node->leftChild)) { + node = node->leftChild; + while (IS_NOT_LEAF(node->rightChild)) + node = node->rightChild; + } else { + if (IS_NOT_LEAF(node->parent) && AM_I_RIGHT_CHILD(node)) + node = node->parent; + else { + while (IS_NOT_LEAF(node) && AM_I_LEFT_CHILD(node)) + node = node->parent; + + node = node->parent; + } + } + + return node; +} + +void * pico_tree_insert(struct pico_tree* tree, void * key){ + + struct pico_tree_node * last_node = INIT_LEAF; + struct pico_tree_node * temp = tree->root; + struct pico_tree_node * insert; + void * LocalKey; + int result = 0; + + LocalKey = (IS_NOT_LEAF(tree->root) ? pico_tree_findKey(tree,key) : NULL); + // if node already in, bail out + if(LocalKey) + return LocalKey; + else + insert = create_node(key); + + // search for the place to insert the new node + while(IS_NOT_LEAF(temp)) + { + last_node = temp; + result = tree->compare(insert->keyValue,temp->keyValue); + + temp = (result < 0 ? temp->leftChild : temp->rightChild); + } + + // make the needed connections + insert->parent = last_node; + + if(IS_LEAF(last_node)) + tree->root = insert; + else{ + result = tree->compare(insert->keyValue,last_node->keyValue); + if(result < 0) + last_node->leftChild = insert; + else + last_node->rightChild = insert; + } + + // fix colour issues + fix_insert_collisions(tree, insert); + + return NULL; +} + +struct pico_tree_node * pico_tree_findNode(struct pico_tree * tree, void * key) +{ + struct pico_tree_node *found; + found = tree->root; + + while(IS_NOT_LEAF(found)) + { + int result; + result = tree->compare(found->keyValue, key); + if(result == 0) + return found; + else if(result < 0) + found = found->rightChild; + else + found = found->leftChild; + } + + return NULL; +} + +void * pico_tree_findKey(struct pico_tree * tree, void * key) +{ + struct pico_tree_node *found; + found = tree->root; + + while(IS_NOT_LEAF(found)) + { + int result; + + result = tree->compare(found->keyValue, key); + if(result == 0) + { + return found->keyValue; + } + else if(result < 0) + found = found->rightChild; + else + found = found->leftChild; + + } + + return NULL; +} + +void * pico_tree_first(struct pico_tree * tree) +{ + return pico_tree_firstNode(tree->root)->keyValue; +} + +void * pico_tree_last(struct pico_tree * tree) +{ + return pico_tree_lastNode(tree->root)->keyValue; +} + +void * pico_tree_delete(struct pico_tree * tree, void * key){ + + uint8_t nodeColor; // keeps the color of the node to be deleted + void * lkey; // keeps a copy of the key which will be removed + struct pico_tree_node * delete; // keeps a copy of the node to be extracted + struct pico_tree_node * temp; // temporary + struct pico_tree_node * ltemp; // used for switching nodes, as a copy + + delete = pico_tree_findNode(tree, key); + ltemp = delete; + + // this key isn't in the tree, bail out + if(!delete) + return NULL; + + lkey = delete->keyValue; + nodeColor = delete->color; + + if(IS_LEAF(delete->leftChild)) + { + temp = ltemp->rightChild; + switchNodes(tree, ltemp, ltemp->rightChild); + } + else + if(IS_LEAF(delete->rightChild)) + { + struct pico_tree_node * ltemp = delete; + temp = ltemp->leftChild; + switchNodes(tree, ltemp, ltemp->leftChild); + } + else{ + struct pico_tree_node * min; + min = pico_tree_firstNode(delete->rightChild); + nodeColor = min->color; + + temp = min->rightChild; + if(min->parent == ltemp && IS_NOT_LEAF(temp)) + temp->parent = min; + else{ + switchNodes(tree, min, min->rightChild); + min->rightChild = ltemp->rightChild; + if(IS_NOT_LEAF(min->rightChild)) min->rightChild->parent = min; + } + switchNodes(tree, ltemp, min); + min->leftChild = ltemp->leftChild; + if(IS_NOT_LEAF(min->leftChild)) min->leftChild->parent = min; + min->color = ltemp->color; + } + + // deleted node is black, this will mess up the black path property + if(nodeColor == BLACK) + fix_delete_collisions(tree, temp); + + pico_free(delete); + + return lkey; +} + +int pico_tree_empty(struct pico_tree * tree) +{ + return (!tree->root || IS_LEAF(tree->root)); +} + +/* + * Private functions + */ +static void rotateToLeft(struct pico_tree* tree, struct pico_tree_node* node) +{ + struct pico_tree_node* temp; + + temp = node->rightChild; + + if(temp == &LEAF) return; + + node->rightChild = temp->leftChild; + + if(IS_NOT_LEAF(temp->leftChild)) + temp->leftChild->parent = node; + + temp->parent = node->parent; + + if(IS_LEAF(node->parent)) + tree->root = temp; + else + if(node == node->parent->leftChild) + node->parent->leftChild = temp; + else + node->parent->rightChild = temp; + + temp->leftChild = node; + node->parent = temp; +} + + +static void rotateToRight(struct pico_tree * tree, struct pico_tree_node * node) +{ + struct pico_tree_node* temp; + + temp = node->leftChild; + node->leftChild = temp->rightChild; + + if(temp == &LEAF) return; + + if(IS_NOT_LEAF(temp->rightChild)) + temp->rightChild->parent = node; + + temp->parent = node->parent; + + if(IS_LEAF(node->parent)) + tree->root = temp; + else + if(node == node->parent->rightChild) + node->parent->rightChild = temp; + else + node->parent->leftChild = temp; + + temp->rightChild = node; + node->parent = temp; + return; +} + +static struct pico_tree_node * create_node(void* key) +{ + struct pico_tree_node *temp; + temp = (struct pico_tree_node *)pico_zalloc(sizeof(struct pico_tree_node)); + + if(!temp) + return NULL; + + temp->keyValue = key; + temp->parent = &LEAF; + temp->leftChild = &LEAF; + temp->rightChild = &LEAF; + // by default every new node is red + temp->color = RED; + + return temp; +} + +/* + * This function fixes the possible collisions in the tree. + * Eg. if a node is red his children must be black ! + */ +static void fix_insert_collisions(struct pico_tree* tree, struct pico_tree_node* node) +{ + struct pico_tree_node* temp; + + while(node->parent->color == RED) + { + if(AM_I_RIGHT_CHILD(node->parent)) + { + temp = GRANPA(node)->leftChild; + if(temp->color == RED){ + node->parent->color = BLACK; + temp->color = BLACK; + GRANPA(node)->color = RED; + node = GRANPA(node); + } + else if(temp->color == BLACK){ + if(node == node->parent->leftChild){ + node = node->parent; + rotateToRight(tree, node); + } + node->parent->color = BLACK; + GRANPA(node)->color = RED; + rotateToLeft(tree, GRANPA(node)); + } + } + else if(AM_I_LEFT_CHILD(node->parent)) + { + temp = GRANPA(node)->rightChild; + if(temp->color == RED){ + node->parent->color = BLACK; + temp->color = BLACK; + GRANPA(node)->color = RED; + node = GRANPA(node); + } + else if(temp->color == BLACK){ + if(AM_I_RIGHT_CHILD(node)){ + node = node->parent; + rotateToLeft(tree, node); + } + node->parent->color = BLACK; + GRANPA(node)->color = RED; + rotateToRight(tree, GRANPA(node)); + } + } + } + + // make sure that the root of the tree stays black + tree->root->color = BLACK; +} + +static void switchNodes(struct pico_tree* tree, struct pico_tree_node* nodeA, struct pico_tree_node* nodeB) +{ + + if(IS_LEAF(nodeA->parent)) + tree->root = nodeB; + else + if(IS_NOT_LEAF(nodeA)) + { + if(AM_I_LEFT_CHILD(nodeA)) + nodeA->parent->leftChild = nodeB; + else + nodeA->parent->rightChild = nodeB; + } + + if(IS_NOT_LEAF(nodeB)) nodeB->parent = nodeA->parent; + +} + +/* + * This function fixes the possible collisions in the tree. + * Eg. if a node is red his children must be black ! + * In this case the function fixes the constant black path property. + */ +static void fix_delete_collisions(struct pico_tree* tree, struct pico_tree_node * node) +{ + struct pico_tree_node* temp; + + while( node != tree->root && node->color == BLACK && IS_NOT_LEAF(node)) + { + if(AM_I_LEFT_CHILD(node)){ + + temp = node->parent->rightChild; + if(temp->color == RED) + { + temp->color = BLACK; + node->parent->color = RED; + rotateToLeft(tree, node->parent); + temp = node->parent->rightChild; + } + if(temp->leftChild->color == BLACK && temp->rightChild->color == BLACK) + { + temp->color = RED; + node = node->parent; + } + else + { + if(temp->rightChild->color == BLACK) + { + temp->leftChild->color = BLACK; + temp->color = RED; + rotateToRight(tree, temp); + temp = temp->parent->rightChild; + } + temp->color = node->parent->color; + node->parent->color = BLACK; + temp->rightChild->color = BLACK; + rotateToLeft(tree, node->parent); + node = tree->root; + } + } + else{ + temp = node->parent->leftChild; + if(temp->color == RED) + { + temp->color = BLACK; + node->parent->color = RED; + rotateToRight(tree, node->parent); + temp = node->parent->leftChild; + } + if(temp->rightChild->color == BLACK && temp->leftChild->color == BLACK) + { + temp->color = RED; + node = node->parent; + } + else{ + if(temp->leftChild->color == BLACK) + { + temp->rightChild->color = BLACK; + temp->color = RED; + rotateToLeft(tree, temp); + temp = temp->parent->leftChild; + } + temp->color = node->parent->color; + node->parent->color = BLACK; + temp->leftChild->color = BLACK; + rotateToRight(tree, node->parent); + node = tree->root; + } + } + } + + node->color = BLACK; +}