Free (GPLv2) TCP/IP stack developed by TASS Belgium
Dependents: lpc1768-picotcp-demo ZeroMQ_PicoTCP_Publisher_demo TCPSocket_HelloWorld_PicoTCP Pico_TCP_UDP_Test ... more
PicoTCP. Copyright (c) 2013 TASS Belgium NV.
Released under the GNU General Public License, version 2.
Different licensing models may exist, at the sole discretion of the Copyright holders.
Official homepage: http://www.picotcp.com
Bug tracker: https://github.com/tass-belgium/picotcp/issues
Development steps:
initial integration with mbed RTOSgeneric mbed Ethernet driverhigh performance NXP LPC1768 specific Ethernet driverMulti-threading support for mbed RTOSBerkeley sockets and integration with the New Socket APIFork of the apps running on top of the New Socket APIScheduling optimizations- Debugging/benchmarking/testing
Demo application (measuring TCP sender performance):
Import programlpc1768-picotcp-demo
A PicoTCP demo app testing the ethernet throughput on the lpc1768 mbed board.
stack/pico_socket.c
- Committer:
- tass picotcp@tass.be
- Date:
- 2014-04-09
- Revision:
- 150:551effcf6a39
- Parent:
- 149:5f4cb161cec3
- Child:
- 152:a3d286bf94e5
File content as of revision 150:551effcf6a39:
/********************************************************************* 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" #include "pico_socket_multicast.h" #include "pico_socket_tcp.h" #include "pico_socket_udp.h" #if defined (PICO_SUPPORT_IPV4) || defined (PICO_SUPPORT_IPV6) #if defined (PICO_SUPPORT_TCP) || defined (PICO_SUPPORT_UDP) #define PROTO(s) ((s)->proto->proto_number) #define PICO_SOCKET4_MTU 1480 /* Ethernet MTU(1500) - IP header size(20) */ #define PICO_SOCKET6_MTU 1460 /* Ethernet MTU(1500) - IP header size(40) */ #define TCP_STATE(s) (s->state & PICO_SOCKET_STATE_TCP) #ifdef PICO_SUPPORT_MUTEX static void *Mutex = NULL; #define PICOTCP_MUTEX_LOCK(x) { \ if (x == NULL) \ x = pico_mutex_init(); \ pico_mutex_lock(x); \ } #define PICOTCP_MUTEX_UNLOCK(x) pico_mutex_unlock(x) #else #define PICOTCP_MUTEX_LOCK(x) do {} while(0) #define PICOTCP_MUTEX_UNLOCK(x) do {} while(0) #endif #define PROTO(s) ((s)->proto->proto_number) #define PICO_SOCKET_MTU 1480 /* Ethernet MTU(1500) - IP header size(20) */ #ifdef PICO_SUPPORT_IPV4 # define IS_SOCK_IPV4(s) ((s->net == &pico_proto_ipv4)) #else # define IS_SOCK_IPV4(s) (0) #endif #ifdef PICO_SUPPORT_IPV6 # define IS_SOCK_IPV6(s) ((s->net == &pico_proto_ipv6)) #else # define IS_SOCK_IPV6(s) (0) #endif # define frag_dbg(...) do {} while(0) static struct pico_sockport *sp_udp = NULL, *sp_tcp = NULL; struct pico_frame *pico_socket_frame_alloc(struct pico_socket *s, uint16_t len); static int socket_cmp_family(struct pico_socket *a, struct pico_socket *b) { uint32_t a_is_ip6 = is_sock_ipv6(a); uint32_t b_is_ip6 = is_sock_ipv6(b); (void)a; (void)b; if (a_is_ip6 < b_is_ip6) return -1; if (a_is_ip6 > b_is_ip6) return 1; return 0; } static int socket_cmp_ipv6(struct pico_socket *a, struct pico_socket *b) { int ret = 0; (void)a; (void)b; #ifdef PICO_SUPPORT_IPV6 if (!is_sock_ipv6(a) || !is_sock_ipv6(b)) return 0; if ((memcmp(a->local_addr.ip6.addr, PICO_IP6_ANY, PICO_SIZE_IP6) == 0) || (memcmp(b->local_addr.ip6.addr, PICO_IP6_ANY, PICO_SIZE_IP6) == 0)) ret = 0; else ret = memcmp(a->local_addr.ip6.addr, b->local_addr.ip6.addr, PICO_SIZE_IP6); #endif return ret; } static int socket_cmp_ipv4(struct pico_socket *a, struct pico_socket *b) { int ret = 0; (void)a; (void)b; if (!is_sock_ipv4(a) || !is_sock_ipv4(b)) return 0; #ifdef PICO_SUPPORT_IPV4 if ((a->local_addr.ip4.addr == PICO_IP4_ANY) || (b->local_addr.ip4.addr == PICO_IP4_ANY)) ret = 0; else ret = (int)(a->local_addr.ip4.addr - b->local_addr.ip4.addr); #endif return ret; } static int socket_cmp_remotehost(struct pico_socket *a, struct pico_socket *b) { int ret = 0; if (is_sock_ipv6(a)) ret = memcmp(a->remote_addr.ip6.addr, b->remote_addr.ip6.addr, PICO_SIZE_IP6); else ret = (int)(a->remote_addr.ip4.addr - b->remote_addr.ip4.addr); return ret; } static int socket_cmp_addresses(struct pico_socket *a, struct pico_socket *b) { int ret = 0; /* At this point, sort by local host */ ret = socket_cmp_ipv6(a, b); if (ret == 0) ret = socket_cmp_ipv4(a, b); /* Sort by remote host */ if (ret == 0) ret = socket_cmp_remotehost(a, b); return 0; } static int socket_cmp(void *ka, void *kb) { struct pico_socket *a = ka, *b = kb; int ret = 0; /* First, order by network family */ ret = socket_cmp_family(a, b); /* Then, compare by source/destination addresses */ if (ret == 0) ret = socket_cmp_addresses(a, b); /* And finally by remote port. The two sockets are coincident if the quad is the same. */ if (ret == 0) ret = b->remote_port - a->remote_port; return ret; } #define INIT_SOCKPORT { {&LEAF, socket_cmp}, 0, 0 } int sockport_cmp(void *ka, void *kb) { struct pico_sockport *a = ka, *b = kb; if (a->number < b->number) return -1; if (a->number > b->number) return 1; return 0; } PICO_TREE_DECLARE(UDPTable, sockport_cmp); PICO_TREE_DECLARE(TCPTable, sockport_cmp); struct pico_sockport *pico_get_sockport(uint16_t proto, uint16_t port) { struct pico_sockport test = INIT_SOCKPORT; test.number = port; if (proto == PICO_PROTO_UDP) return pico_tree_findKey(&UDPTable, &test); else if (proto == PICO_PROTO_TCP) return pico_tree_findKey(&TCPTable, &test); else return NULL; } #ifdef PICO_SUPPORT_IPV4 static int pico_port_in_use_by_nat(uint16_t proto, uint16_t port) { int ret = 0; (void) proto; (void) port; #ifdef PICO_SUPPORT_NAT if (pico_ipv4_nat_find(port, NULL, 0, (uint8_t)proto)) { dbg("In use by nat....\n"); ret = 1; } #endif return ret; } static int pico_port_in_use_with_this_ipv4_address(struct pico_sockport *sp, struct pico_ip4 ip) { if (sp) { struct pico_ip4 *s_local; struct pico_tree_node *idx; struct pico_socket *s; pico_tree_foreach(idx, &sp->socks) { s = idx->keyValue; if (s->net == &pico_proto_ipv4) { s_local = (struct pico_ip4*) &s->local_addr; if ((s_local->addr == PICO_IPV4_INADDR_ANY) || (s_local->addr == ip.addr)) { return 1; } } } } return 0; } static int pico_port_in_use_ipv4(struct pico_sockport *sp, void *addr) { struct pico_ip4 ip; /* IPv4 */ if (addr) ip.addr = ((struct pico_ip4 *)addr)->addr; else ip.addr = PICO_IPV4_INADDR_ANY; if (ip.addr == PICO_IPV4_INADDR_ANY) { if (!sp) return 0; else { dbg("In use, and asked for ANY\n"); return 1; } } return pico_port_in_use_with_this_ipv4_address(sp, ip); } #endif #ifdef PICO_SUPPORT_IPV6 static int pico_port_in_use_with_this_ipv6_address(struct pico_sockport *sp, struct pico_ip6 ip) { if (sp) { struct pico_ip6 *s_local; struct pico_tree_node *idx; struct pico_socket *s; pico_tree_foreach(idx, &sp->socks) { s = idx->keyValue; if (s->net == &pico_proto_ipv6) { s_local = (struct pico_ip6*) &s->local_addr; if ((pico_ipv6_is_unspecified(s_local->addr)) || (!memcmp(s_local->addr, ip.addr, PICO_SIZE_IP6))) { return 1; } } } } return 0; } static int pico_port_in_use_ipv6(struct pico_sockport *sp, void *addr) { struct pico_ip6 ip; /* IPv6 */ if (addr) memcpy(&ip.addr, ((struct pico_ip6 *)addr)->addr, sizeof(struct pico_ip6)); else memcpy(&ip.addr, PICO_IP6_ANY, sizeof(struct pico_ip6)); if (memcmp(ip.addr, PICO_IP6_ANY, sizeof(struct pico_ip6)) == 0) { if (!sp) return 0; else { dbg("In use, and asked for ANY\n"); return 1; } } return pico_port_in_use_with_this_ipv6_address(sp, ip); } #endif static int pico_generic_port_in_use(uint16_t proto, uint16_t port, struct pico_sockport *sp, void *addr) { #ifdef PICO_SUPPORT_IPV4 if (pico_port_in_use_by_nat(proto, port)) { return 1; } if (pico_port_in_use_ipv4(sp, addr)) { return 1; } #endif #ifdef PICO_SUPPORT_IPV6 if (pico_port_in_use_ipv6(sp, addr)) { return 1; } #endif return 0; } int pico_is_port_free(uint16_t proto, uint16_t port, void *addr, void *net) { struct pico_sockport *sp; (void) net; sp = pico_get_sockport(proto, port); if (pico_generic_port_in_use(proto, port, sp, 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; } struct pico_socket *pico_sockets_find(uint16_t local, uint16_t remote) { struct pico_socket *sock = NULL; struct pico_tree_node *index = NULL; struct pico_sockport *sp = NULL; sp = pico_get_sockport(PICO_PROTO_TCP, local); if(sp) { pico_tree_foreach(index, &sp->socks) { if(((struct pico_socket *)index->keyValue)->remote_port == remote) { sock = (struct pico_socket *)index->keyValue; break; } } } return sock; } int8_t pico_socket_add(struct pico_socket *s) { struct pico_sockport *sp = pico_get_sockport(PROTO(s), s->local_port); PICOTCP_MUTEX_LOCK(Mutex); if (!sp) { /* dbg("Creating sockport..%04x\n", s->local_port); / * In comment due to spam during test * / */ sp = PICO_ZALLOC(sizeof(struct pico_sockport)); if (!sp) { pico_err = PICO_ERR_ENOMEM; PICOTCP_MUTEX_UNLOCK(Mutex); return -1; } sp->proto = PROTO(s); sp->number = s->local_port; sp->socks.root = &LEAF; sp->socks.compare = socket_cmp; if (PROTO(s) == PICO_PROTO_UDP) { pico_tree_insert(&UDPTable, sp); } else if (PROTO(s) == PICO_PROTO_TCP) { pico_tree_insert(&TCPTable, sp); } } pico_tree_insert(&sp->socks, s); s->state |= PICO_SOCKET_STATE_BOUND; PICOTCP_MUTEX_UNLOCK(Mutex); #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_clean_queues(struct pico_socket *sock) { struct pico_frame *f_in = pico_dequeue(&sock->q_in); struct pico_frame *f_out = pico_dequeue(&sock->q_out); while(f_in || f_out) { if(f_in) { pico_frame_discard(f_in); f_in = pico_dequeue(&sock->q_in); } if(f_out) { pico_frame_discard(f_out); f_out = pico_dequeue(&sock->q_out); } } pico_socket_tcp_cleanup(sock); } static void socket_garbage_collect(pico_time now, void *arg) { struct pico_socket *s = (struct pico_socket *) arg; IGNORE_PARAMETER(now); socket_clean_queues(s); PICO_FREE(s); } static void pico_socket_check_empty_sockport(struct pico_socket *s, struct pico_sockport *sp) { if(pico_tree_empty(&sp->socks)) { if (PROTO(s) == PICO_PROTO_UDP) { pico_tree_delete(&UDPTable, sp); } else if (PROTO(s) == PICO_PROTO_TCP) { pico_tree_delete(&TCPTable, sp); } if(sp_tcp == sp) sp_tcp = NULL; if(sp_udp == sp) sp_udp = NULL; PICO_FREE(sp); } } int8_t pico_socket_del(struct pico_socket *s) { struct pico_sockport *sp = pico_get_sockport(PROTO(s), s->local_port); if (!sp) { pico_err = PICO_ERR_ENXIO; return -1; } PICOTCP_MUTEX_LOCK(Mutex); pico_tree_delete(&sp->socks, s); pico_socket_check_empty_sockport(s, sp); pico_multicast_delete(s); pico_socket_tcp_delete(s); s->state = PICO_SOCKET_STATE_CLOSED; pico_timer_add(3000, socket_garbage_collect, s); PICOTCP_MUTEX_UNLOCK(Mutex); return 0; } static void pico_socket_update_tcp_state(struct pico_socket *s, uint16_t tcp_state) { if (tcp_state) { s->state &= 0x00FF; s->state |= tcp_state; } } static int8_t pico_socket_alter_state(struct pico_socket *s, uint16_t more_states, uint16_t less_states, uint16_t tcp_state) { struct pico_sockport *sp; if (more_states & PICO_SOCKET_STATE_BOUND) return pico_socket_add(s); if (less_states & PICO_SOCKET_STATE_BOUND) return pico_socket_del(s); sp = pico_get_sockport(PROTO(s), s->local_port); if (!sp) { pico_err = PICO_ERR_ENXIO; return -1; } s->state |= more_states; s->state = (uint16_t)(s->state & (~less_states)); pico_socket_update_tcp_state(s, tcp_state); return 0; } static int pico_socket_transport_deliver(struct pico_protocol *p, struct pico_sockport *sp, struct pico_frame *f) { if (p->proto_number == PICO_PROTO_TCP) return pico_socket_tcp_deliver(sp, f); if (p->proto_number == PICO_PROTO_UDP) return pico_socket_udp_deliver(sp, f); return -1; } static int pico_socket_deliver(struct pico_protocol *p, struct pico_frame *f, uint16_t localport) { struct pico_sockport *sp = NULL; struct pico_trans *tr = (struct pico_trans *) f->transport_hdr; if (!tr) return -1; sp = pico_get_sockport(p->proto_number, localport); if (!sp) { dbg("No such port %d\n", short_be(localport)); return -1; } return pico_socket_transport_deliver(p, sp, f); } int pico_socket_set_family(struct pico_socket *s, uint16_t family) { (void) family; #ifdef PICO_SUPPORT_IPV4 if (family == PICO_PROTO_IPV4) s->net = &pico_proto_ipv4; #endif #ifdef PICO_SUPPORT_IPV6 if (family == PICO_PROTO_IPV6) s->net = &pico_proto_ipv6; #endif if (s->net == NULL) return -1; return 0; } static struct pico_socket *pico_socket_transport_open(uint16_t proto, uint16_t family) { struct pico_socket *s = NULL; if (proto == PICO_PROTO_UDP) s = pico_socket_udp_open(); if (proto == PICO_PROTO_TCP) s = pico_socket_tcp_open(family); return s; } struct pico_socket *pico_socket_open(uint16_t net, uint16_t proto, void (*wakeup)(uint16_t ev, struct pico_socket *)) { struct pico_socket *s = NULL; s = pico_socket_transport_open(proto, net); if (!s) { pico_err = PICO_ERR_EPROTONOSUPPORT; return NULL; } if (pico_socket_set_family(s, net) != 0) { PICO_FREE(s); pico_err = PICO_ERR_ENETUNREACH; return NULL; } s->q_in.max_size = PICO_DEFAULT_SOCKETQ; s->q_out.max_size = PICO_DEFAULT_SOCKETQ; s->wakeup = wakeup; return s; } static void pico_socket_clone_assign_address(struct pico_socket *s, struct pico_socket *facsimile) { #ifdef PICO_SUPPORT_IPV4 if (facsimile->net == &pico_proto_ipv4) { s->net = &pico_proto_ipv4; memcpy(&s->local_addr, &facsimile->local_addr, sizeof(struct pico_ip4)); memcpy(&s->remote_addr, &facsimile->remote_addr, sizeof(struct pico_ip4)); } #endif #ifdef PICO_SUPPORT_IPV6 if (facsimile->net == &pico_proto_ipv6) { s->net = &pico_proto_ipv6; memcpy(&s->local_addr, &facsimile->local_addr, sizeof(struct pico_ip6)); memcpy(&s->remote_addr, &facsimile->remote_addr, sizeof(struct pico_ip6)); } #endif } struct pico_socket *pico_socket_clone(struct pico_socket *facsimile) { struct pico_socket *s = NULL; s = pico_socket_transport_open(facsimile->proto->proto_number, facsimile->net->proto_number); if (!s) { pico_err = PICO_ERR_EPROTONOSUPPORT; return NULL; } s->local_port = facsimile->local_port; s->remote_port = facsimile->remote_port; s->state = facsimile->state; pico_socket_clone_assign_address(s, facsimile); if (!s->net) { PICO_FREE(s); pico_err = PICO_ERR_ENETUNREACH; return NULL; } s->q_in.max_size = PICO_DEFAULT_SOCKETQ; s->q_out.max_size = PICO_DEFAULT_SOCKETQ; s->wakeup = NULL; return s; } static int pico_socket_transport_read(struct pico_socket *s, void *buf, int len) { if (PROTO(s) == PICO_PROTO_UDP) return pico_socket_udp_recv(s, buf, (uint16_t)len, NULL, NULL); else if (PROTO(s) == PICO_PROTO_TCP) return pico_socket_tcp_read(s, buf, (uint32_t)len); else return 0; } int pico_socket_read(struct pico_socket *s, void *buf, int len) { if (!s || buf == NULL) { pico_err = PICO_ERR_EINVAL; return -1; } else { /* check if exists in tree */ /* See task #178 */ if (pico_check_socket(s) != 0) { pico_err = PICO_ERR_EINVAL; return -1; } } if ((s->state & PICO_SOCKET_STATE_BOUND) == 0) { pico_err = PICO_ERR_EIO; return -1; } return pico_socket_transport_read(s, buf, len); } static int pico_socket_write_check_state(struct pico_socket *s) { if ((s->state & PICO_SOCKET_STATE_BOUND) == 0) { pico_err = PICO_ERR_EIO; return -1; } if ((s->state & PICO_SOCKET_STATE_CONNECTED) == 0) { pico_err = PICO_ERR_ENOTCONN; return -1; } if (s->state & PICO_SOCKET_STATE_SHUT_LOCAL) { /* check if in shutdown state */ pico_err = PICO_ERR_ESHUTDOWN; return -1; } return 0; } static int pico_socket_write_attempt(struct pico_socket *s, const void *buf, int len) { if (pico_socket_write_check_state(s) < 0) { return -1; } else { return pico_socket_sendto(s, buf, len, &s->remote_addr, s->remote_port); } } int pico_socket_write(struct pico_socket *s, const void *buf, int len) { if (!s || buf == NULL) { pico_err = PICO_ERR_EINVAL; return -1; } else { /* check if exists in tree */ /* See task #178 */ if (pico_check_socket(s) != 0) { pico_err = PICO_ERR_EINVAL; return -1; } } return pico_socket_write_attempt(s, buf, len); } 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; } static void *pico_socket_sendto_get_ip4_src(struct pico_socket *s, struct pico_ip4 *dst) { struct pico_ip4 *src4 = NULL; #ifdef PICO_SUPPORT_IPV4 /* Check if socket is connected: destination address MUST match the * current connected endpoint */ if ((s->state & PICO_SOCKET_STATE_CONNECTED)) { src4 = &s->local_addr.ip4; if (s->remote_addr.ip4.addr != ((struct pico_ip4 *)dst)->addr ) { pico_err = PICO_ERR_EADDRNOTAVAIL; return NULL; } } else { src4 = pico_ipv4_source_find(dst); if (!src4) { pico_err = PICO_ERR_EHOSTUNREACH; return NULL; } } if (src4->addr != PICO_IPV4_INADDR_ANY) s->local_addr.ip4.addr = src4->addr; #else pico_err = PICO_ERR_EPROTONOSUPPORT; #endif return src4; } static void *pico_socket_sendto_get_ip6_src(struct pico_socket *s, struct pico_ip6 *dst) { struct pico_ip6 *src6 = NULL; (void)s; (void)dst; #ifdef PICO_SUPPORT_IPV6 /* Check if socket is connected: destination address MUST match the * current connected endpoint */ if ((s->state & PICO_SOCKET_STATE_CONNECTED)) { src6 = &s->local_addr.ip6; if (memcmp(&s->remote_addr, dst, PICO_SIZE_IP6)) { pico_err = PICO_ERR_EADDRNOTAVAIL; return NULL; } } else { src6 = pico_ipv6_source_find(dst); if (!src6) { pico_err = PICO_ERR_EHOSTUNREACH; return NULL; } if (!pico_ipv6_is_unspecified(src6->addr)) s->local_addr.ip6 = *src6; } #else pico_err = PICO_ERR_EPROTONOSUPPORT; #endif return src6; } static int pico_socket_sendto_dest_check(struct pico_socket *s, void *dst, uint16_t port) { /* For the sendto call to be valid, * dst and remote_port should be always populated. */ if (!dst || !port) { pico_err = PICO_ERR_EADDRNOTAVAIL; return -1; } /* When coming from pico_socket_send (or _write), * the destination is automatically assigned to the currently connected endpoint. * This check will ensure that there is no mismatch when sendto() is called directly * on a connected socket */ if ((s->state & PICO_SOCKET_STATE_CONNECTED) != 0) { if (port != s->remote_port) { pico_err = PICO_ERR_EINVAL; return -1; } } return 0; } static int pico_socket_sendto_initial_checks(struct pico_socket *s, const void *buf, const int len, void *dst, uint16_t remote_port) { if (len == 0) { 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; } return pico_socket_sendto_dest_check(s, dst, remote_port); } static void *pico_socket_sendto_get_src(struct pico_socket *s, void *dst) { void *src = NULL; if (is_sock_ipv4(s)) src = pico_socket_sendto_get_ip4_src(s, (struct pico_ip4 *)dst); if (is_sock_ipv6(s)) src = pico_socket_sendto_get_ip6_src(s, (struct pico_ip6 *)dst); return src; } static struct pico_remote_endpoint *pico_socket_sendto_destination_ipv4(struct pico_socket *s, struct pico_ip4 *dst, uint16_t port) { struct pico_remote_endpoint *ep = NULL; (void)s; ep = PICO_ZALLOC(sizeof(struct pico_remote_endpoint)); if (!ep) { pico_err = PICO_ERR_ENOMEM; return NULL; } ep->remote_addr.ip4.addr = ((struct pico_ip4 *)dst)->addr; ep->remote_port = port; return ep; } static void pico_endpoint_free(struct pico_remote_endpoint *ep) { if (ep) PICO_FREE(ep); } static struct pico_remote_endpoint *pico_socket_sendto_destination_ipv6(struct pico_socket *s, struct pico_ip6 *dst, uint16_t port) { struct pico_remote_endpoint *ep = NULL; (void)s; (void)dst; (void)port; #ifdef PICO_SUPPORT_IPV6 ep = PICO_ZALLOC(sizeof(struct pico_remote_endpoint)); if (!ep) { pico_err = PICO_ERR_ENOMEM; return NULL; } memcpy(&ep->remote_addr.ip6, dst, sizeof(struct pico_ip6)); ep->remote_port = port; #endif return ep; } static struct pico_remote_endpoint *pico_socket_sendto_destination(struct pico_socket *s, void *dst, uint16_t port) { struct pico_remote_endpoint *ep = NULL; (void)pico_socket_sendto_destination_ipv6; /* socket remote info could change in a consecutive call, make persistent */ # ifdef PICO_SUPPORT_UDP if (PROTO(s) == PICO_PROTO_UDP) { # ifdef PICO_SUPPORT_IPV6 if (is_sock_ipv6(s)) ep = pico_socket_sendto_destination_ipv6(s, (struct pico_ip6 *)dst, port); # endif # ifdef PICO_SUPPORT_IPV4 if (is_sock_ipv4(s)) ep = pico_socket_sendto_destination_ipv4(s, (struct pico_ip4 *)dst, port); # endif } # endif return ep; } static int32_t pico_socket_sendto_set_localport(struct pico_socket *s) { if ((s->state & PICO_SOCKET_STATE_BOUND) == 0) { s->local_port = pico_socket_high_port(s->proto->proto_number); if (s->local_port == 0) { pico_err = PICO_ERR_EINVAL; return -1; } } return s->local_port; } static int pico_socket_sendto_transport_offset(struct pico_socket *s) { int header_offset = -1; #ifdef PICO_SUPPORT_TCP if (PROTO(s) == PICO_PROTO_TCP) header_offset = pico_tcp_overhead(s); #endif #ifdef PICO_SUPPORT_UDP if (PROTO(s) == PICO_PROTO_UDP) header_offset = sizeof(struct pico_udp_hdr); #endif return header_offset; } static struct pico_remote_endpoint *pico_socket_set_info(struct pico_remote_endpoint *ep) { struct pico_remote_endpoint *info; info = PICO_ZALLOC(sizeof(struct pico_remote_endpoint)); if (!info) { pico_err = PICO_ERR_ENOMEM; return NULL; } memcpy(info, ep, sizeof(struct pico_remote_endpoint)); return info; } static void pico_xmit_frame_set_nofrag(struct pico_frame *f) { #ifdef PICO_SUPPORT_IPFRAG f->frag = short_be(PICO_IPV4_DONTFRAG); #endif } static int pico_socket_final_xmit(struct pico_socket *s, struct pico_frame *f) { if (s->proto->push(s->proto, f) > 0) { return f->payload_len; } else { pico_frame_discard(f); return 0; } } static int pico_socket_xmit_one(struct pico_socket *s, const void *buf, const int len, void *src, struct pico_remote_endpoint *ep) { struct pico_frame *f; uint16_t hdr_offset = (uint16_t)pico_socket_sendto_transport_offset(s); int ret = 0; (void)src; f = pico_socket_frame_alloc(s, (uint16_t)(len + hdr_offset)); if (!f) { pico_err = PICO_ERR_ENOMEM; return -1; } f->payload += hdr_offset; f->payload_len = (uint16_t)(len); f->sock = s; transport_flags_update(f, s); pico_xmit_frame_set_nofrag(f); if (ep) { f->info = pico_socket_set_info(ep); if (!f->info) { pico_frame_discard(f); return -1; } } memcpy(f->payload, (const uint8_t *)buf, f->payload_len); /* dbg("Pushing segment, hdr len: %d, payload_len: %d\n", header_offset, f->payload_len); */ ret = pico_socket_final_xmit(s, f); return ret; } static int pico_socket_xmit_avail_space(struct pico_socket *s); static void pico_socket_xmit_first_fragment_setup(struct pico_frame *f, int space, int hdr_offset) { frag_dbg("FRAG: first fragmented frame %p | len = %u offset = 0\n", f, f->payload_len); /* transport header length field contains total length + header length */ f->transport_len = (uint16_t)(space); #ifdef PICO_SUPPORT_IPFRAG f->frag = short_be(PICO_IPV4_MOREFRAG); #endif f->payload += hdr_offset; f->payload_len = (uint16_t) space; } static void pico_socket_xmit_next_fragment_setup(struct pico_frame *f, int hdr_offset, int total_payload_written, int len) { /* no transport header in fragmented IP */ f->payload = f->transport_hdr; f->payload_len = (uint16_t)(f->payload_len + hdr_offset); /* set offset in octets */ #ifdef PICO_SUPPORT_IPFRAG f->frag = short_be((uint16_t)((uint16_t)(total_payload_written + (uint16_t)hdr_offset) >> 3u)); #endif if (total_payload_written + f->payload_len < len) { frag_dbg("FRAG: intermediate fragmented frame %p | len = %u offset = %u\n", f, f->payload_len, short_be(f->frag)); #ifdef PICO_SUPPORT_IPFRAG f->frag |= short_be(PICO_IPV4_MOREFRAG); #endif } else { frag_dbg("FRAG: last fragmented frame %p | len = %u offset = %u\n", f, f->payload_len, short_be(f->frag)); #ifdef PICO_SUPPORT_IPFRAG f->frag &= short_be(PICO_IPV4_FRAG_MASK); #endif } } static int pico_socket_xmit_fragments(struct pico_socket *s, const void *buf, const int len, void *src, struct pico_remote_endpoint *ep) { int space = pico_socket_xmit_avail_space(s); int hdr_offset = pico_socket_sendto_transport_offset(s); int total_payload_written = 0; struct pico_frame *f = NULL; if (space > len) { return pico_socket_xmit_one(s, buf, len, src, ep); } #ifdef PICO_SUPPORT_IPFRAG while(total_payload_written < len) { /* Always allocate the max space available: space + offset */ if (len < space) space = len; f = pico_socket_frame_alloc(s, (uint16_t)(space + hdr_offset)); if (!f) { pico_err = PICO_ERR_ENOMEM; pico_endpoint_free(ep); return -1; } f->sock = s; if (ep) { f->info = pico_socket_set_info(ep); if (!f->info) { pico_frame_discard(f); pico_endpoint_free(ep); return -1; } } if (!total_payload_written == 0) { /* First fragment: no payload written yet! */ pico_socket_xmit_first_fragment_setup(f, space, hdr_offset); } else { /* Next fragment */ pico_socket_xmit_next_fragment_setup(f, hdr_offset, total_payload_written, len); } memcpy(f->payload, (const uint8_t *)buf + total_payload_written, f->payload_len); transport_flags_update(f, s); if (s->proto->push(s->proto, f) > 0) { total_payload_written += f->payload_len; } else { pico_frame_discard(f); break; } } /* while() */ pico_endpoint_free(ep); return total_payload_written; #else /* Careful with that axe, Eugene! * * cropping down datagrams to the MTU value. */ (void) f; (void) hdr_offset; (void) total_payload_written; return pico_socket_xmit_one(s, buf, space, src, ep); #endif } uint16_t pico_socket_get_mtu(struct pico_socket *s) { (void)s; #ifdef PICO_SUPPORT_IPV6 if (is_sock_ipv6(s)) return PICO_SOCKET6_MTU; #endif return PICO_SOCKET4_MTU; } static int pico_socket_xmit_avail_space(struct pico_socket *s) { int transport_len; int header_offset; #ifdef PICO_SUPPORT_TCP if (PROTO(s) == PICO_PROTO_TCP) { transport_len = pico_tcp_get_socket_mtu(s); } else #endif transport_len = pico_socket_get_mtu(s); header_offset = pico_socket_sendto_transport_offset(s); if (header_offset < 0) { pico_err = PICO_ERR_EPROTONOSUPPORT; return -1; } transport_len -= pico_socket_sendto_transport_offset(s); return transport_len; } static int pico_socket_xmit(struct pico_socket *s, const void *buf, const int len, void *src, struct pico_remote_endpoint *ep) { int space = pico_socket_xmit_avail_space(s); int total_payload_written = 0; if (space < 0) { pico_err = PICO_ERR_EPROTONOSUPPORT; return -1; } if ((PROTO(s) == PICO_PROTO_UDP) && (len > space)) { return pico_socket_xmit_fragments(s, buf, len, src, ep); } while (total_payload_written < len) { int w, chunk_len = len - total_payload_written; if (chunk_len > space) chunk_len = space; w = pico_socket_xmit_one(s, (const void *)((const uint8_t *)buf + total_payload_written), chunk_len, src, ep); if (w <= 0) { break; } total_payload_written += w; if (PROTO(s) == PICO_PROTO_UDP) { /* Break after the first datagram sent with at most MTU bytes. */ break; } } pico_endpoint_free(ep); return total_payload_written; } static void pico_socket_sendto_set_dport(struct pico_socket *s, uint16_t port) { if ((s->state & PICO_SOCKET_STATE_CONNECTED) == 0) { s->remote_port = port; } } int pico_socket_sendto(struct pico_socket *s, const void *buf, const int len, void *dst, uint16_t remote_port) { struct pico_remote_endpoint *remote_endpoint = NULL; void *src = NULL; if (pico_socket_sendto_initial_checks(s, buf, len, dst, remote_port) < 0) return -1; src = pico_socket_sendto_get_src(s, dst); if (!src) { return -1; } remote_endpoint = pico_socket_sendto_destination(s, dst, remote_port); if (pico_socket_sendto_set_localport(s) < 0) return -1; pico_socket_sendto_set_dport(s, remote_port); return pico_socket_xmit(s, buf, len, src, remote_endpoint); } int pico_socket_send(struct pico_socket *s, const void *buf, int len) { if (!s || buf == NULL) { pico_err = PICO_ERR_EINVAL; return -1; } else { /* check if exists in tree */ /* See task #178 */ if (pico_check_socket(s) != 0) { pico_err = PICO_ERR_EINVAL; return -1; } } if ((s->state & PICO_SOCKET_STATE_CONNECTED) == 0) { pico_err = PICO_ERR_ENOTCONN; return -1; } return pico_socket_sendto(s, buf, len, &s->remote_addr, s->remote_port); } int pico_socket_recvfrom(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, (uint16_t)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 (int)pico_tcp_read(s, buf, (uint32_t)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_getname(struct pico_socket *s, void *local_addr, uint16_t *port, uint16_t *proto) { if (!s || !local_addr || !port || !proto) { pico_err = PICO_ERR_EINVAL; return -1; } if (is_sock_ipv4(s)) { #ifdef PICO_SUPPORT_IPV4 struct pico_ip4 *ip = (struct pico_ip4 *)local_addr; ip->addr = s->local_addr.ip4.addr; *proto = PICO_PROTO_IPV4; #endif } else if (is_sock_ipv6(s)) { #ifdef PICO_SUPPORT_IPV6 struct pico_ip6 *ip = (struct pico_ip6 *)local_addr; memcpy(ip->addr, s->local_addr.ip6.addr, PICO_SIZE_IP6); *proto = PICO_PROTO_IPV6; #endif } else { pico_err = PICO_ERR_EINVAL; return -1; } *port = s->local_port; return 0; } int pico_socket_bind(struct pico_socket *s, void *local_addr, uint16_t *port) { if (!s || !local_addr || !port) { pico_err = PICO_ERR_EINVAL; return -1; } if (is_sock_ipv4(s)) { #ifdef PICO_SUPPORT_IPV4 struct pico_ip4 *ip = (struct pico_ip4 *)local_addr; if (ip->addr != PICO_IPV4_INADDR_ANY) { if (!pico_ipv4_link_find(local_addr)) { pico_err = PICO_ERR_EINVAL; return -1; } } #endif } else if (is_sock_ipv6(s)) { #ifdef PICO_SUPPORT_IPV6 struct pico_ip6 *ip = (struct pico_ip6 *)local_addr; if (!pico_ipv6_is_unspecified(ip->addr)) { if (!pico_ipv6_link_find(local_addr)) { pico_err = PICO_ERR_EINVAL; return -1; } } #endif } else { pico_err = PICO_ERR_EINVAL; return -1; } /* When given port = 0, get a random high port to bind to. */ if (*port == 0) { *port = pico_socket_high_port(PROTO(s)); if (*port == 0) { pico_err = PICO_ERR_EINVAL; return -1; } } if (pico_is_port_free(PROTO(s), *port, local_addr, s->net) == 0) { pico_err = PICO_ERR_EADDRINUSE; return -1; } s->local_port = *port; if (is_sock_ipv4(s)) { #ifdef PICO_SUPPORT_IPV4 struct pico_ip4 *ip = (struct pico_ip4 *)local_addr; s->local_addr.ip4 = *ip; #endif } else if (is_sock_ipv6(s)) { #ifdef PICO_SUPPORT_IPV6 struct pico_ip6 *ip = (struct pico_ip6 *)local_addr; s->local_addr.ip6 = *ip; #endif } else { pico_err = PICO_ERR_EINVAL; return -1; } return pico_socket_alter_state(s, PICO_SOCKET_STATE_BOUND, 0, 0); } int pico_socket_connect(struct pico_socket *s, const void *remote_addr, uint16_t remote_port) { int ret = -1; pico_err = PICO_ERR_EPROTONOSUPPORT; if (!s || remote_addr == NULL || remote_port == 0) { pico_err = PICO_ERR_EINVAL; return -1; } s->remote_port = remote_port; if (s->local_port == 0) { s->local_port = pico_socket_high_port(PROTO(s)); if (!s->local_port) { pico_err = PICO_ERR_EINVAL; return -1; } } if (is_sock_ipv4(s)) { #ifdef PICO_SUPPORT_IPV4 struct pico_ip4 *local = NULL; const struct pico_ip4 *ip = (const struct pico_ip4 *)remote_addr; s->remote_addr.ip4 = *ip; local = pico_ipv4_source_find(ip); if (local) { s->local_addr.ip4 = *local; } else { pico_err = PICO_ERR_EHOSTUNREACH; return -1; } #endif } else if (is_sock_ipv6(s)) { #ifdef PICO_SUPPORT_IPV6 struct pico_ip6 *local = NULL; const struct pico_ip6 *ip = (const struct pico_ip6 *)remote_addr; s->remote_addr.ip6 = *ip; local = pico_ipv6_source_find(ip); if (local) { s->local_addr.ip6 = *local; } else { pico_err = PICO_ERR_EHOSTUNREACH; return -1; } #endif } else { pico_err = PICO_ERR_EINVAL; return -1; } pico_socket_alter_state(s, PICO_SOCKET_STATE_BOUND, 0, 0); #ifdef PICO_SUPPORT_UDP if (PROTO(s) == PICO_PROTO_UDP) { pico_socket_alter_state(s, PICO_SOCKET_STATE_CONNECTED, 0, 0); pico_err = PICO_ERR_NOERR; ret = 0; } #endif #ifdef PICO_SUPPORT_TCP if (PROTO(s) == PICO_PROTO_TCP) { if (pico_tcp_initconn(s) == 0) { pico_socket_alter_state(s, PICO_SOCKET_STATE_CONNECTED | PICO_SOCKET_STATE_TCP_SYN_SENT, 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 (PROTO(s) == PICO_PROTO_TCP) pico_socket_alter_state(s, PICO_SOCKET_STATE_TCP_SYN_SENT, 0, PICO_SOCKET_STATE_TCP_LISTEN); s->max_backlog = (uint16_t)backlog; return 0; } struct pico_socket *pico_socket_accept(struct pico_socket *s, void *orig, uint16_t *port) { if (!s || !orig || !port) { pico_err = PICO_ERR_EINVAL; return NULL; } pico_err = PICO_ERR_EINVAL; if ((s->state & PICO_SOCKET_STATE_BOUND) == 0) { return NULL; } if (PROTO(s) == PICO_PROTO_UDP) { return NULL; } if (TCPSTATE(s) == PICO_SOCKET_STATE_TCP_LISTEN) { struct pico_sockport *sp = pico_get_sockport(PICO_PROTO_TCP, s->local_port); struct pico_socket *found; uint32_t socklen = sizeof(struct pico_ip4); /* If at this point no incoming connection socket is found, * the accept call is valid, but no connection is established yet. */ pico_err = PICO_ERR_EAGAIN; if (sp) { struct pico_tree_node *index; /* RB_FOREACH(found, socket_tree, &sp->socks) { */ pico_tree_foreach(index, &sp->socks){ found = index->keyValue; if ((s == found->parent) && ((found->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_ESTABLISHED)) { found->parent = NULL; pico_err = PICO_ERR_NOERR; #ifdef PICO_SUPPORT_IPV6 if (is_sock_ipv6(s)) socklen = sizeof(struct pico_ip6); #endif memcpy(orig, &found->remote_addr, socklen); *port = found->remote_port; s->number_of_pending_conn--; return found; } } } } return NULL; } #else int pico_socket_listen(struct pico_socket *s, int backlog) { IGNORE_PARAMETER(s); IGNORE_PARAMETER(backlog); pico_err = PICO_ERR_EINVAL; return -1; } struct pico_socket *pico_socket_accept(struct pico_socket *s, void *orig, uint16_t *local_port) { IGNORE_PARAMETER(s); IGNORE_PARAMETER(orig); IGNORE_PARAMETER(local_port); pico_err = PICO_ERR_EINVAL; return NULL; } #endif int pico_socket_setoption(struct pico_socket *s, int option, void *value) { if (s == NULL) { pico_err = PICO_ERR_EINVAL; return -1; } if (PROTO(s) == PICO_PROTO_TCP) return pico_setsockopt_tcp(s, option, value); if (PROTO(s) == PICO_PROTO_UDP) return pico_setsockopt_udp(s, option, value); pico_err = PICO_ERR_EPROTONOSUPPORT; return -1; } int pico_socket_getoption(struct pico_socket *s, int option, void *value) { if (s == NULL) { pico_err = PICO_ERR_EINVAL; return -1; } if (PROTO(s) == PICO_PROTO_TCP) return pico_getsockopt_tcp(s, option, value); if (PROTO(s) == PICO_PROTO_UDP) return pico_getsockopt_udp(s, option, value); pico_err = PICO_ERR_EPROTONOSUPPORT; return -1; } int pico_socket_shutdown(struct pico_socket *s, int mode) { if (!s) { pico_err = PICO_ERR_EINVAL; return -1; } /* Check if the socket has already been closed */ if (s->state & PICO_SOCKET_STATE_CLOSED) { pico_err = PICO_ERR_EINVAL; return -1; } #ifdef PICO_SUPPORT_UDP if (PROTO(s) == PICO_PROTO_UDP) { if (mode & PICO_SHUT_RDWR) pico_socket_alter_state(s, PICO_SOCKET_STATE_CLOSED, PICO_SOCKET_STATE_CLOSING | PICO_SOCKET_STATE_BOUND | PICO_SOCKET_STATE_CONNECTED, 0); else if (mode & PICO_SHUT_RD) pico_socket_alter_state(s, 0, PICO_SOCKET_STATE_BOUND, 0); } #endif #ifdef PICO_SUPPORT_TCP if (PROTO(s) == PICO_PROTO_TCP) { if(mode & PICO_SHUT_RDWR) { pico_socket_alter_state(s, PICO_SOCKET_STATE_SHUT_LOCAL | PICO_SOCKET_STATE_SHUT_REMOTE, 0, 0); pico_tcp_notify_closing(s); } else if (mode & PICO_SHUT_WR) pico_socket_alter_state(s, PICO_SOCKET_STATE_SHUT_LOCAL, 0, 0); else if (mode & PICO_SHUT_RD) pico_socket_alter_state(s, PICO_SOCKET_STATE_SHUT_REMOTE, 0, 0); } #endif return 0; } int pico_socket_close(struct pico_socket *s) { if (!s) return -1; #ifdef PICO_SUPPORT_TCP if (PROTO(s) == PICO_PROTO_TCP) { if (pico_tcp_check_listen_close(s) == 0) return 0; } #endif return pico_socket_shutdown(s, PICO_SHUT_RDWR); } #ifdef PICO_SUPPORT_CRC static inline int pico_transport_crc_check(struct pico_frame *f) { struct pico_ipv4_hdr *net_hdr = (struct pico_ipv4_hdr *) f->net_hdr; struct pico_udp_hdr *udp_hdr = NULL; uint16_t checksum_invalid = 1; switch (net_hdr->proto) { case PICO_PROTO_TCP: checksum_invalid = short_be(pico_tcp_checksum(f)); /* dbg("TCP CRC validation == %u\n", checksum_invalid); */ if (checksum_invalid) { dbg("TCP CRC: validation failed!\n"); pico_frame_discard(f); return 0; } break; case PICO_PROTO_UDP: udp_hdr = (struct pico_udp_hdr *) f->transport_hdr; if (short_be(udp_hdr->crc)) { #ifdef PICO_SUPPORT_IPV4 if (IS_IPV4(f)) checksum_invalid = short_be(pico_udp_checksum_ipv4(f)); #endif #ifdef PICO_SUPPORT_IPV6 if (IS_IPV6(f)) checksum_invalid = short_be(pico_udp_checksum_ipv6(f)); #endif /* dbg("UDP CRC validation == %u\n", checksum_invalid); */ if (checksum_invalid) { /* dbg("UDP CRC: validation failed!\n"); */ pico_frame_discard(f); return 0; } } break; default: /* Do nothing */ break; } return 1; } #else static inline int pico_transport_crc_check(struct pico_frame *f) { IGNORE_PARAMETER(f); return 1; } #endif /* PICO_SUPPORT_CRC */ int pico_transport_process_in(struct pico_protocol *self, struct pico_frame *f) { struct pico_trans *hdr = (struct pico_trans *) f->transport_hdr; int ret = 0; if (!hdr) { pico_err = PICO_ERR_EFAULT; return -1; } ret = pico_transport_crc_check(f); if (ret < 1) return ret; else ret = 0; if ((hdr) && (pico_socket_deliver(self, f, hdr->dport) == 0)) return ret; if (!IS_BCAST(f)) { dbg("Socket not found... \n"); pico_notify_socket_unreachable(f); ret = -1; pico_err = PICO_ERR_ENOENT; } pico_frame_discard(f); return ret; } #define SL_LOOP_MIN 1 #ifdef PICO_SUPPORT_TCP static int checkSocketSanity(struct pico_socket *s) { /* checking for pending connections */ if(TCP_STATE(s) == PICO_SOCKET_STATE_TCP_SYN_RECV) { if((PICO_TIME_MS() - s->timestamp) >= PICO_SOCKET_BOUND_TIMEOUT) return -1; } if((PICO_TIME_MS() - s->timestamp) >= PICO_SOCKET_TIMEOUT) { /* checking for hanging sockets */ if((TCP_STATE(s) != PICO_SOCKET_STATE_TCP_LISTEN) && (TCP_STATE(s) != PICO_SOCKET_STATE_TCP_ESTABLISHED)) return -1; } return 0; } #endif static int pico_sockets_loop_udp(int loop_score) { #ifdef PICO_SUPPORT_UDP static struct pico_tree_node *index_udp; struct pico_sockport *start; struct pico_socket *s; struct pico_frame *f; if (sp_udp == NULL) { index_udp = pico_tree_firstNode(UDPTable.root); sp_udp = index_udp->keyValue; } /* init start node */ start = sp_udp; /* round-robin all transport protocols, break if traversed all protocols */ while (loop_score > SL_LOOP_MIN && sp_udp != NULL) { struct pico_tree_node *index; pico_tree_foreach(index, &sp_udp->socks){ s = index->keyValue; f = pico_dequeue(&s->q_out); while (f && (loop_score > 0)) { pico_proto_udp.push(&pico_proto_udp, f); loop_score -= 1; f = pico_dequeue(&s->q_out); } } index_udp = pico_tree_next(index_udp); sp_udp = index_udp->keyValue; if (sp_udp == NULL) { index_udp = pico_tree_firstNode(UDPTable.root); sp_udp = index_udp->keyValue; } if (sp_udp == start) break; } #endif return loop_score; } static int pico_sockets_loop_tcp(int loop_score) { #ifdef PICO_SUPPORT_TCP struct pico_sockport *start; struct pico_socket *s; static struct pico_tree_node *index_tcp; if (sp_tcp == NULL) { index_tcp = pico_tree_firstNode(TCPTable.root); sp_tcp = index_tcp->keyValue; } /* init start node */ start = sp_tcp; while (loop_score > SL_LOOP_MIN && sp_tcp != NULL) { struct pico_tree_node *index = NULL, *safe_index = NULL; pico_tree_foreach_safe(index, &sp_tcp->socks, safe_index){ s = index->keyValue; loop_score = pico_tcp_output(s, loop_score); if ((s->ev_pending) && s->wakeup) { s->wakeup(s->ev_pending, s); if(!s->parent) s->ev_pending = 0; } if (loop_score <= 0) { loop_score = 0; break; } if(checkSocketSanity(s) < 0) { pico_socket_del(s); index_tcp = NULL; /* forcing the restart of loop */ sp_tcp = NULL; break; } } /* check if RB_FOREACH ended, if not, break to keep the cur sp_tcp */ if (!index_tcp || (index && index->keyValue)) break; index_tcp = pico_tree_next(index_tcp); sp_tcp = index_tcp->keyValue; if (sp_tcp == NULL) { index_tcp = pico_tree_firstNode(TCPTable.root); sp_tcp = index_tcp->keyValue; } if (sp_tcp == start) break; } #endif return loop_score; } int pico_sockets_loop(int loop_score) { loop_score = pico_sockets_loop_udp(loop_score); loop_score = pico_sockets_loop_tcp(loop_score); return loop_score; } struct pico_frame *pico_socket_frame_alloc(struct pico_socket *s, uint16_t len) { struct pico_frame *f = NULL; #ifdef PICO_SUPPORT_IPV6 if (is_sock_ipv6(s)) f = pico_proto_ipv6.alloc(&pico_proto_ipv6, len); #endif #ifdef PICO_SUPPORT_IPV4 if (is_sock_ipv4(s)) f = pico_proto_ipv4.alloc(&pico_proto_ipv4, len); #endif if (!f) { pico_err = PICO_ERR_ENOMEM; return f; } f->payload = f->transport_hdr; f->payload_len = len; f->sock = s; return f; } static void pico_transport_error_set_picoerr(int code) { /* dbg("SOCKET ERROR FROM ICMP NOTIFICATION. (icmp code= %d)\n\n", code); */ switch(code) { case PICO_ICMP_UNREACH_NET: pico_err = PICO_ERR_ENETUNREACH; break; case PICO_ICMP_UNREACH_HOST: pico_err = PICO_ERR_EHOSTUNREACH; break; case PICO_ICMP_UNREACH_PROTOCOL: pico_err = PICO_ERR_ENOPROTOOPT; break; case PICO_ICMP_UNREACH_PORT: pico_err = PICO_ERR_ECONNREFUSED; break; case PICO_ICMP_UNREACH_NET_UNKNOWN: pico_err = PICO_ERR_ENETUNREACH; break; case PICO_ICMP_UNREACH_HOST_UNKNOWN: pico_err = PICO_ERR_EHOSTDOWN; break; case PICO_ICMP_UNREACH_ISOLATED: pico_err = PICO_ERR_ENONET; break; case PICO_ICMP_UNREACH_NET_PROHIB: case PICO_ICMP_UNREACH_HOST_PROHIB: pico_err = PICO_ERR_EHOSTUNREACH; break; default: pico_err = PICO_ERR_EOPNOTSUPP; } } int pico_transport_error(struct pico_frame *f, uint8_t proto, int code) { int ret = -1; struct pico_trans *trans = (struct pico_trans*) f->transport_hdr; struct pico_sockport *port = NULL; struct pico_socket *s = NULL; switch (proto) { #ifdef PICO_SUPPORT_UDP case PICO_PROTO_UDP: port = pico_get_sockport(proto, trans->sport); break; #endif #ifdef PICO_SUPPORT_TCP case PICO_PROTO_TCP: port = pico_get_sockport(proto, trans->sport); break; #endif default: /* Protocol not available */ ret = -1; } if (port) { struct pico_tree_node *index; ret = 0; pico_tree_foreach(index, &port->socks) { s = index->keyValue; if (trans->dport == s->remote_port) { if (s->wakeup) { pico_transport_error_set_picoerr(code); s->state |= PICO_SOCKET_STATE_SHUT_REMOTE; s->wakeup(PICO_SOCK_EV_ERR, s); } break; } } } pico_frame_discard(f); return ret; } #endif #endif