#include "pico_config.h" #include "pico_socket.h" #include "pico_ipv4.h" #include "pico_ipv6.h" #include "pico_tcp.h" #include "pico_socket_tcp.h" static int sockopt_validate_args(struct pico_socket *s, void *value) { if (!value) { pico_err = PICO_ERR_EINVAL; return -1; } if (s->proto->proto_number != PICO_PROTO_TCP) { pico_err = PICO_ERR_EPROTONOSUPPORT; return -1; } return 0; } int pico_getsockopt_tcp(struct pico_socket *s, int option, void *value) { if (sockopt_validate_args(s, value) < 0) return -1; #ifdef PICO_SUPPORT_TCP if (option == PICO_TCP_NODELAY) { /* state of the NODELAY option */ *(int *)value = PICO_SOCKET_GETOPT(s, PICO_SOCKET_OPT_TCPNODELAY); return 0; } else if (option == PICO_SOCKET_OPT_RCVBUF) { return pico_tcp_get_bufsize_in(s, (uint32_t *)value); } else if (option == PICO_SOCKET_OPT_SNDBUF) { return pico_tcp_get_bufsize_out(s, (uint32_t *)value); } #endif return -1; } static void tcp_set_nagle_option(struct pico_socket *s, void *value) { int *val = (int*)value; if (*val > 0) { dbg("setsockopt: Nagle algorithm disabled.\n"); PICO_SOCKET_SETOPT_EN(s, PICO_SOCKET_OPT_TCPNODELAY); } else { dbg("setsockopt: Nagle algorithm enabled.\n"); PICO_SOCKET_SETOPT_DIS(s, PICO_SOCKET_OPT_TCPNODELAY); } } int pico_setsockopt_tcp(struct pico_socket *s, int option, void *value) { if (sockopt_validate_args(s, value) < 0) return -1; #ifdef PICO_SUPPORT_TCP if (option == PICO_TCP_NODELAY) { tcp_set_nagle_option(s, value); return 0; } else if (option == PICO_SOCKET_OPT_RCVBUF) { uint32_t *val = (uint32_t*)value; pico_tcp_set_bufsize_in(s, *val); return 0; } else if (option == PICO_SOCKET_OPT_SNDBUF) { uint32_t *val = (uint32_t*)value; pico_tcp_set_bufsize_out(s, *val); return 0; } else if (option == PICO_SOCKET_OPT_KEEPCNT) { uint32_t *val = (uint32_t*)value; pico_tcp_set_keepalive_probes(s, *val); return 0; } else if (option == PICO_SOCKET_OPT_KEEPIDLE) { uint32_t *val = (uint32_t*)value; pico_tcp_set_keepalive_time(s, *val); return 0; } else if (option == PICO_SOCKET_OPT_KEEPINTVL) { uint32_t *val = (uint32_t*)value; pico_tcp_set_keepalive_intvl(s, *val); return 0; } else if (option == PICO_SOCKET_OPT_LINGER) { uint32_t *val = (uint32_t*)value; pico_tcp_set_linger(s, *val); return 0; } #endif pico_err = PICO_ERR_EINVAL; return -1; } void pico_socket_tcp_cleanup(struct pico_socket *sock) { #ifdef PICO_SUPPORT_TCP /* for tcp sockets go further and clean the sockets inside queue */ if(is_sock_tcp(sock)) pico_tcp_cleanup_queues(sock); #endif } void pico_socket_tcp_delete(struct pico_socket *s) { #ifdef PICO_SUPPORT_TCP if(s->parent) s->parent->number_of_pending_conn--; #endif } static struct pico_socket *socket_tcp_deliver_ipv4(struct pico_socket *s, struct pico_frame *f) { struct pico_socket *found = NULL; #ifdef PICO_SUPPORT_IPV4 struct pico_ip4 s_local, s_remote, p_src, p_dst; struct pico_ipv4_hdr *ip4hdr = (struct pico_ipv4_hdr*)(f->net_hdr); struct pico_trans *tr = (struct pico_trans *) f->transport_hdr; s_local.addr = s->local_addr.ip4.addr; s_remote.addr = s->remote_addr.ip4.addr; p_src.addr = ip4hdr->src.addr; p_dst.addr = ip4hdr->dst.addr; if ((s->remote_port == tr->sport) && /* remote port check */ (s_remote.addr == p_src.addr) && /* remote addr check */ ((s_local.addr == PICO_IPV4_INADDR_ANY) || (s_local.addr == p_dst.addr))) { /* Either local socket is ANY, or matches dst */ found = s; return found; } else if ((s->remote_port == 0) && /* not connected... listening */ ((s_local.addr == PICO_IPV4_INADDR_ANY) || (s_local.addr == p_dst.addr))) { /* Either local socket is ANY, or matches dst */ /* listen socket */ found = s; } #endif return found; } static struct pico_socket *socket_tcp_deliver_ipv6(struct pico_socket *s, struct pico_frame *f) { struct pico_socket *found = NULL; #ifdef PICO_SUPPORT_IPV6 struct pico_trans *tr = (struct pico_trans *) f->transport_hdr; struct pico_ip6 s_local = {{0}}, s_remote = {{0}}, p_src = {{0}}, p_dst = {{0}}; struct pico_ipv6_hdr *ip6hdr = (struct pico_ipv6_hdr *)(f->net_hdr); s_local = s->local_addr.ip6; s_remote = s->remote_addr.ip6; p_src = ip6hdr->src; p_dst = ip6hdr->dst; if ((s->remote_port == tr->sport) && (!memcmp(s_remote.addr, p_src.addr, PICO_SIZE_IP6)) && ((!memcmp(s_local.addr, PICO_IP6_ANY, PICO_SIZE_IP6)) || (!memcmp(s_local.addr, p_dst.addr, PICO_SIZE_IP6)))) { found = s; return found; } else if ((s->remote_port == 0) && /* not connected... listening */ ((!memcmp(s_local.addr, PICO_IP6_ANY, PICO_SIZE_IP6)) || (!memcmp(s_local.addr, p_dst.addr, PICO_SIZE_IP6)))) { /* listen socket */ found = s; } #else (void) s; (void) f; #endif return found; } static int socket_tcp_do_deliver(struct pico_socket *s, struct pico_frame *f) { if (s != NULL) { pico_tcp_input(s, f); if ((s->ev_pending) && s->wakeup) { s->wakeup(s->ev_pending, s); if(!s->parent) s->ev_pending = 0; } return 0; } dbg("TCP SOCKET> Not s.\n"); return -1; } int pico_socket_tcp_deliver(struct pico_sockport *sp, struct pico_frame *f) { struct pico_socket *found = NULL; struct pico_socket *target = NULL; struct pico_tree_node *index = NULL; struct pico_tree_node *_tmp; struct pico_socket *s = NULL; pico_tree_foreach_safe(index, &sp->socks, _tmp){ s = index->keyValue; /* 4-tuple identification of socket (port-IP) */ if (IS_IPV4(f)) { found = socket_tcp_deliver_ipv4(s, f); } if (IS_IPV6(f)) { found = socket_tcp_deliver_ipv6(s, f); } if (found) { target = found; if ( found->remote_port != 0) /* only break if it's connected */ break; } } /* FOREACH */ return socket_tcp_do_deliver(target, f); } struct pico_socket *pico_socket_tcp_open(uint16_t family) { struct pico_socket *s = NULL; (void) family; #ifdef PICO_SUPPORT_TCP s = pico_tcp_open(family); if (!s) { pico_err = PICO_ERR_ENOMEM; return NULL; } s->proto = &pico_proto_tcp; /*check if Nagle enabled */ /* if (!IS_NAGLE_ENABLED(s)) dbg("ERROR Nagle should be enabled here\n\n"); */ #endif return s; } int pico_socket_tcp_read(struct pico_socket *s, void *buf, uint32_t len) { #ifdef PICO_SUPPORT_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 (int)(pico_tcp_read(s, buf, (uint32_t)len)); } #else return 0; #endif } void transport_flags_update(struct pico_frame *f, struct pico_socket *s) { #ifdef PICO_SUPPORT_TCP if(is_sock_tcp(s)) pico_tcp_flags_update(f, s); #endif }