1#include "pico_config.h"
2#include "pico_socket.h"
3#include "pico_ipv4.h"
4#include "pico_ipv6.h"
5#include "pico_tcp.h"
6#include "pico_socket_tcp.h"
7
8
9static int sockopt_validate_args(struct pico_socket *s,  void *value)
10{
11    if (!value) {
12        pico_err = PICO_ERR_EINVAL;
13        return -1;
14    }
15
16    if (s->proto->proto_number != PICO_PROTO_TCP) {
17        pico_err = PICO_ERR_EPROTONOSUPPORT;
18        return -1;
19    }
20
21    return 0;
22}
23
24int pico_getsockopt_tcp(struct pico_socket *s, int option, void *value)
25{
26    if (sockopt_validate_args(s, value) < 0)
27        return -1;
28
29#ifdef PICO_SUPPORT_TCP
30    if (option == PICO_TCP_NODELAY) {
31        /* state of the NODELAY option */
32        *(int *)value = PICO_SOCKET_GETOPT(s, PICO_SOCKET_OPT_TCPNODELAY);
33        return 0;
34    }
35    else if (option == PICO_SOCKET_OPT_RCVBUF) {
36        return pico_tcp_get_bufsize_in(s, (uint32_t *)value);
37    }
38
39    else if (option == PICO_SOCKET_OPT_SNDBUF) {
40        return pico_tcp_get_bufsize_out(s, (uint32_t *)value);
41    }
42
43#endif
44    return -1;
45}
46
47static void tcp_set_nagle_option(struct pico_socket *s, void *value)
48{
49    int *val = (int*)value;
50    if (*val > 0) {
51        dbg("setsockopt: Nagle algorithm disabled.\n");
52        PICO_SOCKET_SETOPT_EN(s, PICO_SOCKET_OPT_TCPNODELAY);
53    } else {
54        dbg("setsockopt: Nagle algorithm enabled.\n");
55        PICO_SOCKET_SETOPT_DIS(s, PICO_SOCKET_OPT_TCPNODELAY);
56    }
57}
58
59int pico_setsockopt_tcp(struct pico_socket *s, int option, void *value)
60{
61    if (sockopt_validate_args(s, value) < 0)
62        return -1;
63
64#ifdef PICO_SUPPORT_TCP
65    if (option ==  PICO_TCP_NODELAY) {
66        tcp_set_nagle_option(s, value);
67        return 0;
68    }
69    else if (option == PICO_SOCKET_OPT_RCVBUF) {
70        uint32_t *val = (uint32_t*)value;
71        pico_tcp_set_bufsize_in(s, *val);
72        return 0;
73    }
74    else if (option == PICO_SOCKET_OPT_SNDBUF) {
75        uint32_t *val = (uint32_t*)value;
76        pico_tcp_set_bufsize_out(s, *val);
77        return 0;
78    }
79    else if (option == PICO_SOCKET_OPT_KEEPCNT) {
80        uint32_t *val = (uint32_t*)value;
81        pico_tcp_set_keepalive_probes(s, *val);
82        return 0;
83    }
84    else if (option == PICO_SOCKET_OPT_KEEPIDLE) {
85        uint32_t *val = (uint32_t*)value;
86        pico_tcp_set_keepalive_time(s, *val);
87        return 0;
88    }
89    else if (option == PICO_SOCKET_OPT_KEEPINTVL) {
90        uint32_t *val = (uint32_t*)value;
91        pico_tcp_set_keepalive_intvl(s, *val);
92        return 0;
93    }
94    else if (option == PICO_SOCKET_OPT_LINGER) {
95        uint32_t *val = (uint32_t*)value;
96        pico_tcp_set_linger(s, *val);
97        return 0;
98    }
99
100#endif
101    pico_err = PICO_ERR_EINVAL;
102    return -1;
103}
104
105void pico_socket_tcp_cleanup(struct pico_socket *sock)
106{
107#ifdef PICO_SUPPORT_TCP
108    /* for tcp sockets go further and clean the sockets inside queue */
109    if(is_sock_tcp(sock))
110        pico_tcp_cleanup_queues(sock);
111
112#endif
113}
114
115
116void pico_socket_tcp_delete(struct pico_socket *s)
117{
118#ifdef PICO_SUPPORT_TCP
119    if(s->parent)
120        s->parent->number_of_pending_conn--;
121
122#endif
123}
124
125static struct pico_socket *socket_tcp_deliver_ipv4(struct pico_socket *s, struct pico_frame *f)
126{
127    struct pico_socket *found = NULL;
128    #ifdef PICO_SUPPORT_IPV4
129    struct pico_ip4 s_local, s_remote, p_src, p_dst;
130    struct pico_ipv4_hdr *ip4hdr = (struct pico_ipv4_hdr*)(f->net_hdr);
131    struct pico_trans *tr = (struct pico_trans *) f->transport_hdr;
132    s_local.addr = s->local_addr.ip4.addr;
133    s_remote.addr = s->remote_addr.ip4.addr;
134    p_src.addr = ip4hdr->src.addr;
135    p_dst.addr = ip4hdr->dst.addr;
136    if ((s->remote_port == tr->sport) && /* remote port check */
137        (s_remote.addr == p_src.addr) && /* remote addr check */
138        ((s_local.addr == PICO_IPV4_INADDR_ANY) || (s_local.addr == p_dst.addr))) { /* Either local socket is ANY, or matches dst */
139        found = s;
140        return found;
141    } else if ((s->remote_port == 0)  && /* not connected... listening */
142               ((s_local.addr == PICO_IPV4_INADDR_ANY) || (s_local.addr == p_dst.addr))) { /* Either local socket is ANY, or matches dst */
143        /* listen socket */
144        found = s;
145    }
146
147    #endif
148    return found;
149}
150
151static struct pico_socket *socket_tcp_deliver_ipv6(struct pico_socket *s, struct pico_frame *f)
152{
153    struct pico_socket *found = NULL;
154    #ifdef PICO_SUPPORT_IPV6
155    struct pico_trans *tr = (struct pico_trans *) f->transport_hdr;
156    struct pico_ip6 s_local = {{0}}, s_remote = {{0}}, p_src = {{0}}, p_dst = {{0}};
157    struct pico_ipv6_hdr *ip6hdr = (struct pico_ipv6_hdr *)(f->net_hdr);
158    s_local = s->local_addr.ip6;
159    s_remote = s->remote_addr.ip6;
160    p_src = ip6hdr->src;
161    p_dst = ip6hdr->dst;
162    if ((s->remote_port == tr->sport) &&
163        (!memcmp(s_remote.addr, p_src.addr, PICO_SIZE_IP6)) &&
164        ((!memcmp(s_local.addr, PICO_IP6_ANY, PICO_SIZE_IP6)) || (!memcmp(s_local.addr, p_dst.addr, PICO_SIZE_IP6)))) {
165        found = s;
166        return found;
167    } else if ((s->remote_port == 0)  && /* not connected... listening */
168               ((!memcmp(s_local.addr, PICO_IP6_ANY, PICO_SIZE_IP6)) || (!memcmp(s_local.addr, p_dst.addr, PICO_SIZE_IP6)))) {
169        /* listen socket */
170        found = s;
171    }
172
173    #else
174    (void) s;
175    (void) f;
176    #endif
177    return found;
178}
179
180static int socket_tcp_do_deliver(struct pico_socket *s, struct pico_frame *f)
181{
182    if (s != NULL) {
183        pico_tcp_input(s, f);
184        if ((s->ev_pending) && s->wakeup) {
185            s->wakeup(s->ev_pending, s);
186            if(!s->parent)
187                s->ev_pending = 0;
188        }
189
190        return 0;
191    }
192
193    dbg("TCP SOCKET> Not s.\n");
194    return -1;
195}
196
197int pico_socket_tcp_deliver(struct pico_sockport *sp, struct pico_frame *f)
198{
199    struct pico_socket *found = NULL;
200    struct pico_socket *target = NULL;
201    struct pico_tree_node *index = NULL;
202    struct pico_tree_node *_tmp;
203    struct pico_socket *s = NULL;
204
205    pico_tree_foreach_safe(index, &sp->socks, _tmp){
206        s = index->keyValue;
207        /* 4-tuple identification of socket (port-IP) */
208        if (IS_IPV4(f)) {
209            found = socket_tcp_deliver_ipv4(s, f);
210        }
211
212        if (IS_IPV6(f)) {
213            found = socket_tcp_deliver_ipv6(s, f);
214        }
215
216        if (found)
217        {
218            target = found;
219            if ( found->remote_port != 0)
220                /* only break if it's connected */
221                break;
222        }
223    } /* FOREACH */
224
225    return socket_tcp_do_deliver(target, f);
226}
227
228struct pico_socket *pico_socket_tcp_open(uint16_t family)
229{
230    struct pico_socket *s = NULL;
231    (void) family;
232#ifdef PICO_SUPPORT_TCP
233    s = pico_tcp_open(family);
234    if (!s) {
235        pico_err = PICO_ERR_ENOMEM;
236        return NULL;
237    }
238
239    s->proto = &pico_proto_tcp;
240    /*check if Nagle enabled */
241    /*
242       if (!IS_NAGLE_ENABLED(s))
243           dbg("ERROR Nagle should be enabled here\n\n");
244     */
245#endif
246    return s;
247}
248
249int pico_socket_tcp_read(struct pico_socket *s, void *buf, uint32_t len)
250{
251#ifdef PICO_SUPPORT_TCP
252    /* check if in shutdown state and if no more data in tcpq_in */
253    if ((s->state & PICO_SOCKET_STATE_SHUT_REMOTE) && pico_tcp_queue_in_is_empty(s)) {
254        pico_err = PICO_ERR_ESHUTDOWN;
255        return -1;
256    } else {
257        return (int)(pico_tcp_read(s, buf, (uint32_t)len));
258    }
259
260#else
261    return 0;
262#endif
263}
264
265void transport_flags_update(struct pico_frame *f, struct pico_socket *s)
266{
267#ifdef PICO_SUPPORT_TCP
268    if(is_sock_tcp(s))
269        pico_tcp_flags_update(f, s);
270
271#endif
272}
273