273 lines
7.4 KiB
C
273 lines
7.4 KiB
C
#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
|
|
}
|