vaseboot/VasEBoot-core/net/tcp.c

1063 lines
28 KiB
C

/*
* VAS_EBOOT -- GRand Unified Bootloader
* Copyright (C) 2010,2011 Free Software Foundation, Inc.
*
* VAS_EBOOT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VAS_EBOOT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VAS_EBOOT. If not, see <http://www.gnu.org/licenses/>.
*/
#include <VasEBoot/net.h>
#include <VasEBoot/net/ip.h>
#include <VasEBoot/net/tcp.h>
#include <VasEBoot/net/netbuff.h>
#include <VasEBoot/time.h>
#include <VasEBoot/priority_queue.h>
#include <VasEBoot/datetime.h>
#define TCP_SYN_RETRANSMISSION_TIMEOUT VAS_EBOOT_NET_INTERVAL
#define TCP_SYN_RETRANSMISSION_COUNT VAS_EBOOT_NET_TRIES
#define TCP_RETRANSMISSION_TIMEOUT VAS_EBOOT_NET_INTERVAL
#define TCP_RETRANSMISSION_COUNT VAS_EBOOT_NET_TRIES
struct unacked
{
struct unacked *next;
struct unacked **prev;
struct VasEBoot_net_buff *nb;
VasEBoot_uint64_t last_try;
int try_count;
};
enum
{
TCP_FIN = 0x1,
TCP_SYN = 0x2,
TCP_RST = 0x4,
TCP_PUSH = 0x8,
TCP_ACK = 0x10,
TCP_URG = 0x20,
};
struct VasEBoot_net_tcp_socket
{
struct VasEBoot_net_tcp_socket *next;
struct VasEBoot_net_tcp_socket **prev;
int established;
int i_closed;
int they_closed;
int in_port;
int out_port;
int errors;
int they_reseted;
int i_reseted;
int i_stall;
VasEBoot_uint32_t my_start_seq;
VasEBoot_uint32_t my_cur_seq;
VasEBoot_uint32_t their_start_seq;
VasEBoot_uint32_t their_cur_seq;
VasEBoot_uint16_t my_window;
struct unacked *unack_first;
struct unacked *unack_last;
VasEBoot_err_t (*recv_hook) (VasEBoot_net_tcp_socket_t sock, struct VasEBoot_net_buff *nb,
void *recv);
void (*error_hook) (VasEBoot_net_tcp_socket_t sock, void *recv);
void (*fin_hook) (VasEBoot_net_tcp_socket_t sock, void *recv);
void *hook_data;
VasEBoot_net_network_level_address_t out_nla;
VasEBoot_net_link_level_address_t ll_target_addr;
struct VasEBoot_net_network_level_interface *inf;
VasEBoot_net_packets_t packs;
VasEBoot_priority_queue_t pq;
};
struct VasEBoot_net_tcp_listen
{
struct VasEBoot_net_tcp_listen *next;
struct VasEBoot_net_tcp_listen **prev;
VasEBoot_uint16_t port;
const struct VasEBoot_net_network_level_interface *inf;
VasEBoot_err_t (*listen_hook) (VasEBoot_net_tcp_listen_t listen,
VasEBoot_net_tcp_socket_t sock,
void *data);
void *hook_data;
};
struct tcphdr
{
VasEBoot_uint16_t src;
VasEBoot_uint16_t dst;
VasEBoot_uint32_t seqnr;
VasEBoot_uint32_t ack;
VasEBoot_uint16_t flags;
VasEBoot_uint16_t window;
VasEBoot_uint16_t checksum;
VasEBoot_uint16_t urgent;
} VAS_EBOOT_PACKED;
struct tcp_pseudohdr
{
VasEBoot_uint32_t src;
VasEBoot_uint32_t dst;
VasEBoot_uint8_t zero;
VasEBoot_uint8_t proto;
VasEBoot_uint16_t tcp_length;
} VAS_EBOOT_PACKED;
struct tcp6_pseudohdr
{
VasEBoot_uint64_t src[2];
VasEBoot_uint64_t dst[2];
VasEBoot_uint32_t tcp_length;
VasEBoot_uint8_t zero[3];
VasEBoot_uint8_t proto;
} VAS_EBOOT_PACKED;
static struct VasEBoot_net_tcp_socket *tcp_sockets;
static struct VasEBoot_net_tcp_listen *tcp_listens;
#define FOR_TCP_SOCKETS(var) FOR_LIST_ELEMENTS (var, tcp_sockets)
#define FOR_TCP_LISTENS(var) FOR_LIST_ELEMENTS (var, tcp_listens)
VasEBoot_net_tcp_listen_t
VasEBoot_net_tcp_listen (VasEBoot_uint16_t port,
const struct VasEBoot_net_network_level_interface *inf,
VasEBoot_err_t (*listen_hook) (VasEBoot_net_tcp_listen_t listen,
VasEBoot_net_tcp_socket_t sock,
void *data),
void *hook_data)
{
VasEBoot_net_tcp_listen_t ret;
ret = VasEBoot_malloc (sizeof (*ret));
if (!ret)
return NULL;
ret->listen_hook = listen_hook;
ret->hook_data = hook_data;
ret->port = port;
ret->inf = inf;
VasEBoot_list_push (VAS_EBOOT_AS_LIST_P (&tcp_listens), VAS_EBOOT_AS_LIST (ret));
return ret;
}
void
VasEBoot_net_tcp_stop_listen (VasEBoot_net_tcp_listen_t listen)
{
VasEBoot_list_remove (VAS_EBOOT_AS_LIST (listen));
}
static inline void
tcp_socket_register (VasEBoot_net_tcp_socket_t sock)
{
VasEBoot_list_push (VAS_EBOOT_AS_LIST_P (&tcp_sockets),
VAS_EBOOT_AS_LIST (sock));
}
static void
error (VasEBoot_net_tcp_socket_t sock)
{
struct unacked *unack, *next;
if (sock->error_hook)
sock->error_hook (sock, sock->hook_data);
for (unack = sock->unack_first; unack; unack = next)
{
next = unack->next;
VasEBoot_netbuff_free (unack->nb);
VasEBoot_free (unack);
}
sock->unack_first = NULL;
sock->unack_last = NULL;
}
static VasEBoot_err_t
tcp_send (struct VasEBoot_net_buff *nb, VasEBoot_net_tcp_socket_t socket)
{
VasEBoot_err_t err;
VasEBoot_uint8_t *nbd;
struct unacked *unack;
struct tcphdr *tcph;
VasEBoot_size_t size;
tcph = (struct tcphdr *) nb->data;
tcph->seqnr = VasEBoot_cpu_to_be32 (socket->my_cur_seq);
size = (nb->tail - nb->data - (VasEBoot_be_to_cpu16 (tcph->flags) >> 12) * 4);
if (VasEBoot_be_to_cpu16 (tcph->flags) & TCP_FIN)
size++;
socket->my_cur_seq += size;
tcph->src = VasEBoot_cpu_to_be16 (socket->in_port);
tcph->dst = VasEBoot_cpu_to_be16 (socket->out_port);
tcph->checksum = 0;
tcph->checksum = VasEBoot_net_ip_transport_checksum (nb, VAS_EBOOT_NET_IP_TCP,
&socket->inf->address,
&socket->out_nla);
nbd = nb->data;
if (size)
{
unack = VasEBoot_malloc (sizeof (*unack));
if (!unack)
return VasEBoot_errno;
unack->next = NULL;
unack->nb = nb;
unack->try_count = 1;
unack->last_try = VasEBoot_get_time_ms ();
if (!socket->unack_last)
socket->unack_first = socket->unack_last = unack;
else
socket->unack_last->next = unack;
}
err = VasEBoot_net_send_ip_packet (socket->inf, &(socket->out_nla),
&(socket->ll_target_addr), nb,
VAS_EBOOT_NET_IP_TCP);
if (err)
return err;
nb->data = nbd;
if (!size)
VasEBoot_netbuff_free (nb);
return VAS_EBOOT_ERR_NONE;
}
void
VasEBoot_net_tcp_close (VasEBoot_net_tcp_socket_t sock,
int discard_received)
{
struct VasEBoot_net_buff *nb_fin;
struct tcphdr *tcph_fin;
VasEBoot_err_t err;
if (discard_received != VAS_EBOOT_NET_TCP_CONTINUE_RECEIVING)
{
sock->recv_hook = NULL;
sock->error_hook = NULL;
sock->fin_hook = NULL;
}
if (discard_received == VAS_EBOOT_NET_TCP_ABORT)
sock->i_reseted = 1;
if (sock->i_closed)
return;
sock->i_closed = 1;
nb_fin = VasEBoot_netbuff_alloc (sizeof (*tcph_fin)
+ VAS_EBOOT_NET_OUR_MAX_IP_HEADER_SIZE
+ VAS_EBOOT_NET_MAX_LINK_HEADER_SIZE);
if (!nb_fin)
return;
err = VasEBoot_netbuff_reserve (nb_fin, VAS_EBOOT_NET_OUR_MAX_IP_HEADER_SIZE
+ VAS_EBOOT_NET_MAX_LINK_HEADER_SIZE);
if (err)
{
VasEBoot_netbuff_free (nb_fin);
VasEBoot_dprintf ("net", "error closing socket\n");
VasEBoot_errno = VAS_EBOOT_ERR_NONE;
return;
}
err = VasEBoot_netbuff_put (nb_fin, sizeof (*tcph_fin));
if (err)
{
VasEBoot_netbuff_free (nb_fin);
VasEBoot_dprintf ("net", "error closing socket\n");
VasEBoot_errno = VAS_EBOOT_ERR_NONE;
return;
}
tcph_fin = (void *) nb_fin->data;
tcph_fin->ack = VasEBoot_cpu_to_be32 (sock->their_cur_seq);
tcph_fin->flags = VasEBoot_cpu_to_be16_compile_time ((5 << 12) | TCP_FIN
| TCP_ACK);
tcph_fin->window = VasEBoot_cpu_to_be16_compile_time (0);
tcph_fin->urgent = 0;
err = tcp_send (nb_fin, sock);
if (err)
{
VasEBoot_netbuff_free (nb_fin);
VasEBoot_dprintf ("net", "error closing socket\n");
VasEBoot_errno = VAS_EBOOT_ERR_NONE;
}
return;
}
static void
ack_real (VasEBoot_net_tcp_socket_t sock, int res)
{
struct VasEBoot_net_buff *nb_ack;
struct tcphdr *tcph_ack;
VasEBoot_err_t err;
nb_ack = VasEBoot_netbuff_alloc (sizeof (*tcph_ack) + 128);
if (!nb_ack)
return;
err = VasEBoot_netbuff_reserve (nb_ack, 128);
if (err)
{
VasEBoot_netbuff_free (nb_ack);
VasEBoot_dprintf ("net", "error closing socket\n");
VasEBoot_errno = VAS_EBOOT_ERR_NONE;
return;
}
err = VasEBoot_netbuff_put (nb_ack, sizeof (*tcph_ack));
if (err)
{
VasEBoot_netbuff_free (nb_ack);
VasEBoot_dprintf ("net", "error closing socket\n");
VasEBoot_errno = VAS_EBOOT_ERR_NONE;
return;
}
tcph_ack = (void *) nb_ack->data;
if (res)
{
tcph_ack->ack = VasEBoot_cpu_to_be32_compile_time (0);
tcph_ack->flags = VasEBoot_cpu_to_be16_compile_time ((5 << 12) | TCP_RST);
tcph_ack->window = VasEBoot_cpu_to_be16_compile_time (0);
}
else
{
tcph_ack->ack = VasEBoot_cpu_to_be32 (sock->their_cur_seq);
tcph_ack->flags = VasEBoot_cpu_to_be16_compile_time ((5 << 12) | TCP_ACK);
tcph_ack->window = !sock->i_stall ? VasEBoot_cpu_to_be16 (sock->my_window)
: 0;
}
tcph_ack->urgent = 0;
tcph_ack->src = VasEBoot_cpu_to_be16 (sock->in_port);
tcph_ack->dst = VasEBoot_cpu_to_be16 (sock->out_port);
err = tcp_send (nb_ack, sock);
if (err)
{
VasEBoot_dprintf ("net", "error acking socket\n");
VasEBoot_errno = VAS_EBOOT_ERR_NONE;
}
}
static void
ack (VasEBoot_net_tcp_socket_t sock)
{
ack_real (sock, 0);
}
static void
reset (VasEBoot_net_tcp_socket_t sock)
{
ack_real (sock, 1);
}
void
VasEBoot_net_tcp_retransmit (void)
{
VasEBoot_net_tcp_socket_t sock;
VasEBoot_uint64_t ctime = 0, limit_time = 0;
if (tcp_sockets != NULL)
{
ctime = VasEBoot_get_time_ms ();
limit_time = ctime - TCP_RETRANSMISSION_TIMEOUT;
}
FOR_TCP_SOCKETS (sock)
{
struct unacked *unack;
for (unack = sock->unack_first; unack; unack = unack->next)
{
struct tcphdr *tcph;
VasEBoot_uint8_t *nbd;
VasEBoot_err_t err;
if (unack->last_try > limit_time)
continue;
if (unack->try_count > TCP_RETRANSMISSION_COUNT)
{
error (sock);
break;
}
unack->try_count++;
unack->last_try = ctime;
nbd = unack->nb->data;
tcph = (struct tcphdr *) nbd;
if ((tcph->flags & VasEBoot_cpu_to_be16_compile_time (TCP_ACK))
&& tcph->ack != VasEBoot_cpu_to_be32 (sock->their_cur_seq))
{
tcph->checksum = 0;
tcph->checksum = VasEBoot_net_ip_transport_checksum (unack->nb,
VAS_EBOOT_NET_IP_TCP,
&sock->inf->address,
&sock->out_nla);
}
err = VasEBoot_net_send_ip_packet (sock->inf, &(sock->out_nla),
&(sock->ll_target_addr), unack->nb,
VAS_EBOOT_NET_IP_TCP);
unack->nb->data = nbd;
if (err)
{
VasEBoot_dprintf ("net", "TCP retransmit failed: %s\n", VasEBoot_errmsg);
VasEBoot_errno = VAS_EBOOT_ERR_NONE;
}
}
}
}
VasEBoot_uint16_t
VasEBoot_net_ip_transport_checksum (struct VasEBoot_net_buff *nb,
VasEBoot_uint16_t proto,
const VasEBoot_net_network_level_address_t *src,
const VasEBoot_net_network_level_address_t *dst)
{
VasEBoot_uint16_t a, b = 0;
VasEBoot_uint32_t c;
a = ~VasEBoot_be_to_cpu16 (VasEBoot_net_ip_chksum ((void *) nb->data,
nb->tail - nb->data));
switch (dst->type)
{
case VAS_EBOOT_NET_NETWORK_LEVEL_PROTOCOL_IPV4:
{
struct tcp_pseudohdr ph;
ph.src = src->ipv4;
ph.dst = dst->ipv4;
ph.zero = 0;
ph.tcp_length = VasEBoot_cpu_to_be16 (nb->tail - nb->data);
ph.proto = proto;
b = ~VasEBoot_be_to_cpu16 (VasEBoot_net_ip_chksum ((void *) &ph, sizeof (ph)));
break;
}
case VAS_EBOOT_NET_NETWORK_LEVEL_PROTOCOL_IPV6:
{
struct tcp6_pseudohdr ph;
VasEBoot_memcpy (ph.src, src->ipv6, sizeof (ph.src));
VasEBoot_memcpy (ph.dst, dst->ipv6, sizeof (ph.dst));
VasEBoot_memset (ph.zero, 0, sizeof (ph.zero));
ph.tcp_length = VasEBoot_cpu_to_be32 (nb->tail - nb->data);
ph.proto = proto;
b = ~VasEBoot_be_to_cpu16 (VasEBoot_net_ip_chksum ((void *) &ph, sizeof (ph)));
break;
}
case VAS_EBOOT_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV:
b = 0;
break;
}
c = (VasEBoot_uint32_t) a + (VasEBoot_uint32_t) b;
if (c >= 0xffff)
c -= 0xffff;
return VasEBoot_cpu_to_be16 (~c);
}
/* FIXME: overflow. */
static int
cmp (const void *a__, const void *b__)
{
struct VasEBoot_net_buff *a_ = *(struct VasEBoot_net_buff **) a__;
struct VasEBoot_net_buff *b_ = *(struct VasEBoot_net_buff **) b__;
struct tcphdr *a = (struct tcphdr *) a_->data;
struct tcphdr *b = (struct tcphdr *) b_->data;
/* We want the first elements to be on top. */
if (VasEBoot_be_to_cpu32 (a->seqnr) < VasEBoot_be_to_cpu32 (b->seqnr))
return +1;
if (VasEBoot_be_to_cpu32 (a->seqnr) > VasEBoot_be_to_cpu32 (b->seqnr))
return -1;
return 0;
}
static void
destroy_pq (VasEBoot_net_tcp_socket_t sock)
{
struct VasEBoot_net_buff **nb_p;
while ((nb_p = VasEBoot_priority_queue_top (sock->pq)))
{
VasEBoot_netbuff_free (*nb_p);
VasEBoot_priority_queue_pop (sock->pq);
}
VasEBoot_priority_queue_destroy (sock->pq);
}
VasEBoot_err_t
VasEBoot_net_tcp_accept (VasEBoot_net_tcp_socket_t sock,
VasEBoot_err_t (*recv_hook) (VasEBoot_net_tcp_socket_t sock,
struct VasEBoot_net_buff *nb,
void *data),
void (*error_hook) (VasEBoot_net_tcp_socket_t sock,
void *data),
void (*fin_hook) (VasEBoot_net_tcp_socket_t sock,
void *data),
void *hook_data)
{
struct VasEBoot_net_buff *nb_ack;
struct tcphdr *tcph;
VasEBoot_err_t err;
VasEBoot_net_network_level_address_t gateway;
struct VasEBoot_net_network_level_interface *inf;
sock->recv_hook = recv_hook;
sock->error_hook = error_hook;
sock->fin_hook = fin_hook;
sock->hook_data = hook_data;
err = VasEBoot_net_route_address (sock->out_nla, &gateway, &inf);
if (err)
return err;
err = VasEBoot_net_link_layer_resolve (sock->inf, &gateway, &(sock->ll_target_addr));
if (err)
return err;
nb_ack = VasEBoot_netbuff_alloc (sizeof (*tcph)
+ VAS_EBOOT_NET_OUR_MAX_IP_HEADER_SIZE
+ VAS_EBOOT_NET_MAX_LINK_HEADER_SIZE);
if (!nb_ack)
return VasEBoot_errno;
err = VasEBoot_netbuff_reserve (nb_ack, VAS_EBOOT_NET_OUR_MAX_IP_HEADER_SIZE
+ VAS_EBOOT_NET_MAX_LINK_HEADER_SIZE);
if (err)
{
VasEBoot_netbuff_free (nb_ack);
return err;
}
err = VasEBoot_netbuff_put (nb_ack, sizeof (*tcph));
if (err)
{
VasEBoot_netbuff_free (nb_ack);
return err;
}
tcph = (void *) nb_ack->data;
tcph->ack = VasEBoot_cpu_to_be32 (sock->their_cur_seq);
tcph->flags = VasEBoot_cpu_to_be16_compile_time ((5 << 12) | TCP_SYN | TCP_ACK);
tcph->window = VasEBoot_cpu_to_be16 (sock->my_window);
tcph->urgent = 0;
sock->established = 1;
tcp_socket_register (sock);
err = tcp_send (nb_ack, sock);
if (err)
return err;
sock->my_cur_seq++;
return VAS_EBOOT_ERR_NONE;
}
/*
* Derive a time-based source port to avoid reusing the same port across
* reboots. This helps prevent failures caused by server side TCP state
* (e.g. TIME_WAIT) from interfering with new connections using the same socket.
*
* The base port starts at 21550 and increments every second by 8 across
* a 5 minute window (300 seconds), giving 2400 possible distinct base ports
* per window. In typical VAS_EBOOT usage, the number of connections per boot is
* small, so reuse is effectively avoided.
*/
static VasEBoot_uint16_t
get_initial_base_port (void)
{
VasEBoot_err_t err;
struct VasEBoot_datetime date;
VasEBoot_int64_t t = 0;
VasEBoot_uint64_t r = 0;
err = VasEBoot_get_datetime (&date);
if (err != VAS_EBOOT_ERR_NONE || !VasEBoot_datetime2unixtime (&date, &t))
{
VasEBoot_errno = VAS_EBOOT_ERR_NONE;
return 21550;
}
VasEBoot_divmod64 (t, 300, &r);
return 21550 + (r << 3);
}
VasEBoot_net_tcp_socket_t
VasEBoot_net_tcp_open (char *server,
VasEBoot_uint16_t out_port,
VasEBoot_err_t (*recv_hook) (VasEBoot_net_tcp_socket_t sock,
struct VasEBoot_net_buff *nb,
void *data),
void (*error_hook) (VasEBoot_net_tcp_socket_t sock,
void *data),
void (*fin_hook) (VasEBoot_net_tcp_socket_t sock,
void *data),
void *hook_data)
{
VasEBoot_err_t err;
VasEBoot_net_network_level_address_t addr;
struct VasEBoot_net_network_level_interface *inf;
VasEBoot_net_network_level_address_t gateway;
VasEBoot_net_tcp_socket_t socket;
static VasEBoot_uint16_t in_port;
struct VasEBoot_net_buff *nb;
struct tcphdr *tcph;
int i;
VasEBoot_uint8_t *nbd;
VasEBoot_net_link_level_address_t ll_target_addr;
if (!in_port)
{
in_port = get_initial_base_port ();
VasEBoot_dprintf ("net", "base port: %d\n", in_port);
}
err = VasEBoot_net_resolve_address (server, &addr);
if (err)
return NULL;
if (addr.type != VAS_EBOOT_NET_NETWORK_LEVEL_PROTOCOL_IPV4
&& addr.type != VAS_EBOOT_NET_NETWORK_LEVEL_PROTOCOL_IPV6)
{
VasEBoot_error (VAS_EBOOT_ERR_BUG, "not an IP address");
return NULL;
}
err = VasEBoot_net_route_address (addr, &gateway, &inf);
if (err)
return NULL;
err = VasEBoot_net_link_layer_resolve (inf, &gateway, &ll_target_addr);
if (err)
return NULL;
socket = VasEBoot_zalloc (sizeof (*socket));
if (socket == NULL)
return NULL;
socket->out_port = out_port;
socket->inf = inf;
socket->out_nla = addr;
socket->ll_target_addr = ll_target_addr;
socket->in_port = in_port++;
socket->recv_hook = recv_hook;
socket->error_hook = error_hook;
socket->fin_hook = fin_hook;
socket->hook_data = hook_data;
nb = VasEBoot_netbuff_alloc (sizeof (*tcph) + 128);
if (!nb)
{
VasEBoot_free (socket);
return NULL;
}
err = VasEBoot_netbuff_reserve (nb, 128);
if (err)
{
VasEBoot_free (socket);
VasEBoot_netbuff_free (nb);
return NULL;
}
err = VasEBoot_netbuff_put (nb, sizeof (*tcph));
if (err)
{
VasEBoot_free (socket);
VasEBoot_netbuff_free (nb);
return NULL;
}
socket->pq = VasEBoot_priority_queue_new (sizeof (struct VasEBoot_net_buff *), cmp);
if (!socket->pq)
{
VasEBoot_free (socket);
VasEBoot_netbuff_free (nb);
return NULL;
}
tcph = (void *) nb->data;
socket->my_start_seq = VasEBoot_get_time_ms ();
socket->my_cur_seq = socket->my_start_seq + 1;
socket->my_window = 8192;
tcph->seqnr = VasEBoot_cpu_to_be32 (socket->my_start_seq);
tcph->ack = VasEBoot_cpu_to_be32_compile_time (0);
tcph->flags = VasEBoot_cpu_to_be16_compile_time ((5 << 12) | TCP_SYN);
tcph->window = VasEBoot_cpu_to_be16 (socket->my_window);
tcph->urgent = 0;
tcph->src = VasEBoot_cpu_to_be16 (socket->in_port);
tcph->dst = VasEBoot_cpu_to_be16 (socket->out_port);
tcph->checksum = 0;
tcph->checksum = VasEBoot_net_ip_transport_checksum (nb, VAS_EBOOT_NET_IP_TCP,
&socket->inf->address,
&socket->out_nla);
tcp_socket_register (socket);
nbd = nb->data;
for (i = 0; i < TCP_SYN_RETRANSMISSION_COUNT; i++)
{
int j;
nb->data = nbd;
err = VasEBoot_net_send_ip_packet (socket->inf, &(socket->out_nla),
&(socket->ll_target_addr), nb,
VAS_EBOOT_NET_IP_TCP);
if (err)
{
VasEBoot_list_remove (VAS_EBOOT_AS_LIST (socket));
VasEBoot_free (socket);
VasEBoot_netbuff_free (nb);
return NULL;
}
for (j = 0; (j < TCP_SYN_RETRANSMISSION_TIMEOUT / 50
&& !socket->established); j++)
VasEBoot_net_poll_cards (50, &socket->established);
if (socket->established)
break;
}
if (!socket->established)
{
VasEBoot_list_remove (VAS_EBOOT_AS_LIST (socket));
if (socket->they_reseted)
VasEBoot_error (VAS_EBOOT_ERR_NET_PORT_CLOSED,
N_("connection refused"));
else
VasEBoot_error (VAS_EBOOT_ERR_NET_NO_ANSWER,
N_("connection timeout"));
VasEBoot_netbuff_free (nb);
destroy_pq (socket);
VasEBoot_free (socket);
return NULL;
}
VasEBoot_netbuff_free (nb);
return socket;
}
VasEBoot_err_t
VasEBoot_net_send_tcp_packet (const VasEBoot_net_tcp_socket_t socket,
struct VasEBoot_net_buff *nb, int push)
{
struct tcphdr *tcph;
VasEBoot_err_t err;
VasEBoot_ssize_t fraglen;
COMPILE_TIME_ASSERT (sizeof (struct tcphdr) == VAS_EBOOT_NET_TCP_HEADER_SIZE);
if (socket->out_nla.type == VAS_EBOOT_NET_NETWORK_LEVEL_PROTOCOL_IPV4)
fraglen = (socket->inf->card->mtu - VAS_EBOOT_NET_OUR_IPV4_HEADER_SIZE
- sizeof (*tcph));
else
fraglen = 1280 - VAS_EBOOT_NET_OUR_IPV6_HEADER_SIZE;
while (nb->tail - nb->data > fraglen)
{
struct VasEBoot_net_buff *nb2;
nb2 = VasEBoot_netbuff_alloc (fraglen + sizeof (*tcph)
+ VAS_EBOOT_NET_OUR_MAX_IP_HEADER_SIZE
+ VAS_EBOOT_NET_MAX_LINK_HEADER_SIZE);
if (!nb2)
return VasEBoot_errno;
err = VasEBoot_netbuff_reserve (nb2, VAS_EBOOT_NET_MAX_LINK_HEADER_SIZE
+ VAS_EBOOT_NET_OUR_MAX_IP_HEADER_SIZE);
if (err)
return err;
err = VasEBoot_netbuff_put (nb2, sizeof (*tcph));
if (err)
return err;
tcph = (struct tcphdr *) nb2->data;
tcph->ack = VasEBoot_cpu_to_be32 (socket->their_cur_seq);
tcph->flags = VasEBoot_cpu_to_be16_compile_time ((5 << 12) | TCP_ACK);
tcph->window = !socket->i_stall ? VasEBoot_cpu_to_be16 (socket->my_window)
: 0;
tcph->urgent = 0;
err = VasEBoot_netbuff_put (nb2, fraglen);
if (err)
return err;
VasEBoot_memcpy (tcph + 1, nb->data, fraglen);
err = VasEBoot_netbuff_pull (nb, fraglen);
if (err)
return err;
err = tcp_send (nb2, socket);
if (err)
return err;
}
err = VasEBoot_netbuff_push (nb, sizeof (*tcph));
if (err)
return err;
tcph = (struct tcphdr *) nb->data;
tcph->ack = VasEBoot_cpu_to_be32 (socket->their_cur_seq);
tcph->flags = (VasEBoot_cpu_to_be16_compile_time ((5 << 12) | TCP_ACK)
| (push ? VasEBoot_cpu_to_be16_compile_time (TCP_PUSH) : 0));
tcph->window = !socket->i_stall ? VasEBoot_cpu_to_be16 (socket->my_window) : 0;
tcph->urgent = 0;
return tcp_send (nb, socket);
}
VasEBoot_err_t
VasEBoot_net_recv_tcp_packet (struct VasEBoot_net_buff *nb,
struct VasEBoot_net_network_level_interface *inf,
const VasEBoot_net_network_level_address_t *source)
{
struct tcphdr *tcph;
VasEBoot_net_tcp_socket_t sock;
VasEBoot_err_t err;
/* Ignore broadcast. */
if (!inf)
{
VasEBoot_netbuff_free (nb);
return VAS_EBOOT_ERR_NONE;
}
tcph = (struct tcphdr *) nb->data;
if ((VasEBoot_be_to_cpu16 (tcph->flags) >> 12) < 5)
{
VasEBoot_dprintf ("net", "TCP header too short: %u\n",
VasEBoot_be_to_cpu16 (tcph->flags) >> 12);
VasEBoot_netbuff_free (nb);
return VAS_EBOOT_ERR_NONE;
}
if (nb->tail - nb->data < (VasEBoot_ssize_t) ((VasEBoot_be_to_cpu16 (tcph->flags)
>> 12) * sizeof (VasEBoot_uint32_t)))
{
VasEBoot_dprintf ("net", "TCP packet too short: %" PRIuVAS_EBOOT_SIZE "\n",
(VasEBoot_size_t) (nb->tail - nb->data));
VasEBoot_netbuff_free (nb);
return VAS_EBOOT_ERR_NONE;
}
FOR_TCP_SOCKETS (sock)
{
if (!(VasEBoot_be_to_cpu16 (tcph->dst) == sock->in_port
&& VasEBoot_be_to_cpu16 (tcph->src) == sock->out_port
&& inf == sock->inf
&& VasEBoot_net_addr_cmp (source, &sock->out_nla) == 0))
continue;
if (tcph->checksum)
{
VasEBoot_uint16_t chk, expected;
chk = tcph->checksum;
tcph->checksum = 0;
expected = VasEBoot_net_ip_transport_checksum (nb, VAS_EBOOT_NET_IP_TCP,
&sock->out_nla,
&sock->inf->address);
if (expected != chk)
{
VasEBoot_dprintf ("net", "Invalid TCP checksum. "
"Expected %x, got %x\n",
VasEBoot_be_to_cpu16 (expected),
VasEBoot_be_to_cpu16 (chk));
VasEBoot_netbuff_free (nb);
return VAS_EBOOT_ERR_NONE;
}
tcph->checksum = chk;
}
if ((VasEBoot_be_to_cpu16 (tcph->flags) & TCP_SYN)
&& (VasEBoot_be_to_cpu16 (tcph->flags) & TCP_ACK)
&& !sock->established)
{
sock->their_start_seq = VasEBoot_be_to_cpu32 (tcph->seqnr);
sock->their_cur_seq = sock->their_start_seq + 1;
sock->established = 1;
}
if (VasEBoot_be_to_cpu16 (tcph->flags) & TCP_RST)
{
sock->they_reseted = 1;
error (sock);
VasEBoot_netbuff_free (nb);
return VAS_EBOOT_ERR_NONE;
}
if (VasEBoot_be_to_cpu16 (tcph->flags) & TCP_ACK)
{
struct unacked *unack, *next;
VasEBoot_uint32_t acked = VasEBoot_be_to_cpu32 (tcph->ack);
for (unack = sock->unack_first; unack; unack = next)
{
VasEBoot_uint32_t seqnr;
struct tcphdr *unack_tcph;
next = unack->next;
seqnr = VasEBoot_be_to_cpu32 (((struct tcphdr *) unack->nb->data)
->seqnr);
unack_tcph = (struct tcphdr *) unack->nb->data;
seqnr += (unack->nb->tail - unack->nb->data
- (VasEBoot_be_to_cpu16 (unack_tcph->flags) >> 12) * 4);
if (VasEBoot_be_to_cpu16 (unack_tcph->flags) & TCP_FIN)
seqnr++;
if (seqnr > acked)
break;
VasEBoot_netbuff_free (unack->nb);
VasEBoot_free (unack);
}
sock->unack_first = unack;
if (!sock->unack_first)
sock->unack_last = NULL;
}
if (VasEBoot_be_to_cpu32 (tcph->seqnr) < sock->their_cur_seq)
{
ack (sock);
VasEBoot_netbuff_free (nb);
return VAS_EBOOT_ERR_NONE;
}
if (sock->i_reseted && (nb->tail - nb->data
- (VasEBoot_be_to_cpu16 (tcph->flags)
>> 12) * sizeof (VasEBoot_uint32_t)) > 0)
{
reset (sock);
}
err = VasEBoot_priority_queue_push (sock->pq, &nb);
if (err)
{
VasEBoot_netbuff_free (nb);
return err;
}
{
struct VasEBoot_net_buff **nb_top_p, *nb_top;
int do_ack = 0;
int just_closed = 0;
while (1)
{
nb_top_p = VasEBoot_priority_queue_top (sock->pq);
if (!nb_top_p)
return VAS_EBOOT_ERR_NONE;
nb_top = *nb_top_p;
tcph = (struct tcphdr *) nb_top->data;
if (VasEBoot_be_to_cpu32 (tcph->seqnr) >= sock->their_cur_seq)
break;
VasEBoot_netbuff_free (nb_top);
VasEBoot_priority_queue_pop (sock->pq);
}
if (VasEBoot_be_to_cpu32 (tcph->seqnr) != sock->their_cur_seq)
{
ack (sock);
return VAS_EBOOT_ERR_NONE;
}
while (1)
{
nb_top_p = VasEBoot_priority_queue_top (sock->pq);
if (!nb_top_p)
break;
nb_top = *nb_top_p;
tcph = (struct tcphdr *) nb_top->data;
if (VasEBoot_be_to_cpu32 (tcph->seqnr) != sock->their_cur_seq)
break;
VasEBoot_priority_queue_pop (sock->pq);
err = VasEBoot_netbuff_pull (nb_top, (VasEBoot_be_to_cpu16 (tcph->flags)
>> 12) * sizeof (VasEBoot_uint32_t));
if (err)
{
VasEBoot_netbuff_free (nb_top);
return err;
}
sock->their_cur_seq += (nb_top->tail - nb_top->data);
if (VasEBoot_be_to_cpu16 (tcph->flags) & TCP_FIN)
{
sock->they_closed = 1;
just_closed = 1;
sock->their_cur_seq++;
do_ack = 1;
}
/* If there is data, puts packet in socket list. */
if ((nb_top->tail - nb_top->data) > 0)
{
VasEBoot_net_put_packet (&sock->packs, nb_top);
do_ack = 1;
}
else
VasEBoot_netbuff_free (nb_top);
}
if (do_ack)
ack (sock);
while (sock->packs.first)
{
nb = sock->packs.first->nb;
if (sock->recv_hook)
sock->recv_hook (sock, sock->packs.first->nb, sock->hook_data);
else
VasEBoot_netbuff_free (nb);
VasEBoot_net_remove_packet (sock->packs.first);
}
if (sock->fin_hook && just_closed)
sock->fin_hook (sock, sock->hook_data);
}
return VAS_EBOOT_ERR_NONE;
}
if (VasEBoot_be_to_cpu16 (tcph->flags) & TCP_SYN)
{
VasEBoot_net_tcp_listen_t listen;
FOR_TCP_LISTENS (listen)
{
if (!(VasEBoot_be_to_cpu16 (tcph->dst) == listen->port
&& (inf == listen->inf || listen->inf == NULL)))
continue;
sock = VasEBoot_zalloc (sizeof (*sock));
if (sock == NULL)
return VasEBoot_errno;
sock->out_port = VasEBoot_be_to_cpu16 (tcph->src);
sock->in_port = VasEBoot_be_to_cpu16 (tcph->dst);
sock->inf = inf;
sock->out_nla = *source;
sock->their_start_seq = VasEBoot_be_to_cpu32 (tcph->seqnr);
sock->their_cur_seq = sock->their_start_seq + 1;
sock->my_cur_seq = sock->my_start_seq = VasEBoot_get_time_ms ();
sock->my_window = 8192;
sock->pq = VasEBoot_priority_queue_new (sizeof (struct VasEBoot_net_buff *),
cmp);
if (!sock->pq)
{
VasEBoot_free (sock);
VasEBoot_netbuff_free (nb);
return VasEBoot_errno;
}
err = listen->listen_hook (listen, sock, listen->hook_data);
VasEBoot_netbuff_free (nb);
return err;
}
}
VasEBoot_netbuff_free (nb);
return VAS_EBOOT_ERR_NONE;
}
void
VasEBoot_net_tcp_stall (VasEBoot_net_tcp_socket_t sock)
{
if (sock->i_stall)
return;
sock->i_stall = 1;
ack (sock);
}
void
VasEBoot_net_tcp_unstall (VasEBoot_net_tcp_socket_t sock)
{
if (!sock->i_stall)
return;
sock->i_stall = 0;
ack (sock);
}