Kernel 0.5.8-r8; tcp protocol is finished from base and was merge with sockets, added...
[ZeXOS.git] / kernel / core / net / tcp.c
blob24384926e69da88a0c2bbed2c5d5bffe7a2d4027
1 /*
2 * ZeX/OS
3 * Copyright (C) 2008 Tomas 'ZeXx86' Jedrzejek (zexx86@gmail.com)
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include <system.h>
21 #include <string.h>
22 #include <dev.h>
23 #include <net/eth.h>
24 #include <net/net.h>
25 #include <net/if.h>
26 #include <net/packet.h>
27 #include <net/ip.h>
28 #include <net/tcp.h>
29 #include <net/socket.h>
31 proto_tcp_conn_t proto_tcp_conn_list;
33 extern netif_t netif_list;
35 static net_port proto_tcp_client_port;
36 static unsigned short proto_tcp_dup;
37 static unsigned proto_tcp_seq;
39 static unsigned proto_tcp_fd;
41 /* prototype */
42 proto_tcp_conn_t *net_proto_tcp_conn_check (net_ipv4 ip_source, net_port port_source, net_ipv4 ip_dest, net_port port_dest);
43 int net_proto_tcp_conn_add ();
44 int net_proto_tcp_conn_set (proto_tcp_conn_t *conn, netif_t *eth, net_port port_source, net_ipv4 ip_dest, net_port port_dest);
45 int net_proto_tcp_conn_estabilish (proto_tcp_conn_t *conn, netif_t *eth, net_ipv4 ip_dest, net_port port_dest);
46 int net_proto_tcp_conn_estabilish_reply (proto_tcp_conn_t *conn, proto_ip_t *ip_old, proto_tcp_t *tcp_old);
47 int net_proto_tcp_read_cache (proto_tcp_conn_t *conn, char *data, unsigned len);
48 int net_proto_tcp_read_ok (proto_tcp_conn_t *conn, proto_ip_t *ip_old, proto_tcp_t *tcp_old, unsigned len);
49 int net_proto_tcp_write (netif_t *eth, net_ipv4 dest, proto_tcp_t *tcp, char *data, unsigned len);
50 int net_proto_tcp_write_data (proto_tcp_conn_t *conn, char *data, unsigned len);
51 int net_proto_tcp_write_data_ok (proto_tcp_conn_t *conn, proto_ip_t *ip_old, proto_tcp_t *tcp_old);
52 int net_proto_tcp_conn_disconnected (proto_tcp_conn_t *conn, proto_ip_t *ip_old, proto_tcp_t *tcp_old);
53 int net_proto_tcp_conn_close (proto_tcp_conn_t *conn);
54 unsigned net_proto_tcp_conn_del (proto_tcp_conn_t *conn);
55 proto_tcp_conn_t *net_proto_tcp_conn_find (int fd);
57 /** TCP protocol
58 * User-friendly socket functions
60 int net_proto_tcp_socket ()
62 return net_proto_tcp_conn_add ();
65 int net_proto_tcp_connect (int fd, sockaddr_in *addr)
67 int ret = -1;
69 proto_tcp_conn_t *conn = net_proto_tcp_conn_find (fd);
71 if (!conn)
72 return -1;
74 netif_t *netif;
75 for (netif = netif_list.next; netif != &netif_list; netif = netif->next) {
76 ret = net_proto_tcp_conn_estabilish (conn, netif, net_proto_ip_convert (addr->sin_addr), addr->sin_port);
78 /* TODO: connect timeout */
80 /* blocking mode */
81 if (!(conn->flags & O_NONBLOCK)) {
82 if (!ret)
83 return -1;
85 while (conn->state != PROTO_TCP_CONN_STATE_ESTABILISHED)
86 schedule ();
87 } else
88 /* non-blocking mode */
89 return -1;
91 ret = 0;
94 return ret;
97 int net_proto_tcp_send (int fd, char *msg, unsigned size)
99 proto_tcp_conn_t *conn = net_proto_tcp_conn_find (fd);
101 if (!conn)
102 return -1;
104 int ret = net_proto_tcp_write_data (conn, msg, size);
106 if (ret) {
107 /* blocking mode */
108 if (!(conn->flags & O_NONBLOCK)) {
109 while (conn->state != PROTO_TCP_CONN_STATE_READY)
110 schedule ();
111 } else {
112 /* non-blocking mode */
113 /* TODO: ? */
114 while (conn->state != PROTO_TCP_CONN_STATE_READY)
115 schedule ();
119 return size;
122 int net_proto_tcp_recv (int fd, char *msg, unsigned size)
124 proto_tcp_conn_t *conn = net_proto_tcp_conn_find (fd);
126 if (!conn)
127 return -3;
129 int ret = 0;
131 /* blocking mode */
132 if (!(conn->flags & O_NONBLOCK)) {
133 while (!conn->len/* || conn->state != PROTO_TCP_CONN_STATE_READY*/) {
134 if (!conn->state == PROTO_TCP_CONN_STATE_CLOSE) {
135 if (net_proto_tcp_conn_del (conn));
136 return -1;
139 schedule ();
141 } else {
142 if (conn->state == PROTO_TCP_CONN_STATE_CLOSE) {
143 if (net_proto_tcp_conn_del (conn));
144 return -1;
147 if (!conn->len)
148 return 0;
151 if (conn->len > size)
152 return -2;
154 ret = conn->len;
156 memcpy (msg, conn->data, conn->len);
158 conn->len = 0;
160 kfree (conn->data);
162 return ret;
165 int net_proto_tcp_close (int fd)
167 proto_tcp_conn_t *conn = net_proto_tcp_conn_find (fd);
169 if (!conn)
170 return -1;
172 if (conn->state == PROTO_TCP_CONN_STATE_DISCONNECTED)
173 return 0;
175 int ret = net_proto_tcp_conn_close (conn);
177 return ret;
180 int net_proto_tcp_fcntl (int fd, int cmd, long arg)
182 proto_tcp_conn_t *conn = net_proto_tcp_conn_find (fd);
184 if (!conn)
185 return -1;
187 switch (cmd) {
188 case F_GETFL:
189 return conn->flags;
190 case F_SETFL:
191 conn->flags = arg;
192 return 0;
195 return -1;
198 /** TCP protocol
199 * Hardend code - syn, ack, psh, fin, etc :P
201 unsigned net_proto_tcp_handler (packet_t *packet, proto_ip_t *ip, char *buf, unsigned len)
203 proto_tcp_t *tcp = (proto_tcp_t *) buf;
205 /* First check ip address and ports, that we want this data */
206 proto_tcp_conn_t *conn = net_proto_tcp_conn_check (ip->ip_dest, tcp->port_dest, ip->ip_source, tcp->port_source);
208 if (!conn)
209 return 1;
211 /* data received */
212 if (tcp->flags & 0x08) {
213 /* needed for calculate real offset from 4bit number */
214 unsigned offset = tcp->data_offset * 4;
216 /* now calculate accurate length of tcp _data_ */
217 unsigned size = swap16 (ip->total_len) - (offset + (ip->head_len*4));
219 net_proto_tcp_read_cache (conn, buf+offset, size);
221 /* send ack */
222 net_proto_tcp_read_ok (conn, ip, tcp, size);
225 /* sended data was delivered succefully */
226 if (tcp->flags & 0x10) {
227 net_proto_tcp_write_data_ok (conn, ip, tcp);
230 /* connection estabilish respond */
231 if (tcp->flags == 0x12) {
232 return net_proto_tcp_conn_estabilish_reply (conn, ip, tcp);
235 /* another side close connection */
236 if (tcp->flags & 0x01) {
237 DPRINT ("TCP -> fd %d connection closed by remote host\n", conn->fd);
238 net_proto_tcp_conn_disconnected (conn, ip, tcp);
241 return 1;
244 int net_proto_tcp_read_cache (proto_tcp_conn_t *conn, char *data, unsigned len)
246 if (!data || !conn || !len)
247 return 0;
249 if (!conn->len)
250 conn->data = (char *) kmalloc (sizeof (char) * len);
251 else
252 conn->data = (char *) krealloc (conn->data, (sizeof (char) * (conn->len+len)));
254 if (!conn->data)
255 return -1;
257 memcpy (conn->data+conn->len, data, len);
259 conn->len += len;
261 conn->data[conn->len] = '\0';
263 return conn->len;
266 int net_proto_tcp_read_ok (proto_tcp_conn_t *conn, proto_ip_t *ip_old, proto_tcp_t *tcp_old, unsigned len)
268 if (!ip_old || !tcp_old || !conn)
269 return 0;
272 proto_tcp_t *tcp = (proto_tcp_t *) kmalloc (sizeof (proto_tcp_t));
274 if (!tcp)
275 return 0;
277 tcp->port_source = conn->port_source; // increase client port too
278 tcp->port_dest = conn->port_dest;
280 tcp->seq = tcp_old->ack;
282 /* this small thing did bad thing when e.g. 10 10 10 fe get to calculation,
283 it returns 10 10 10 08, no 10 10 11 08 */
284 unsigned seq = swap32 (tcp_old->seq);
285 seq += len;
287 tcp->ack = swap32 (seq);
289 tcp->res = 0;
290 tcp->data_offset = 5;
291 tcp->flags = 0x10;
292 tcp->window = tcp_old->window + swap16 (64);
293 tcp->checksum = 0;
295 int ret = net_proto_tcp_write (conn->netif, conn->ip_dest, tcp, 0, 0);
297 /*if (proto_tcp_dup == tcp_old->checksum) {
298 printf ("Duplicated packet :)");
299 timer_wait (150);
302 if (ret) {
303 conn->seq = tcp->ack;
304 conn->ack = tcp->seq;
306 proto_tcp_dup = tcp_old->checksum;
309 kfree (tcp);
311 return ret;
314 int net_proto_tcp_write (netif_t *eth, net_ipv4 dest, proto_tcp_t *tcp, char *data, unsigned len)
316 if (!eth || !tcp)
317 return 0;
319 if (len)
320 if (!data)
321 return 0;
323 mac_addr_t mac_dest;
324 unsigned get = arp_cache_get (dest, &mac_dest);
326 if (!get) {
327 arp_send_request (eth, dest);
329 unsigned i = 0;
330 /* 100ms for waiting on ARP reply */
331 while (i < 100) {
332 get = arp_cache_get (dest, &mac_dest);
334 if (get)
335 break;
337 /* TODO: make better waiting for ARP reply */
338 timer_wait (1);
340 schedule ();
342 i ++;
345 if (!get)
346 return 0;
349 /* packet header */
350 packet_t *packet = (packet_t *) kmalloc (sizeof (packet_t));
352 if (!packet)
353 return 0;
355 memcpy (&packet->mac_source, eth->dev->dev_addr, 6);
356 memcpy (&packet->mac_dest, mac_dest, 6);
357 packet->type = NET_PACKET_TYPE_IPV4;
359 /* ip layer */
360 proto_ip_t *ip = (proto_ip_t *) kmalloc (sizeof (proto_ip_t));
362 if (!ip)
363 return 0;
365 /* there are some fixed values - yeah it is horrible */
366 ip->ver = 4;
367 ip->head_len = 5;
369 ip->flags = 0;
370 ip->frag = 0;
371 ip->ttl = 64;
372 ip->checksum = 0;
373 ip->proto = NET_PROTO_IP_TYPE_TCP;
374 ip->ip_source = eth->ip;
375 ip->ip_dest = dest;
377 unsigned l = (tcp->data_offset*4);
379 /* calculate total length of packet (tcp/ip) */
380 ip->total_len = swap16 (l+sizeof (proto_tcp_t)+len);
382 char *buf_tcp = (char *) kmalloc ((len+l) * sizeof (char));
384 if (!buf_tcp)
385 return 0;
387 memcpy (buf_tcp, (char *) tcp, l);
389 if (len)
390 memcpy (buf_tcp+l, data, len);
392 buf_tcp[l+len] = '\0';
394 /* calculate checksum and put it to tcp header */
395 proto_tcp_t *tcp_ = (proto_tcp_t *) buf_tcp;
397 tcp_->checksum = checksum16_tcp (eth->ip, dest, buf_tcp, l+len);
399 /* send tcp header+data to ip layer */
400 unsigned ret = net_proto_ip_send (eth, packet, ip, (char *) buf_tcp, l+len);
402 kfree (buf_tcp);
403 kfree (ip);
404 kfree (packet);
406 return ret;
409 int net_proto_tcp_write_data (proto_tcp_conn_t *conn, char *data, unsigned len)
411 if (!conn || !len || !data)
412 return 0;
414 proto_tcp_t *tcp = (proto_tcp_t *) kmalloc (sizeof (proto_tcp_t));
416 if (!tcp)
417 return 0;
419 tcp->port_source = conn->port_source;
420 tcp->port_dest = conn->port_dest;
422 proto_tcp_seq += swap32 (1);
424 tcp->seq = conn->seq;
425 tcp->ack = conn->ack;
427 tcp->res = 0;
428 tcp->data_offset = 5;
429 tcp->flags = 0x18;
430 tcp->window = swap16 (32768);
431 tcp->checksum = 0;
433 int ret = net_proto_tcp_write (conn->netif, conn->ip_dest, tcp, data, len);
435 kfree (tcp);
437 if (ret)
438 conn->state = PROTO_TCP_CONN_STATE_WAIT;
440 return ret;
443 int net_proto_tcp_write_data_ok (proto_tcp_conn_t *conn, proto_ip_t *ip_old, proto_tcp_t *tcp_old)
445 if (!ip_old || !tcp_old || !conn)
446 return 0;
448 /* cross - because we change now sides :) */
449 conn->seq = tcp_old->ack;
450 conn->ack = tcp_old->seq;
452 conn->state = PROTO_TCP_CONN_STATE_READY;
454 return 1;
457 /* try connect to server */
458 int net_proto_tcp_conn_estabilish (proto_tcp_conn_t *conn, netif_t *eth, net_ipv4 ip_dest, net_port port_dest)
460 if (!conn)
461 return 0;
463 proto_tcp_t *tcp = (proto_tcp_t *) kmalloc (sizeof (proto_tcp_t));
465 if (!tcp)
466 return 0;
468 tcp->port_source = swap16 (proto_tcp_client_port); // increase client port too
469 tcp->port_dest = port_dest;
471 /* setup new connection */
472 net_proto_tcp_conn_set (conn, eth, tcp->port_source, ip_dest, tcp->port_dest);
474 tcp->seq = conn->seq;
475 tcp->ack = conn->ack;
477 tcp->res = 0;
478 tcp->data_offset = 5;
479 tcp->flags = 0x02;
480 tcp->window = swap16 (32792);
481 tcp->checksum = 0;
483 int ret = net_proto_tcp_write (eth, ip_dest, tcp, 0, 0);
485 kfree (tcp);
487 if (!ret) {
488 net_proto_tcp_conn_del (conn);
489 return 0;
492 return 1;
495 int net_proto_tcp_conn_estabilish_reply (proto_tcp_conn_t *conn, proto_ip_t *ip_old, proto_tcp_t *tcp_old)
497 if (!ip_old || !tcp_old || !conn)
498 return 0;
500 proto_tcp_t *tcp = (proto_tcp_t *) kmalloc (sizeof (proto_tcp_t));
502 if (!tcp)
503 return 0;
505 tcp->port_source = conn->port_source; // increase client port too
506 tcp->port_dest = conn->port_dest;
508 tcp->seq = tcp_old->ack;
509 tcp->ack = tcp_old->seq + swap32 (1);
511 tcp->res = 0;
512 tcp->data_offset = 5;
513 tcp->flags = 0x10;
514 tcp->window = tcp_old->window + swap16 (64);
515 tcp->checksum = 0;
517 int ret = net_proto_tcp_write (conn->netif, conn->ip_dest, tcp, 0, 0);
519 if (ret) {
520 conn->seq = tcp->seq;
521 conn->ack = tcp->ack;
522 conn->state = PROTO_TCP_CONN_STATE_ESTABILISHED;
525 DPRINT ("TCP -> fd %ï connected to server succefully\n", conn->fd);
527 kfree (tcp);
529 return ret;
532 int net_proto_tcp_conn_disconnected (proto_tcp_conn_t *conn, proto_ip_t *ip_old, proto_tcp_t *tcp_old)
534 if (!ip_old || !tcp_old || !conn)
535 return 0;
537 proto_tcp_t *tcp = (proto_tcp_t *) kmalloc (sizeof (proto_tcp_t));
539 if (!tcp)
540 return 0;
542 tcp->port_source = conn->port_source; // increase client port too
543 tcp->port_dest = conn->port_dest;
545 tcp->seq = tcp_old->ack;
546 tcp->ack = tcp_old->seq + swap32 (1);
548 tcp->res = 0;
549 tcp->data_offset = 5;
550 tcp->flags = 0x10;
551 tcp->window = tcp_old->window + swap16 (64);
552 tcp->checksum = 0;
554 int ret = net_proto_tcp_write (conn->netif, conn->ip_dest, tcp, 0, 0);
556 kfree (tcp);
558 if (ret)
559 conn->state = PROTO_TCP_CONN_STATE_CLOSE; //ret = net_proto_tcp_conn_del (conn);
560 else
561 return -1;
563 return ret;
566 int net_proto_tcp_conn_close (proto_tcp_conn_t *conn)
568 if (!conn)
569 return 0;
571 proto_tcp_t *tcp = (proto_tcp_t *) kmalloc (sizeof (proto_tcp_t));
573 if (!tcp)
574 return 0;
576 tcp->port_source = conn->port_source; // increase client port too
577 tcp->port_dest = conn->port_dest;
579 tcp->seq = conn->seq;
580 tcp->ack = conn->ack;
582 tcp->res = 0;
583 tcp->data_offset = 5;
584 tcp->flags = 0x11;
585 tcp->window = swap16 (32832);
586 tcp->checksum = 0;
588 int ret = net_proto_tcp_write (conn->netif, conn->ip_dest, tcp, 0, 0);
590 kfree (tcp);
592 if (ret) {
593 conn->state = PROTO_TCP_CONN_STATE_CLOSE;
596 return ret;
599 /* Create new TCP connection */
600 int net_proto_tcp_conn_add ()
602 proto_tcp_conn_t *conn;
604 /* alloc and init context */
605 conn = (proto_tcp_conn_t *) kmalloc (sizeof (proto_tcp_conn_t));
607 if (!conn)
608 return 0;
610 memset (conn, 0, sizeof (proto_tcp_conn_t));
612 conn->flags = 0;
614 conn->fd = proto_tcp_fd ++;
616 /* add into list */
617 conn->next = &proto_tcp_conn_list;
618 conn->prev = proto_tcp_conn_list.prev;
619 conn->prev->next = conn;
620 conn->next->prev = conn;
622 return conn->fd;
625 /* Setup new connection */
626 int net_proto_tcp_conn_set (proto_tcp_conn_t *conn, netif_t *eth, net_port port_source, net_ipv4 ip_dest, net_port port_dest)
628 if (!conn || !eth)
629 return 0;
631 conn->ip_source = eth->ip;
632 conn->ip_dest = ip_dest;
634 conn->port_source = port_source;
635 conn->port_dest = port_dest;
637 proto_tcp_seq += swap32 (1);
639 conn->seq = proto_tcp_seq;
640 conn->ack = 0;
642 conn->state = PROTO_TCP_CONN_STATE_ESTABILISH;
644 conn->netif = eth;
646 conn->len = 0;
647 conn->data = 0;
649 return 1;
652 /* Delete existing connection from list */
653 unsigned net_proto_tcp_conn_del (proto_tcp_conn_t *conn)
655 if (!conn)
656 return 0;
658 conn->state = PROTO_TCP_CONN_STATE_DISCONNECTED;
660 if (conn->len)
661 kfree (conn->data);
663 conn->len = 0;
665 conn->next->prev = conn->prev;
666 conn->prev->next = conn->next;
668 kfree (conn);
670 return 1;
673 proto_tcp_conn_t *net_proto_tcp_conn_check (net_ipv4 ip_source, net_port port_source, net_ipv4 ip_dest, net_port port_dest)
675 proto_tcp_conn_t *conn = NULL;
676 for (conn = proto_tcp_conn_list.next; conn != &proto_tcp_conn_list; conn = conn->next) {
677 if (conn->ip_source == ip_source)
678 if (conn->ip_dest == ip_dest && conn->port_source == port_source
679 && conn->port_dest == port_dest)
680 return conn;
683 return 0;
686 proto_tcp_conn_t *net_proto_tcp_conn_find (int fd)
688 proto_tcp_conn_t *conn = NULL;
689 for (conn = proto_tcp_conn_list.next; conn != &proto_tcp_conn_list; conn = conn->next) {
690 if (conn->fd == fd)
691 return conn;
694 return 0;
697 /* init of tcp protocol */
698 unsigned init_net_proto_tcp ()
700 proto_tcp_conn_list.next = &proto_tcp_conn_list;
701 proto_tcp_conn_list.prev = &proto_tcp_conn_list;
703 /* base tcp client port, which is used for client's use */
704 proto_tcp_client_port = 1024;
705 proto_tcp_dup = 0x0;
706 proto_tcp_seq = 0x1;
707 proto_tcp_fd = 1024;
709 return 1;