2N/A/*
2N/A * GRUB -- GRand Unified Bootloader
2N/A * Copyright (C) 2010,2011 Free Software Foundation, Inc.
2N/A * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
2N/A *
2N/A * GRUB is free software: you can redistribute it and/or modify
2N/A * it under the terms of the GNU General Public License as published by
2N/A * the Free Software Foundation, either version 3 of the License, or
2N/A * (at your option) any later version.
2N/A *
2N/A * GRUB is distributed in the hope that it will be useful,
2N/A * but WITHOUT ANY WARRANTY; without even the implied warranty of
2N/A * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2N/A * GNU General Public License for more details.
2N/A *
2N/A * You should have received a copy of the GNU General Public License
2N/A * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
2N/A */
2N/A
2N/A#include <grub/net.h>
2N/A#include <grub/net/ip.h>
2N/A#include <grub/net/tcp.h>
2N/A#include <grub/net/netbuff.h>
2N/A#include <grub/time.h>
2N/A#include <grub/priority_queue.h>
2N/A
2N/A#define TCP_SYN_RETRANSMISSION_TIMEOUT GRUB_NET_INTERVAL
2N/A#define TCP_SYN_RETRANSMISSION_COUNT GRUB_NET_TRIES
2N/A#define TCP_RETRANSMISSION_TIMEOUT GRUB_NET_INTERVAL
2N/A#define TCP_RETRANSMISSION_COUNT GRUB_NET_TRIES
2N/A
2N/Astruct unacked
2N/A{
2N/A struct unacked *next;
2N/A struct grub_net_buff *nb;
2N/A grub_uint64_t last_try;
2N/A int try_count;
2N/A};
2N/A
2N/Aenum
2N/A {
2N/A TCP_FIN = 0x1,
2N/A TCP_SYN = 0x2,
2N/A TCP_RST = 0x4,
2N/A TCP_PUSH = 0x8,
2N/A TCP_ACK = 0x10,
2N/A TCP_URG = 0x20,
2N/A };
2N/A
2N/Astruct grub_net_tcp_socket
2N/A{
2N/A struct grub_net_tcp_socket *next;
2N/A
2N/A int established;
2N/A int i_closed;
2N/A int they_closed;
2N/A int in_port;
2N/A int out_port;
2N/A int errors;
2N/A int they_reseted;
2N/A int i_reseted;
2N/A grub_uint32_t my_start_seq;
2N/A grub_uint32_t my_cur_seq;
2N/A grub_uint32_t their_start_seq;
2N/A grub_uint32_t their_cur_seq;
2N/A grub_uint16_t my_window;
2N/A struct unacked *unack_first;
2N/A struct unacked *unack_last;
2N/A grub_err_t (*recv_hook) (grub_net_tcp_socket_t sock, struct grub_net_buff *nb,
2N/A void *recv);
2N/A void (*error_hook) (grub_net_tcp_socket_t sock, void *recv);
2N/A void (*fin_hook) (grub_net_tcp_socket_t sock, void *recv);
2N/A void *hook_data;
2N/A grub_net_network_level_address_t out_nla;
2N/A grub_net_link_level_address_t ll_target_addr;
2N/A struct grub_net_network_level_interface *inf;
2N/A grub_net_packets_t packs;
2N/A grub_priority_queue_t pq;
2N/A};
2N/A
2N/Astruct grub_net_tcp_listen
2N/A{
2N/A struct grub_net_tcp_listen *next;
2N/A
2N/A grub_uint16_t port;
2N/A const struct grub_net_network_level_interface *inf;
2N/A
2N/A grub_err_t (*listen_hook) (grub_net_tcp_listen_t listen,
2N/A grub_net_tcp_socket_t sock,
2N/A void *data);
2N/A void *hook_data;
2N/A};
2N/A
2N/Astruct tcphdr
2N/A{
2N/A grub_uint16_t src;
2N/A grub_uint16_t dst;
2N/A grub_uint32_t seqnr;
2N/A grub_uint32_t ack;
2N/A grub_uint16_t flags;
2N/A grub_uint16_t window;
2N/A grub_uint16_t checksum;
2N/A grub_uint16_t urgent;
2N/A} __attribute__ ((packed));
2N/A
2N/Astruct tcp_pseudohdr
2N/A{
2N/A grub_uint32_t src;
2N/A grub_uint32_t dst;
2N/A grub_uint8_t zero;
2N/A grub_uint8_t proto;
2N/A grub_uint16_t tcp_length;
2N/A} __attribute__ ((packed));
2N/A
2N/Astruct tcp6_pseudohdr
2N/A{
2N/A grub_uint64_t src[2];
2N/A grub_uint64_t dst[2];
2N/A grub_uint32_t tcp_length;
2N/A grub_uint8_t zero[3];
2N/A grub_uint8_t proto;
2N/A} __attribute__ ((packed));
2N/A
2N/Astatic struct grub_net_tcp_socket *tcp_sockets;
2N/Astatic struct grub_net_tcp_listen *tcp_listens;
2N/A
2N/A#define FOR_TCP_SOCKETS(var) FOR_LIST_ELEMENTS (var, tcp_sockets)
2N/A#define FOR_TCP_LISTENS(var) FOR_LIST_ELEMENTS (var, tcp_listens)
2N/A
2N/Agrub_net_tcp_listen_t
2N/Agrub_net_tcp_listen (grub_uint16_t port,
2N/A const struct grub_net_network_level_interface *inf,
2N/A grub_err_t (*listen_hook) (grub_net_tcp_listen_t listen,
2N/A grub_net_tcp_socket_t sock,
2N/A void *data),
2N/A void *hook_data)
2N/A{
2N/A grub_net_tcp_listen_t ret;
2N/A ret = grub_malloc (sizeof (*ret));
2N/A if (!ret)
2N/A return NULL;
2N/A ret->listen_hook = listen_hook;
2N/A ret->hook_data = hook_data;
2N/A ret->port = port;
2N/A ret->inf = inf;
2N/A grub_list_push (GRUB_AS_LIST_P (&tcp_listens), GRUB_AS_LIST (ret));
2N/A return ret;
2N/A}
2N/A
2N/Avoid
2N/Agrub_net_tcp_stop_listen (grub_net_tcp_listen_t listen)
2N/A{
2N/A grub_list_remove (GRUB_AS_LIST_P (&tcp_listens),
2N/A GRUB_AS_LIST (listen));
2N/A}
2N/A
2N/Astatic inline void
2N/Atcp_socket_register (grub_net_tcp_socket_t sock)
2N/A{
2N/A grub_list_push (GRUB_AS_LIST_P (&tcp_sockets),
2N/A GRUB_AS_LIST (sock));
2N/A}
2N/A
2N/Astatic void
2N/Aerror (grub_net_tcp_socket_t sock)
2N/A{
2N/A struct unacked *unack, *next;
2N/A
2N/A if (sock->error_hook)
2N/A sock->error_hook (sock, sock->hook_data);
2N/A
2N/A for (unack = sock->unack_first; unack; unack = next)
2N/A {
2N/A next = unack->next;
2N/A grub_netbuff_free (unack->nb);
2N/A grub_free (unack);
2N/A }
2N/A
2N/A sock->unack_first = NULL;
2N/A sock->unack_last = NULL;
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Atcp_send (struct grub_net_buff *nb, grub_net_tcp_socket_t socket)
2N/A{
2N/A grub_err_t err;
2N/A grub_uint8_t *nbd;
2N/A struct unacked *unack;
2N/A struct tcphdr *tcph;
2N/A grub_size_t size;
2N/A
2N/A tcph = (struct tcphdr *) nb->data;
2N/A
2N/A tcph->seqnr = grub_cpu_to_be32 (socket->my_cur_seq);
2N/A size = (nb->tail - nb->data - (grub_be_to_cpu16 (tcph->flags) >> 12) * 4);
2N/A if (grub_be_to_cpu16 (tcph->flags) & TCP_FIN)
2N/A size++;
2N/A socket->my_cur_seq += size;
2N/A tcph->src = grub_cpu_to_be16 (socket->in_port);
2N/A tcph->dst = grub_cpu_to_be16 (socket->out_port);
2N/A tcph->checksum = 0;
2N/A tcph->checksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_TCP,
2N/A &socket->inf->address.addr,
2N/A &socket->out_nla);
2N/A nbd = nb->data;
2N/A if (size)
2N/A {
2N/A unack = grub_malloc (sizeof (*unack));
2N/A if (!unack)
2N/A return grub_errno;
2N/A
2N/A unack->next = NULL;
2N/A unack->nb = nb;
2N/A unack->try_count = 1;
2N/A unack->last_try = grub_get_time_ms ();
2N/A if (!socket->unack_last)
2N/A socket->unack_first = socket->unack_last = unack;
2N/A else
2N/A socket->unack_last->next = unack;
2N/A }
2N/A
2N/A err = grub_net_send_ip_packet (socket->inf, &(socket->out_nla),
2N/A &(socket->ll_target_addr), nb,
2N/A GRUB_NET_IP_TCP);
2N/A if (err)
2N/A return err;
2N/A nb->data = nbd;
2N/A if (!size)
2N/A grub_netbuff_free (nb);
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/Avoid
2N/Agrub_net_tcp_close (grub_net_tcp_socket_t sock,
2N/A int discard_received)
2N/A{
2N/A struct grub_net_buff *nb_fin;
2N/A struct tcphdr *tcph_fin;
2N/A grub_err_t err;
2N/A
2N/A if (discard_received != GRUB_NET_TCP_CONTINUE_RECEIVING)
2N/A {
2N/A sock->recv_hook = NULL;
2N/A sock->error_hook = NULL;
2N/A sock->fin_hook = NULL;
2N/A }
2N/A
2N/A if (discard_received == GRUB_NET_TCP_ABORT)
2N/A sock->i_reseted = 1;
2N/A
2N/A if (sock->i_closed)
2N/A return;
2N/A
2N/A sock->i_closed = 1;
2N/A
2N/A nb_fin = grub_netbuff_alloc (sizeof (*tcph_fin)
2N/A + GRUB_NET_OUR_MAX_IP_HEADER_SIZE
2N/A + GRUB_NET_MAX_LINK_HEADER_SIZE);
2N/A if (!nb_fin)
2N/A return;
2N/A err = grub_netbuff_reserve (nb_fin, GRUB_NET_OUR_MAX_IP_HEADER_SIZE
2N/A + GRUB_NET_MAX_LINK_HEADER_SIZE);
2N/A if (err)
2N/A {
2N/A grub_netbuff_free (nb_fin);
2N/A grub_dprintf ("net", "error closing socket\n");
2N/A grub_errno = GRUB_ERR_NONE;
2N/A return;
2N/A }
2N/A
2N/A err = grub_netbuff_put (nb_fin, sizeof (*tcph_fin));
2N/A if (err)
2N/A {
2N/A grub_netbuff_free (nb_fin);
2N/A grub_dprintf ("net", "error closing socket\n");
2N/A grub_errno = GRUB_ERR_NONE;
2N/A return;
2N/A }
2N/A tcph_fin = (void *) nb_fin->data;
2N/A tcph_fin->ack = grub_cpu_to_be32 (sock->their_cur_seq);
2N/A tcph_fin->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_FIN
2N/A | TCP_ACK);
2N/A tcph_fin->window = grub_cpu_to_be16_compile_time (0);
2N/A tcph_fin->urgent = 0;
2N/A err = tcp_send (nb_fin, sock);
2N/A if (err)
2N/A {
2N/A grub_netbuff_free (nb_fin);
2N/A grub_dprintf ("net", "error closing socket\n");
2N/A grub_errno = GRUB_ERR_NONE;
2N/A }
2N/A return;
2N/A}
2N/A
2N/Astatic void
2N/Aack_real (grub_net_tcp_socket_t sock, int res)
2N/A{
2N/A struct grub_net_buff *nb_ack;
2N/A struct tcphdr *tcph_ack;
2N/A grub_err_t err;
2N/A
2N/A nb_ack = grub_netbuff_alloc (sizeof (*tcph_ack) + 128);
2N/A if (!nb_ack)
2N/A return;
2N/A err = grub_netbuff_reserve (nb_ack, 128);
2N/A if (err)
2N/A {
2N/A grub_netbuff_free (nb_ack);
2N/A grub_dprintf ("net", "error closing socket\n");
2N/A grub_errno = GRUB_ERR_NONE;
2N/A return;
2N/A }
2N/A
2N/A err = grub_netbuff_put (nb_ack, sizeof (*tcph_ack));
2N/A if (err)
2N/A {
2N/A grub_netbuff_free (nb_ack);
2N/A grub_dprintf ("net", "error closing socket\n");
2N/A grub_errno = GRUB_ERR_NONE;
2N/A return;
2N/A }
2N/A tcph_ack = (void *) nb_ack->data;
2N/A if (res)
2N/A {
2N/A tcph_ack->ack = grub_cpu_to_be32_compile_time (0);
2N/A tcph_ack->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_RST);
2N/A tcph_ack->window = grub_cpu_to_be16_compile_time (0);
2N/A }
2N/A else
2N/A {
2N/A tcph_ack->ack = grub_cpu_to_be32 (sock->their_cur_seq);
2N/A tcph_ack->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_ACK);
2N/A tcph_ack->window = grub_cpu_to_be16 (sock->my_window);
2N/A }
2N/A tcph_ack->urgent = 0;
2N/A tcph_ack->src = grub_cpu_to_be16 (sock->in_port);
2N/A tcph_ack->dst = grub_cpu_to_be16 (sock->out_port);
2N/A err = tcp_send (nb_ack, sock);
2N/A if (err)
2N/A {
2N/A grub_dprintf ("net", "error acking socket\n");
2N/A grub_errno = GRUB_ERR_NONE;
2N/A }
2N/A}
2N/A
2N/Astatic void
2N/Aack (grub_net_tcp_socket_t sock)
2N/A{
2N/A ack_real (sock, 0);
2N/A}
2N/A
2N/Astatic void
2N/Areset (grub_net_tcp_socket_t sock)
2N/A{
2N/A ack_real (sock, 1);
2N/A}
2N/A
2N/Avoid
2N/Agrub_net_tcp_retransmit (void)
2N/A{
2N/A grub_net_tcp_socket_t sock;
2N/A grub_uint64_t ctime = grub_get_time_ms ();
2N/A grub_uint64_t limit_time = ctime - TCP_RETRANSMISSION_TIMEOUT;
2N/A
2N/A FOR_TCP_SOCKETS (sock)
2N/A {
2N/A struct unacked *unack;
2N/A for (unack = sock->unack_first; unack; unack = unack->next)
2N/A {
2N/A struct tcphdr *tcph;
2N/A grub_uint8_t *nbd;
2N/A grub_err_t err;
2N/A
2N/A if (unack->last_try > limit_time)
2N/A continue;
2N/A
2N/A if (unack->try_count > TCP_RETRANSMISSION_COUNT)
2N/A {
2N/A error (sock);
2N/A break;
2N/A }
2N/A unack->try_count++;
2N/A unack->last_try = ctime;
2N/A nbd = unack->nb->data;
2N/A tcph = (struct tcphdr *) nbd;
2N/A
2N/A if ((tcph->flags & grub_cpu_to_be16_compile_time (TCP_ACK))
2N/A && tcph->ack != grub_cpu_to_be32 (sock->their_cur_seq))
2N/A {
2N/A tcph->checksum = 0;
2N/A tcph->checksum = grub_net_ip_transport_checksum (unack->nb,
2N/A GRUB_NET_IP_TCP,
2N/A &sock->inf->address.addr,
2N/A &sock->out_nla);
2N/A }
2N/A
2N/A err = grub_net_send_ip_packet (sock->inf, &(sock->out_nla),
2N/A &(sock->ll_target_addr), unack->nb,
2N/A GRUB_NET_IP_TCP);
2N/A unack->nb->data = nbd;
2N/A if (err)
2N/A {
2N/A grub_dprintf ("net", "TCP retransmit failed: %s\n", grub_errmsg);
2N/A grub_errno = GRUB_ERR_NONE;
2N/A }
2N/A }
2N/A }
2N/A}
2N/A
2N/Agrub_uint16_t
2N/Agrub_net_ip_transport_checksum (struct grub_net_buff *nb,
2N/A grub_uint16_t proto,
2N/A const grub_net_network_level_address_t *src,
2N/A const grub_net_network_level_address_t *dst)
2N/A{
2N/A grub_uint16_t a, b = 0;
2N/A grub_uint32_t c;
2N/A a = ~grub_be_to_cpu16 (grub_net_ip_chksum ((void *) nb->data,
2N/A nb->tail - nb->data));
2N/A
2N/A switch (dst->type)
2N/A {
2N/A case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4:
2N/A {
2N/A struct tcp_pseudohdr ph;
2N/A ph.src = src->ipv4;
2N/A ph.dst = dst->ipv4;
2N/A ph.zero = 0;
2N/A ph.tcp_length = grub_cpu_to_be16 (nb->tail - nb->data);
2N/A ph.proto = proto;
2N/A b = ~grub_be_to_cpu16 (grub_net_ip_chksum ((void *) &ph, sizeof (ph)));
2N/A break;
2N/A }
2N/A case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6:
2N/A {
2N/A struct tcp6_pseudohdr ph;
2N/A grub_memcpy (ph.src, src->ipv6, sizeof (ph.src));
2N/A grub_memcpy (ph.dst, dst->ipv6, sizeof (ph.dst));
2N/A grub_memset (ph.zero, 0, sizeof (ph.zero));
2N/A ph.tcp_length = grub_cpu_to_be32 (nb->tail - nb->data);
2N/A ph.proto = proto;
2N/A b = ~grub_be_to_cpu16 (grub_net_ip_chksum ((void *) &ph, sizeof (ph)));
2N/A break;
2N/A }
2N/A case GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV:
2N/A b = 0;
2N/A break;
2N/A }
2N/A c = (grub_uint32_t) a + (grub_uint32_t) b;
2N/A if (c >= 0xffff)
2N/A c -= 0xffff;
2N/A return grub_cpu_to_be16 (~c);
2N/A}
2N/A
2N/A/* FIXME: overflow. */
2N/Astatic int
2N/Acmp (const void *a__, const void *b__)
2N/A{
2N/A struct grub_net_buff *a_ = *(struct grub_net_buff **) a__;
2N/A struct grub_net_buff *b_ = *(struct grub_net_buff **) b__;
2N/A struct tcphdr *a = (struct tcphdr *) a_->data;
2N/A struct tcphdr *b = (struct tcphdr *) b_->data;
2N/A /* We want the first elements to be on top. */
2N/A if (grub_be_to_cpu32 (a->seqnr) < grub_be_to_cpu32 (b->seqnr))
2N/A return +1;
2N/A if (grub_be_to_cpu32 (a->seqnr) > grub_be_to_cpu32 (b->seqnr))
2N/A return -1;
2N/A return 0;
2N/A}
2N/A
2N/Astatic void
2N/Adestroy_pq (grub_net_tcp_socket_t sock)
2N/A{
2N/A struct grub_net_buff **nb_p;
2N/A while ((nb_p = grub_priority_queue_top (sock->pq)))
2N/A {
2N/A grub_netbuff_free (*nb_p);
2N/A grub_priority_queue_pop (sock->pq);
2N/A }
2N/A
2N/A grub_priority_queue_destroy (sock->pq);
2N/A}
2N/A
2N/Agrub_err_t
2N/Agrub_net_tcp_accept (grub_net_tcp_socket_t sock,
2N/A grub_err_t (*recv_hook) (grub_net_tcp_socket_t sock,
2N/A struct grub_net_buff *nb,
2N/A void *data),
2N/A void (*error_hook) (grub_net_tcp_socket_t sock,
2N/A void *data),
2N/A void (*fin_hook) (grub_net_tcp_socket_t sock,
2N/A void *data),
2N/A void *hook_data)
2N/A{
2N/A struct grub_net_buff *nb_ack;
2N/A struct tcphdr *tcph;
2N/A grub_err_t err;
2N/A
2N/A sock->recv_hook = recv_hook;
2N/A sock->error_hook = error_hook;
2N/A sock->fin_hook = fin_hook;
2N/A sock->hook_data = hook_data;
2N/A nb_ack = grub_netbuff_alloc (sizeof (*tcph)
2N/A + GRUB_NET_OUR_MAX_IP_HEADER_SIZE
2N/A + GRUB_NET_MAX_LINK_HEADER_SIZE);
2N/A if (!nb_ack)
2N/A return grub_errno;
2N/A err = grub_netbuff_reserve (nb_ack, GRUB_NET_OUR_MAX_IP_HEADER_SIZE
2N/A + GRUB_NET_MAX_LINK_HEADER_SIZE);
2N/A if (err)
2N/A {
2N/A grub_netbuff_free (nb_ack);
2N/A return err;
2N/A }
2N/A
2N/A err = grub_netbuff_put (nb_ack, sizeof (*tcph));
2N/A if (err)
2N/A {
2N/A grub_netbuff_free (nb_ack);
2N/A return err;
2N/A }
2N/A tcph = (void *) nb_ack->data;
2N/A tcph->ack = grub_cpu_to_be32 (sock->their_cur_seq);
2N/A tcph->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_SYN | TCP_ACK);
2N/A tcph->window = grub_cpu_to_be16 (sock->my_window);
2N/A tcph->urgent = 0;
2N/A sock->established = 1;
2N/A tcp_socket_register (sock);
2N/A err = tcp_send (nb_ack, sock);
2N/A if (err)
2N/A return err;
2N/A sock->my_cur_seq++;
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/Agrub_net_tcp_socket_t
2N/Agrub_net_tcp_open (char *server,
2N/A grub_uint16_t out_port,
2N/A grub_err_t (*recv_hook) (grub_net_tcp_socket_t sock,
2N/A struct grub_net_buff *nb,
2N/A void *data),
2N/A void (*error_hook) (grub_net_tcp_socket_t sock,
2N/A void *data),
2N/A void (*fin_hook) (grub_net_tcp_socket_t sock,
2N/A void *data),
2N/A void *hook_data)
2N/A{
2N/A grub_err_t err;
2N/A grub_net_network_level_address_t addr;
2N/A struct grub_net_network_level_interface *inf;
2N/A grub_net_network_level_address_t gateway;
2N/A grub_net_tcp_socket_t socket;
2N/A static grub_uint16_t in_port = 21550;
2N/A struct grub_net_buff *nb;
2N/A struct tcphdr *tcph;
2N/A int i;
2N/A grub_uint8_t *nbd;
2N/A grub_net_link_level_address_t ll_target_addr;
2N/A
2N/A err = grub_net_resolve_address (server, &addr);
2N/A if (err)
2N/A return NULL;
2N/A
2N/A if (addr.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4
2N/A && addr.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6)
2N/A {
2N/A grub_error (GRUB_ERR_BAD_ARGUMENT, "not an IP address");
2N/A return NULL;
2N/A }
2N/A
2N/A err = grub_net_route_address (addr, &gateway, &inf);
2N/A if (err)
2N/A return NULL;
2N/A
2N/A err = grub_net_link_layer_resolve (inf, &gateway, &ll_target_addr);
2N/A if (err)
2N/A return NULL;
2N/A
2N/A socket = grub_zalloc (sizeof (*socket));
2N/A if (socket == NULL)
2N/A return NULL;
2N/A
2N/A socket->out_port = out_port;
2N/A socket->inf = inf;
2N/A socket->out_nla = addr;
2N/A socket->ll_target_addr = ll_target_addr;
2N/A socket->in_port = in_port++;
2N/A socket->recv_hook = recv_hook;
2N/A socket->error_hook = error_hook;
2N/A socket->fin_hook = fin_hook;
2N/A socket->hook_data = hook_data;
2N/A
2N/A nb = grub_netbuff_alloc (sizeof (*tcph) + 128);
2N/A if (!nb)
2N/A return NULL;
2N/A err = grub_netbuff_reserve (nb, 128);
2N/A if (err)
2N/A {
2N/A grub_netbuff_free (nb);
2N/A return NULL;
2N/A }
2N/A
2N/A err = grub_netbuff_put (nb, sizeof (*tcph));
2N/A if (err)
2N/A {
2N/A grub_netbuff_free (nb);
2N/A return NULL;
2N/A }
2N/A socket->pq = grub_priority_queue_new (sizeof (struct grub_net_buff *), cmp);
2N/A if (!socket->pq)
2N/A {
2N/A grub_netbuff_free (nb);
2N/A return NULL;
2N/A }
2N/A
2N/A tcph = (void *) nb->data;
2N/A socket->my_start_seq = grub_get_time_ms ();
2N/A socket->my_cur_seq = socket->my_start_seq + 1;
2N/A socket->my_window = 8192;
2N/A tcph->seqnr = grub_cpu_to_be32 (socket->my_start_seq);
2N/A tcph->ack = grub_cpu_to_be32_compile_time (0);
2N/A tcph->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_SYN);
2N/A tcph->window = grub_cpu_to_be16 (socket->my_window);
2N/A tcph->urgent = 0;
2N/A tcph->src = grub_cpu_to_be16 (socket->in_port);
2N/A tcph->dst = grub_cpu_to_be16 (socket->out_port);
2N/A tcph->checksum = 0;
2N/A tcph->checksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_TCP,
2N/A &socket->inf->address.addr,
2N/A &socket->out_nla);
2N/A
2N/A tcp_socket_register (socket);
2N/A
2N/A nbd = nb->data;
2N/A for (i = 0; i < TCP_SYN_RETRANSMISSION_COUNT; i++)
2N/A {
2N/A int j;
2N/A nb->data = nbd;
2N/A err = grub_net_send_ip_packet (socket->inf, &(socket->out_nla),
2N/A &(socket->ll_target_addr), nb,
2N/A GRUB_NET_IP_TCP);
2N/A if (err)
2N/A {
2N/A grub_list_remove (GRUB_AS_LIST_P (&tcp_sockets),
2N/A GRUB_AS_LIST (socket));
2N/A grub_free (socket);
2N/A grub_netbuff_free (nb);
2N/A return NULL;
2N/A }
2N/A for (j = 0; (j < TCP_SYN_RETRANSMISSION_TIMEOUT / 50
2N/A && !socket->established); j++)
2N/A grub_net_poll_cards (50);
2N/A if (socket->established)
2N/A break;
2N/A }
2N/A if (!socket->established)
2N/A {
2N/A grub_list_remove (GRUB_AS_LIST_P (&tcp_sockets),
2N/A GRUB_AS_LIST (socket));
2N/A if (socket->they_reseted)
2N/A grub_error (GRUB_ERR_NET_PORT_CLOSED, "port closed");
2N/A else
2N/A grub_error (GRUB_ERR_NET_NO_ANSWER, "no answer");
2N/A
2N/A grub_netbuff_free (nb);
2N/A destroy_pq (socket);
2N/A grub_free (socket);
2N/A return NULL;
2N/A }
2N/A
2N/A grub_netbuff_free (nb);
2N/A return socket;
2N/A}
2N/A
2N/Agrub_err_t
2N/Agrub_net_send_tcp_packet (const grub_net_tcp_socket_t socket,
2N/A struct grub_net_buff *nb, int push)
2N/A{
2N/A struct tcphdr *tcph;
2N/A grub_err_t err;
2N/A grub_ssize_t fraglen;
2N/A COMPILE_TIME_ASSERT (sizeof (struct tcphdr) == GRUB_NET_TCP_HEADER_SIZE);
2N/A if (socket->out_nla.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4)
2N/A fraglen = (socket->inf->card->mtu - GRUB_NET_OUR_IPV4_HEADER_SIZE
2N/A - sizeof (*tcph));
2N/A else
2N/A fraglen = 1280 - GRUB_NET_OUR_IPV6_HEADER_SIZE;
2N/A
2N/A while (nb->tail - nb->data > fraglen)
2N/A {
2N/A struct grub_net_buff *nb2;
2N/A
2N/A nb2 = grub_netbuff_alloc (fraglen + sizeof (*tcph)
2N/A + GRUB_NET_OUR_MAX_IP_HEADER_SIZE
2N/A + GRUB_NET_MAX_LINK_HEADER_SIZE);
2N/A if (!nb2)
2N/A return grub_errno;
2N/A err = grub_netbuff_reserve (nb2, GRUB_NET_MAX_LINK_HEADER_SIZE
2N/A + GRUB_NET_OUR_MAX_IP_HEADER_SIZE);
2N/A if (err)
2N/A return err;
2N/A err = grub_netbuff_put (nb2, sizeof (*tcph));
2N/A if (err)
2N/A return err;
2N/A
2N/A tcph = (struct tcphdr *) nb2->data;
2N/A tcph->ack = grub_cpu_to_be32 (socket->their_cur_seq);
2N/A tcph->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_ACK);
2N/A tcph->window = grub_cpu_to_be16 (socket->my_window);
2N/A tcph->urgent = 0;
2N/A err = grub_netbuff_put (nb2, fraglen);
2N/A if (err)
2N/A return err;
2N/A grub_memcpy (tcph + 1, nb->data, fraglen);
2N/A err = grub_netbuff_pull (nb, fraglen);
2N/A if (err)
2N/A return err;
2N/A
2N/A err = tcp_send (nb2, socket);
2N/A if (err)
2N/A return err;
2N/A }
2N/A
2N/A err = grub_netbuff_push (nb, sizeof (*tcph));
2N/A if (err)
2N/A return err;
2N/A
2N/A tcph = (struct tcphdr *) nb->data;
2N/A tcph->ack = grub_cpu_to_be32 (socket->their_cur_seq);
2N/A tcph->flags = (grub_cpu_to_be16_compile_time ((5 << 12) | TCP_ACK)
2N/A | (push ? grub_cpu_to_be16_compile_time (TCP_PUSH) : 0));
2N/A tcph->window = grub_cpu_to_be16 (socket->my_window);
2N/A tcph->urgent = 0;
2N/A return tcp_send (nb, socket);
2N/A}
2N/A
2N/Agrub_err_t
2N/Agrub_net_recv_tcp_packet (struct grub_net_buff *nb,
2N/A struct grub_net_network_level_interface *inf,
2N/A const grub_net_network_level_address_t *source)
2N/A{
2N/A struct tcphdr *tcph;
2N/A grub_net_tcp_socket_t sock;
2N/A grub_err_t err;
2N/A
2N/A /* Ignore broadcast. */
2N/A if (!inf)
2N/A {
2N/A grub_netbuff_free (nb);
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A
2N/A tcph = (struct tcphdr *) nb->data;
2N/A if ((grub_be_to_cpu16 (tcph->flags) >> 12) < 5)
2N/A {
2N/A grub_dprintf ("net", "TCP header too short: %u\n",
2N/A grub_be_to_cpu16 (tcph->flags) >> 12);
2N/A grub_netbuff_free (nb);
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A if (nb->tail - nb->data < (grub_ssize_t) ((grub_be_to_cpu16 (tcph->flags)
2N/A >> 12) * sizeof (grub_uint32_t)))
2N/A {
2N/A grub_dprintf ("net", "TCP packet too short: %" PRIuGRUB_SIZE "\n",
2N/A nb->tail - nb->data);
2N/A grub_netbuff_free (nb);
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A
2N/A FOR_TCP_SOCKETS (sock)
2N/A {
2N/A if (!(grub_be_to_cpu16 (tcph->dst) == sock->in_port
2N/A && grub_be_to_cpu16 (tcph->src) == sock->out_port
2N/A && inf == sock->inf
2N/A && grub_net_addr_cmp (source, &sock->out_nla) == 0))
2N/A continue;
2N/A if (tcph->checksum)
2N/A {
2N/A grub_uint16_t chk, expected;
2N/A chk = tcph->checksum;
2N/A tcph->checksum = 0;
2N/A expected = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_TCP,
2N/A &sock->out_nla,
2N/A &sock->inf->address.addr);
2N/A if (expected != chk)
2N/A {
2N/A grub_dprintf ("net", "Invalid TCP checksum. "
2N/A "Expected %x, got %x\n",
2N/A grub_be_to_cpu16 (expected),
2N/A grub_be_to_cpu16 (chk));
2N/A grub_netbuff_free (nb);
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A tcph->checksum = chk;
2N/A }
2N/A
2N/A if ((grub_be_to_cpu16 (tcph->flags) & TCP_SYN)
2N/A && (grub_be_to_cpu16 (tcph->flags) & TCP_ACK)
2N/A && !sock->established)
2N/A {
2N/A sock->their_start_seq = grub_be_to_cpu32 (tcph->seqnr);
2N/A sock->their_cur_seq = sock->their_start_seq + 1;
2N/A sock->established = 1;
2N/A }
2N/A
2N/A if (grub_be_to_cpu16 (tcph->flags) & TCP_RST)
2N/A {
2N/A sock->they_reseted = 1;
2N/A
2N/A error (sock);
2N/A
2N/A grub_netbuff_free (nb);
2N/A
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A
2N/A if (grub_be_to_cpu16 (tcph->flags) & TCP_ACK)
2N/A {
2N/A struct unacked *unack, *next;
2N/A grub_uint32_t acked = grub_be_to_cpu32 (tcph->ack);
2N/A for (unack = sock->unack_first; unack; unack = next)
2N/A {
2N/A grub_uint32_t seqnr;
2N/A struct tcphdr *unack_tcph;
2N/A next = unack->next;
2N/A seqnr = grub_be_to_cpu32 (((struct tcphdr *) unack->nb->data)
2N/A ->seqnr);
2N/A unack_tcph = (struct tcphdr *) unack->nb->data;
2N/A seqnr += (unack->nb->tail - unack->nb->data
2N/A - (grub_be_to_cpu16 (unack_tcph->flags) >> 12) * 4);
2N/A if (grub_be_to_cpu16 (unack_tcph->flags) & TCP_FIN)
2N/A seqnr++;
2N/A
2N/A if (seqnr > acked)
2N/A break;
2N/A grub_netbuff_free (unack->nb);
2N/A grub_free (unack);
2N/A }
2N/A sock->unack_first = unack;
2N/A if (!sock->unack_first)
2N/A sock->unack_last = NULL;
2N/A }
2N/A
2N/A if (grub_be_to_cpu32 (tcph->seqnr) < sock->their_cur_seq)
2N/A {
2N/A ack (sock);
2N/A grub_netbuff_free (nb);
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A if (sock->i_reseted && (nb->tail - nb->data
2N/A - (grub_be_to_cpu16 (tcph->flags)
2N/A >> 12) * sizeof (grub_uint32_t)) > 0)
2N/A {
2N/A reset (sock);
2N/A }
2N/A
2N/A err = grub_priority_queue_push (sock->pq, &nb);
2N/A if (err)
2N/A {
2N/A grub_netbuff_free (nb);
2N/A return err;
2N/A }
2N/A
2N/A {
2N/A struct grub_net_buff **nb_top_p, *nb_top;
2N/A int do_ack = 0;
2N/A int just_closed = 0;
2N/A while (1)
2N/A {
2N/A nb_top_p = grub_priority_queue_top (sock->pq);
2N/A if (!nb_top_p)
2N/A return GRUB_ERR_NONE;
2N/A nb_top = *nb_top_p;
2N/A tcph = (struct tcphdr *) nb_top->data;
2N/A if (grub_be_to_cpu32 (tcph->seqnr) >= sock->their_cur_seq)
2N/A break;
2N/A grub_netbuff_free (nb_top);
2N/A grub_priority_queue_pop (sock->pq);
2N/A }
2N/A if (grub_be_to_cpu32 (tcph->seqnr) != sock->their_cur_seq)
2N/A return GRUB_ERR_NONE;
2N/A while (1)
2N/A {
2N/A nb_top_p = grub_priority_queue_top (sock->pq);
2N/A if (!nb_top_p)
2N/A break;
2N/A nb_top = *nb_top_p;
2N/A tcph = (struct tcphdr *) nb_top->data;
2N/A
2N/A if (grub_be_to_cpu32 (tcph->seqnr) != sock->their_cur_seq)
2N/A break;
2N/A grub_priority_queue_pop (sock->pq);
2N/A
2N/A err = grub_netbuff_pull (nb_top, (grub_be_to_cpu16 (tcph->flags)
2N/A >> 12) * sizeof (grub_uint32_t));
2N/A if (err)
2N/A {
2N/A grub_netbuff_free (nb_top);
2N/A return err;
2N/A }
2N/A
2N/A sock->their_cur_seq += (nb_top->tail - nb_top->data);
2N/A if (grub_be_to_cpu16 (tcph->flags) & TCP_FIN)
2N/A {
2N/A sock->they_closed = 1;
2N/A just_closed = 1;
2N/A sock->their_cur_seq++;
2N/A do_ack = 1;
2N/A }
2N/A /* If there is data, puts packet in socket list. */
2N/A if ((nb_top->tail - nb_top->data) > 0)
2N/A {
2N/A grub_net_put_packet (&sock->packs, nb_top);
2N/A do_ack = 1;
2N/A }
2N/A else
2N/A grub_netbuff_free (nb);
2N/A }
2N/A if (do_ack)
2N/A ack (sock);
2N/A while (sock->packs.first)
2N/A {
2N/A nb = sock->packs.first->nb;
2N/A if (sock->recv_hook)
2N/A sock->recv_hook (sock, sock->packs.first->nb, sock->hook_data);
2N/A else
2N/A grub_netbuff_free (nb);
2N/A grub_net_remove_packet (sock->packs.first);
2N/A }
2N/A
2N/A if (sock->fin_hook && just_closed)
2N/A sock->fin_hook (sock, sock->hook_data);
2N/A }
2N/A
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A if (grub_be_to_cpu16 (tcph->flags) & TCP_SYN)
2N/A {
2N/A grub_net_tcp_listen_t listen;
2N/A
2N/A FOR_TCP_LISTENS (listen)
2N/A {
2N/A if (!(grub_be_to_cpu16 (tcph->dst) == listen->port
2N/A && (inf == listen->inf || listen->inf == NULL)))
2N/A continue;
2N/A sock = grub_zalloc (sizeof (*sock));
2N/A if (sock == NULL)
2N/A return grub_errno;
2N/A
2N/A sock->out_port = grub_be_to_cpu16 (tcph->src);
2N/A sock->in_port = grub_be_to_cpu16 (tcph->dst);
2N/A sock->inf = inf;
2N/A sock->out_nla = *source;
2N/A sock->their_start_seq = grub_be_to_cpu32 (tcph->seqnr);
2N/A sock->their_cur_seq = sock->their_start_seq + 1;
2N/A sock->my_cur_seq = sock->my_start_seq = grub_get_time_ms ();
2N/A sock->my_window = 8192;
2N/A
2N/A sock->pq = grub_priority_queue_new (sizeof (struct grub_net_buff *),
2N/A cmp);
2N/A if (!sock->pq)
2N/A {
2N/A grub_netbuff_free (nb);
2N/A return grub_errno;
2N/A }
2N/A
2N/A err = listen->listen_hook (listen, sock, listen->hook_data);
2N/A
2N/A grub_netbuff_free (nb);
2N/A return err;
2N/A
2N/A }
2N/A }
2N/A grub_netbuff_free (nb);
2N/A return GRUB_ERR_NONE;
2N/A}