123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026 |
- /*
- * GRUB -- GRand Unified Bootloader
- * Copyright (C) 2010,2011 Free Software Foundation, Inc.
- *
- * GRUB 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.
- *
- * GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <grub/net.h>
- #include <grub/net/ip.h>
- #include <grub/net/tcp.h>
- #include <grub/net/netbuff.h>
- #include <grub/time.h>
- #include <grub/priority_queue.h>
- #define TCP_SYN_RETRANSMISSION_TIMEOUT GRUB_NET_INTERVAL
- #define TCP_SYN_RETRANSMISSION_COUNT GRUB_NET_TRIES
- #define TCP_RETRANSMISSION_TIMEOUT GRUB_NET_INTERVAL
- #define TCP_RETRANSMISSION_COUNT GRUB_NET_TRIES
- struct unacked
- {
- struct unacked *next;
- struct unacked **prev;
- struct grub_net_buff *nb;
- grub_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 grub_net_tcp_socket
- {
- struct grub_net_tcp_socket *next;
- struct grub_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;
- grub_uint32_t my_start_seq;
- grub_uint32_t my_cur_seq;
- grub_uint32_t their_start_seq;
- grub_uint32_t their_cur_seq;
- grub_uint16_t my_window;
- struct unacked *unack_first;
- struct unacked *unack_last;
- grub_err_t (*recv_hook) (grub_net_tcp_socket_t sock, struct grub_net_buff *nb,
- void *recv);
- void (*error_hook) (grub_net_tcp_socket_t sock, void *recv);
- void (*fin_hook) (grub_net_tcp_socket_t sock, void *recv);
- void *hook_data;
- grub_net_network_level_address_t out_nla;
- grub_net_link_level_address_t ll_target_addr;
- struct grub_net_network_level_interface *inf;
- grub_net_packets_t packs;
- grub_priority_queue_t pq;
- };
- struct grub_net_tcp_listen
- {
- struct grub_net_tcp_listen *next;
- struct grub_net_tcp_listen **prev;
- grub_uint16_t port;
- const struct grub_net_network_level_interface *inf;
- grub_err_t (*listen_hook) (grub_net_tcp_listen_t listen,
- grub_net_tcp_socket_t sock,
- void *data);
- void *hook_data;
- };
- struct tcphdr
- {
- grub_uint16_t src;
- grub_uint16_t dst;
- grub_uint32_t seqnr;
- grub_uint32_t ack;
- grub_uint16_t flags;
- grub_uint16_t window;
- grub_uint16_t checksum;
- grub_uint16_t urgent;
- } GRUB_PACKED;
- struct tcp_pseudohdr
- {
- grub_uint32_t src;
- grub_uint32_t dst;
- grub_uint8_t zero;
- grub_uint8_t proto;
- grub_uint16_t tcp_length;
- } GRUB_PACKED;
- struct tcp6_pseudohdr
- {
- grub_uint64_t src[2];
- grub_uint64_t dst[2];
- grub_uint32_t tcp_length;
- grub_uint8_t zero[3];
- grub_uint8_t proto;
- } GRUB_PACKED;
- static struct grub_net_tcp_socket *tcp_sockets;
- static struct grub_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)
- grub_net_tcp_listen_t
- grub_net_tcp_listen (grub_uint16_t port,
- const struct grub_net_network_level_interface *inf,
- grub_err_t (*listen_hook) (grub_net_tcp_listen_t listen,
- grub_net_tcp_socket_t sock,
- void *data),
- void *hook_data)
- {
- grub_net_tcp_listen_t ret;
- ret = grub_malloc (sizeof (*ret));
- if (!ret)
- return NULL;
- ret->listen_hook = listen_hook;
- ret->hook_data = hook_data;
- ret->port = port;
- ret->inf = inf;
- grub_list_push (GRUB_AS_LIST_P (&tcp_listens), GRUB_AS_LIST (ret));
- return ret;
- }
- void
- grub_net_tcp_stop_listen (grub_net_tcp_listen_t listen)
- {
- grub_list_remove (GRUB_AS_LIST (listen));
- }
- static inline void
- tcp_socket_register (grub_net_tcp_socket_t sock)
- {
- grub_list_push (GRUB_AS_LIST_P (&tcp_sockets),
- GRUB_AS_LIST (sock));
- }
- static void
- error (grub_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;
- grub_netbuff_free (unack->nb);
- grub_free (unack);
- }
- sock->unack_first = NULL;
- sock->unack_last = NULL;
- }
- static grub_err_t
- tcp_send (struct grub_net_buff *nb, grub_net_tcp_socket_t socket)
- {
- grub_err_t err;
- grub_uint8_t *nbd;
- struct unacked *unack;
- struct tcphdr *tcph;
- grub_size_t size;
- tcph = (struct tcphdr *) nb->data;
- tcph->seqnr = grub_cpu_to_be32 (socket->my_cur_seq);
- size = (nb->tail - nb->data - (grub_be_to_cpu16 (tcph->flags) >> 12) * 4);
- if (grub_be_to_cpu16 (tcph->flags) & TCP_FIN)
- size++;
- socket->my_cur_seq += size;
- tcph->src = grub_cpu_to_be16 (socket->in_port);
- tcph->dst = grub_cpu_to_be16 (socket->out_port);
- tcph->checksum = 0;
- tcph->checksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_TCP,
- &socket->inf->address,
- &socket->out_nla);
- nbd = nb->data;
- if (size)
- {
- unack = grub_malloc (sizeof (*unack));
- if (!unack)
- return grub_errno;
- unack->next = NULL;
- unack->nb = nb;
- unack->try_count = 1;
- unack->last_try = grub_get_time_ms ();
- if (!socket->unack_last)
- socket->unack_first = socket->unack_last = unack;
- else
- socket->unack_last->next = unack;
- }
- err = grub_net_send_ip_packet (socket->inf, &(socket->out_nla),
- &(socket->ll_target_addr), nb,
- GRUB_NET_IP_TCP);
- if (err)
- return err;
- nb->data = nbd;
- if (!size)
- grub_netbuff_free (nb);
- return GRUB_ERR_NONE;
- }
- void
- grub_net_tcp_close (grub_net_tcp_socket_t sock,
- int discard_received)
- {
- struct grub_net_buff *nb_fin;
- struct tcphdr *tcph_fin;
- grub_err_t err;
- if (discard_received != GRUB_NET_TCP_CONTINUE_RECEIVING)
- {
- sock->recv_hook = NULL;
- sock->error_hook = NULL;
- sock->fin_hook = NULL;
- }
- if (discard_received == GRUB_NET_TCP_ABORT)
- sock->i_reseted = 1;
- if (sock->i_closed)
- return;
- sock->i_closed = 1;
- nb_fin = grub_netbuff_alloc (sizeof (*tcph_fin)
- + GRUB_NET_OUR_MAX_IP_HEADER_SIZE
- + GRUB_NET_MAX_LINK_HEADER_SIZE);
- if (!nb_fin)
- return;
- err = grub_netbuff_reserve (nb_fin, GRUB_NET_OUR_MAX_IP_HEADER_SIZE
- + GRUB_NET_MAX_LINK_HEADER_SIZE);
- if (err)
- {
- grub_netbuff_free (nb_fin);
- grub_dprintf ("net", "error closing socket\n");
- grub_errno = GRUB_ERR_NONE;
- return;
- }
- err = grub_netbuff_put (nb_fin, sizeof (*tcph_fin));
- if (err)
- {
- grub_netbuff_free (nb_fin);
- grub_dprintf ("net", "error closing socket\n");
- grub_errno = GRUB_ERR_NONE;
- return;
- }
- tcph_fin = (void *) nb_fin->data;
- tcph_fin->ack = grub_cpu_to_be32 (sock->their_cur_seq);
- tcph_fin->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_FIN
- | TCP_ACK);
- tcph_fin->window = grub_cpu_to_be16_compile_time (0);
- tcph_fin->urgent = 0;
- err = tcp_send (nb_fin, sock);
- if (err)
- {
- grub_netbuff_free (nb_fin);
- grub_dprintf ("net", "error closing socket\n");
- grub_errno = GRUB_ERR_NONE;
- }
- return;
- }
- static void
- ack_real (grub_net_tcp_socket_t sock, int res)
- {
- struct grub_net_buff *nb_ack;
- struct tcphdr *tcph_ack;
- grub_err_t err;
- nb_ack = grub_netbuff_alloc (sizeof (*tcph_ack) + 128);
- if (!nb_ack)
- return;
- err = grub_netbuff_reserve (nb_ack, 128);
- if (err)
- {
- grub_netbuff_free (nb_ack);
- grub_dprintf ("net", "error closing socket\n");
- grub_errno = GRUB_ERR_NONE;
- return;
- }
- err = grub_netbuff_put (nb_ack, sizeof (*tcph_ack));
- if (err)
- {
- grub_netbuff_free (nb_ack);
- grub_dprintf ("net", "error closing socket\n");
- grub_errno = GRUB_ERR_NONE;
- return;
- }
- tcph_ack = (void *) nb_ack->data;
- if (res)
- {
- tcph_ack->ack = grub_cpu_to_be32_compile_time (0);
- tcph_ack->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_RST);
- tcph_ack->window = grub_cpu_to_be16_compile_time (0);
- }
- else
- {
- tcph_ack->ack = grub_cpu_to_be32 (sock->their_cur_seq);
- tcph_ack->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_ACK);
- tcph_ack->window = !sock->i_stall ? grub_cpu_to_be16 (sock->my_window)
- : 0;
- }
- tcph_ack->urgent = 0;
- tcph_ack->src = grub_cpu_to_be16 (sock->in_port);
- tcph_ack->dst = grub_cpu_to_be16 (sock->out_port);
- err = tcp_send (nb_ack, sock);
- if (err)
- {
- grub_dprintf ("net", "error acking socket\n");
- grub_errno = GRUB_ERR_NONE;
- }
- }
- static void
- ack (grub_net_tcp_socket_t sock)
- {
- ack_real (sock, 0);
- }
- static void
- reset (grub_net_tcp_socket_t sock)
- {
- ack_real (sock, 1);
- }
- void
- grub_net_tcp_retransmit (void)
- {
- grub_net_tcp_socket_t sock;
- grub_uint64_t ctime = 0, limit_time = 0;
- if (tcp_sockets != NULL)
- {
- ctime = grub_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;
- grub_uint8_t *nbd;
- grub_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 & grub_cpu_to_be16_compile_time (TCP_ACK))
- && tcph->ack != grub_cpu_to_be32 (sock->their_cur_seq))
- {
- tcph->checksum = 0;
- tcph->checksum = grub_net_ip_transport_checksum (unack->nb,
- GRUB_NET_IP_TCP,
- &sock->inf->address,
- &sock->out_nla);
- }
- err = grub_net_send_ip_packet (sock->inf, &(sock->out_nla),
- &(sock->ll_target_addr), unack->nb,
- GRUB_NET_IP_TCP);
- unack->nb->data = nbd;
- if (err)
- {
- grub_dprintf ("net", "TCP retransmit failed: %s\n", grub_errmsg);
- grub_errno = GRUB_ERR_NONE;
- }
- }
- }
- }
- grub_uint16_t
- grub_net_ip_transport_checksum (struct grub_net_buff *nb,
- grub_uint16_t proto,
- const grub_net_network_level_address_t *src,
- const grub_net_network_level_address_t *dst)
- {
- grub_uint16_t a, b = 0;
- grub_uint32_t c;
- a = ~grub_be_to_cpu16 (grub_net_ip_chksum ((void *) nb->data,
- nb->tail - nb->data));
- switch (dst->type)
- {
- case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4:
- {
- struct tcp_pseudohdr ph;
- ph.src = src->ipv4;
- ph.dst = dst->ipv4;
- ph.zero = 0;
- ph.tcp_length = grub_cpu_to_be16 (nb->tail - nb->data);
- ph.proto = proto;
- b = ~grub_be_to_cpu16 (grub_net_ip_chksum ((void *) &ph, sizeof (ph)));
- break;
- }
- case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6:
- {
- struct tcp6_pseudohdr ph;
- grub_memcpy (ph.src, src->ipv6, sizeof (ph.src));
- grub_memcpy (ph.dst, dst->ipv6, sizeof (ph.dst));
- grub_memset (ph.zero, 0, sizeof (ph.zero));
- ph.tcp_length = grub_cpu_to_be32 (nb->tail - nb->data);
- ph.proto = proto;
- b = ~grub_be_to_cpu16 (grub_net_ip_chksum ((void *) &ph, sizeof (ph)));
- break;
- }
- case GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV:
- b = 0;
- break;
- }
- c = (grub_uint32_t) a + (grub_uint32_t) b;
- if (c >= 0xffff)
- c -= 0xffff;
- return grub_cpu_to_be16 (~c);
- }
- /* FIXME: overflow. */
- static int
- cmp (const void *a__, const void *b__)
- {
- struct grub_net_buff *a_ = *(struct grub_net_buff **) a__;
- struct grub_net_buff *b_ = *(struct grub_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 (grub_be_to_cpu32 (a->seqnr) < grub_be_to_cpu32 (b->seqnr))
- return +1;
- if (grub_be_to_cpu32 (a->seqnr) > grub_be_to_cpu32 (b->seqnr))
- return -1;
- return 0;
- }
- static void
- destroy_pq (grub_net_tcp_socket_t sock)
- {
- struct grub_net_buff **nb_p;
- while ((nb_p = grub_priority_queue_top (sock->pq)))
- {
- grub_netbuff_free (*nb_p);
- grub_priority_queue_pop (sock->pq);
- }
- grub_priority_queue_destroy (sock->pq);
- }
- grub_err_t
- grub_net_tcp_accept (grub_net_tcp_socket_t sock,
- grub_err_t (*recv_hook) (grub_net_tcp_socket_t sock,
- struct grub_net_buff *nb,
- void *data),
- void (*error_hook) (grub_net_tcp_socket_t sock,
- void *data),
- void (*fin_hook) (grub_net_tcp_socket_t sock,
- void *data),
- void *hook_data)
- {
- struct grub_net_buff *nb_ack;
- struct tcphdr *tcph;
- grub_err_t err;
- grub_net_network_level_address_t gateway;
- struct grub_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 = grub_net_route_address (sock->out_nla, &gateway, &inf);
- if (err)
- return err;
- err = grub_net_link_layer_resolve (sock->inf, &gateway, &(sock->ll_target_addr));
- if (err)
- return err;
- nb_ack = grub_netbuff_alloc (sizeof (*tcph)
- + GRUB_NET_OUR_MAX_IP_HEADER_SIZE
- + GRUB_NET_MAX_LINK_HEADER_SIZE);
- if (!nb_ack)
- return grub_errno;
- err = grub_netbuff_reserve (nb_ack, GRUB_NET_OUR_MAX_IP_HEADER_SIZE
- + GRUB_NET_MAX_LINK_HEADER_SIZE);
- if (err)
- {
- grub_netbuff_free (nb_ack);
- return err;
- }
- err = grub_netbuff_put (nb_ack, sizeof (*tcph));
- if (err)
- {
- grub_netbuff_free (nb_ack);
- return err;
- }
- tcph = (void *) nb_ack->data;
- tcph->ack = grub_cpu_to_be32 (sock->their_cur_seq);
- tcph->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_SYN | TCP_ACK);
- tcph->window = grub_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 GRUB_ERR_NONE;
- }
- grub_net_tcp_socket_t
- grub_net_tcp_open (char *server,
- grub_uint16_t out_port,
- grub_err_t (*recv_hook) (grub_net_tcp_socket_t sock,
- struct grub_net_buff *nb,
- void *data),
- void (*error_hook) (grub_net_tcp_socket_t sock,
- void *data),
- void (*fin_hook) (grub_net_tcp_socket_t sock,
- void *data),
- void *hook_data)
- {
- grub_err_t err;
- grub_net_network_level_address_t addr;
- struct grub_net_network_level_interface *inf;
- grub_net_network_level_address_t gateway;
- grub_net_tcp_socket_t socket;
- static grub_uint16_t in_port = 21550;
- struct grub_net_buff *nb;
- struct tcphdr *tcph;
- int i;
- grub_uint8_t *nbd;
- grub_net_link_level_address_t ll_target_addr;
- err = grub_net_resolve_address (server, &addr);
- if (err)
- return NULL;
- if (addr.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4
- && addr.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6)
- {
- grub_error (GRUB_ERR_BUG, "not an IP address");
- return NULL;
- }
- err = grub_net_route_address (addr, &gateway, &inf);
- if (err)
- return NULL;
- err = grub_net_link_layer_resolve (inf, &gateway, &ll_target_addr);
- if (err)
- return NULL;
- socket = grub_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 = grub_netbuff_alloc (sizeof (*tcph) + 128);
- if (!nb)
- {
- grub_free (socket);
- return NULL;
- }
- err = grub_netbuff_reserve (nb, 128);
- if (err)
- {
- grub_free (socket);
- grub_netbuff_free (nb);
- return NULL;
- }
- err = grub_netbuff_put (nb, sizeof (*tcph));
- if (err)
- {
- grub_free (socket);
- grub_netbuff_free (nb);
- return NULL;
- }
- socket->pq = grub_priority_queue_new (sizeof (struct grub_net_buff *), cmp);
- if (!socket->pq)
- {
- grub_free (socket);
- grub_netbuff_free (nb);
- return NULL;
- }
- tcph = (void *) nb->data;
- socket->my_start_seq = grub_get_time_ms ();
- socket->my_cur_seq = socket->my_start_seq + 1;
- socket->my_window = 8192;
- tcph->seqnr = grub_cpu_to_be32 (socket->my_start_seq);
- tcph->ack = grub_cpu_to_be32_compile_time (0);
- tcph->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_SYN);
- tcph->window = grub_cpu_to_be16 (socket->my_window);
- tcph->urgent = 0;
- tcph->src = grub_cpu_to_be16 (socket->in_port);
- tcph->dst = grub_cpu_to_be16 (socket->out_port);
- tcph->checksum = 0;
- tcph->checksum = grub_net_ip_transport_checksum (nb, GRUB_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 = grub_net_send_ip_packet (socket->inf, &(socket->out_nla),
- &(socket->ll_target_addr), nb,
- GRUB_NET_IP_TCP);
- if (err)
- {
- grub_list_remove (GRUB_AS_LIST (socket));
- grub_free (socket);
- grub_netbuff_free (nb);
- return NULL;
- }
- for (j = 0; (j < TCP_SYN_RETRANSMISSION_TIMEOUT / 50
- && !socket->established); j++)
- grub_net_poll_cards (50, &socket->established);
- if (socket->established)
- break;
- }
- if (!socket->established)
- {
- grub_list_remove (GRUB_AS_LIST (socket));
- if (socket->they_reseted)
- grub_error (GRUB_ERR_NET_PORT_CLOSED,
- N_("connection refused"));
- else
- grub_error (GRUB_ERR_NET_NO_ANSWER,
- N_("connection timeout"));
- grub_netbuff_free (nb);
- destroy_pq (socket);
- grub_free (socket);
- return NULL;
- }
- grub_netbuff_free (nb);
- return socket;
- }
- grub_err_t
- grub_net_send_tcp_packet (const grub_net_tcp_socket_t socket,
- struct grub_net_buff *nb, int push)
- {
- struct tcphdr *tcph;
- grub_err_t err;
- grub_ssize_t fraglen;
- COMPILE_TIME_ASSERT (sizeof (struct tcphdr) == GRUB_NET_TCP_HEADER_SIZE);
- if (socket->out_nla.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4)
- fraglen = (socket->inf->card->mtu - GRUB_NET_OUR_IPV4_HEADER_SIZE
- - sizeof (*tcph));
- else
- fraglen = 1280 - GRUB_NET_OUR_IPV6_HEADER_SIZE;
- while (nb->tail - nb->data > fraglen)
- {
- struct grub_net_buff *nb2;
- nb2 = grub_netbuff_alloc (fraglen + sizeof (*tcph)
- + GRUB_NET_OUR_MAX_IP_HEADER_SIZE
- + GRUB_NET_MAX_LINK_HEADER_SIZE);
- if (!nb2)
- return grub_errno;
- err = grub_netbuff_reserve (nb2, GRUB_NET_MAX_LINK_HEADER_SIZE
- + GRUB_NET_OUR_MAX_IP_HEADER_SIZE);
- if (err)
- return err;
- err = grub_netbuff_put (nb2, sizeof (*tcph));
- if (err)
- return err;
- tcph = (struct tcphdr *) nb2->data;
- tcph->ack = grub_cpu_to_be32 (socket->their_cur_seq);
- tcph->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_ACK);
- tcph->window = !socket->i_stall ? grub_cpu_to_be16 (socket->my_window)
- : 0;
- tcph->urgent = 0;
- err = grub_netbuff_put (nb2, fraglen);
- if (err)
- return err;
- grub_memcpy (tcph + 1, nb->data, fraglen);
- err = grub_netbuff_pull (nb, fraglen);
- if (err)
- return err;
- err = tcp_send (nb2, socket);
- if (err)
- return err;
- }
- err = grub_netbuff_push (nb, sizeof (*tcph));
- if (err)
- return err;
- tcph = (struct tcphdr *) nb->data;
- tcph->ack = grub_cpu_to_be32 (socket->their_cur_seq);
- tcph->flags = (grub_cpu_to_be16_compile_time ((5 << 12) | TCP_ACK)
- | (push ? grub_cpu_to_be16_compile_time (TCP_PUSH) : 0));
- tcph->window = !socket->i_stall ? grub_cpu_to_be16 (socket->my_window) : 0;
- tcph->urgent = 0;
- return tcp_send (nb, socket);
- }
- grub_err_t
- grub_net_recv_tcp_packet (struct grub_net_buff *nb,
- struct grub_net_network_level_interface *inf,
- const grub_net_network_level_address_t *source)
- {
- struct tcphdr *tcph;
- grub_net_tcp_socket_t sock;
- grub_err_t err;
- /* Ignore broadcast. */
- if (!inf)
- {
- grub_netbuff_free (nb);
- return GRUB_ERR_NONE;
- }
- tcph = (struct tcphdr *) nb->data;
- if ((grub_be_to_cpu16 (tcph->flags) >> 12) < 5)
- {
- grub_dprintf ("net", "TCP header too short: %u\n",
- grub_be_to_cpu16 (tcph->flags) >> 12);
- grub_netbuff_free (nb);
- return GRUB_ERR_NONE;
- }
- if (nb->tail - nb->data < (grub_ssize_t) ((grub_be_to_cpu16 (tcph->flags)
- >> 12) * sizeof (grub_uint32_t)))
- {
- grub_dprintf ("net", "TCP packet too short: %" PRIuGRUB_SIZE "\n",
- (grub_size_t) (nb->tail - nb->data));
- grub_netbuff_free (nb);
- return GRUB_ERR_NONE;
- }
- FOR_TCP_SOCKETS (sock)
- {
- if (!(grub_be_to_cpu16 (tcph->dst) == sock->in_port
- && grub_be_to_cpu16 (tcph->src) == sock->out_port
- && inf == sock->inf
- && grub_net_addr_cmp (source, &sock->out_nla) == 0))
- continue;
- if (tcph->checksum)
- {
- grub_uint16_t chk, expected;
- chk = tcph->checksum;
- tcph->checksum = 0;
- expected = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_TCP,
- &sock->out_nla,
- &sock->inf->address);
- if (expected != chk)
- {
- grub_dprintf ("net", "Invalid TCP checksum. "
- "Expected %x, got %x\n",
- grub_be_to_cpu16 (expected),
- grub_be_to_cpu16 (chk));
- grub_netbuff_free (nb);
- return GRUB_ERR_NONE;
- }
- tcph->checksum = chk;
- }
- if ((grub_be_to_cpu16 (tcph->flags) & TCP_SYN)
- && (grub_be_to_cpu16 (tcph->flags) & TCP_ACK)
- && !sock->established)
- {
- sock->their_start_seq = grub_be_to_cpu32 (tcph->seqnr);
- sock->their_cur_seq = sock->their_start_seq + 1;
- sock->established = 1;
- }
- if (grub_be_to_cpu16 (tcph->flags) & TCP_RST)
- {
- sock->they_reseted = 1;
- error (sock);
- grub_netbuff_free (nb);
- return GRUB_ERR_NONE;
- }
- if (grub_be_to_cpu16 (tcph->flags) & TCP_ACK)
- {
- struct unacked *unack, *next;
- grub_uint32_t acked = grub_be_to_cpu32 (tcph->ack);
- for (unack = sock->unack_first; unack; unack = next)
- {
- grub_uint32_t seqnr;
- struct tcphdr *unack_tcph;
- next = unack->next;
- seqnr = grub_be_to_cpu32 (((struct tcphdr *) unack->nb->data)
- ->seqnr);
- unack_tcph = (struct tcphdr *) unack->nb->data;
- seqnr += (unack->nb->tail - unack->nb->data
- - (grub_be_to_cpu16 (unack_tcph->flags) >> 12) * 4);
- if (grub_be_to_cpu16 (unack_tcph->flags) & TCP_FIN)
- seqnr++;
- if (seqnr > acked)
- break;
- grub_netbuff_free (unack->nb);
- grub_free (unack);
- }
- sock->unack_first = unack;
- if (!sock->unack_first)
- sock->unack_last = NULL;
- }
- if (grub_be_to_cpu32 (tcph->seqnr) < sock->their_cur_seq)
- {
- ack (sock);
- grub_netbuff_free (nb);
- return GRUB_ERR_NONE;
- }
- if (sock->i_reseted && (nb->tail - nb->data
- - (grub_be_to_cpu16 (tcph->flags)
- >> 12) * sizeof (grub_uint32_t)) > 0)
- {
- reset (sock);
- }
- err = grub_priority_queue_push (sock->pq, &nb);
- if (err)
- {
- grub_netbuff_free (nb);
- return err;
- }
- {
- struct grub_net_buff **nb_top_p, *nb_top;
- int do_ack = 0;
- int just_closed = 0;
- while (1)
- {
- nb_top_p = grub_priority_queue_top (sock->pq);
- if (!nb_top_p)
- return GRUB_ERR_NONE;
- nb_top = *nb_top_p;
- tcph = (struct tcphdr *) nb_top->data;
- if (grub_be_to_cpu32 (tcph->seqnr) >= sock->their_cur_seq)
- break;
- grub_netbuff_free (nb_top);
- grub_priority_queue_pop (sock->pq);
- }
- if (grub_be_to_cpu32 (tcph->seqnr) != sock->their_cur_seq)
- {
- ack (sock);
- return GRUB_ERR_NONE;
- }
- while (1)
- {
- nb_top_p = grub_priority_queue_top (sock->pq);
- if (!nb_top_p)
- break;
- nb_top = *nb_top_p;
- tcph = (struct tcphdr *) nb_top->data;
- if (grub_be_to_cpu32 (tcph->seqnr) != sock->their_cur_seq)
- break;
- grub_priority_queue_pop (sock->pq);
- err = grub_netbuff_pull (nb_top, (grub_be_to_cpu16 (tcph->flags)
- >> 12) * sizeof (grub_uint32_t));
- if (err)
- {
- grub_netbuff_free (nb_top);
- return err;
- }
- sock->their_cur_seq += (nb_top->tail - nb_top->data);
- if (grub_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)
- {
- grub_net_put_packet (&sock->packs, nb_top);
- do_ack = 1;
- }
- else
- grub_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
- grub_netbuff_free (nb);
- grub_net_remove_packet (sock->packs.first);
- }
- if (sock->fin_hook && just_closed)
- sock->fin_hook (sock, sock->hook_data);
- }
- return GRUB_ERR_NONE;
- }
- if (grub_be_to_cpu16 (tcph->flags) & TCP_SYN)
- {
- grub_net_tcp_listen_t listen;
- FOR_TCP_LISTENS (listen)
- {
- if (!(grub_be_to_cpu16 (tcph->dst) == listen->port
- && (inf == listen->inf || listen->inf == NULL)))
- continue;
- sock = grub_zalloc (sizeof (*sock));
- if (sock == NULL)
- return grub_errno;
- sock->out_port = grub_be_to_cpu16 (tcph->src);
- sock->in_port = grub_be_to_cpu16 (tcph->dst);
- sock->inf = inf;
- sock->out_nla = *source;
- sock->their_start_seq = grub_be_to_cpu32 (tcph->seqnr);
- sock->their_cur_seq = sock->their_start_seq + 1;
- sock->my_cur_seq = sock->my_start_seq = grub_get_time_ms ();
- sock->my_window = 8192;
- sock->pq = grub_priority_queue_new (sizeof (struct grub_net_buff *),
- cmp);
- if (!sock->pq)
- {
- grub_free (sock);
- grub_netbuff_free (nb);
- return grub_errno;
- }
- err = listen->listen_hook (listen, sock, listen->hook_data);
- grub_netbuff_free (nb);
- return err;
- }
- }
- grub_netbuff_free (nb);
- return GRUB_ERR_NONE;
- }
- void
- grub_net_tcp_stall (grub_net_tcp_socket_t sock)
- {
- if (sock->i_stall)
- return;
- sock->i_stall = 1;
- ack (sock);
- }
- void
- grub_net_tcp_unstall (grub_net_tcp_socket_t sock)
- {
- if (!sock->i_stall)
- return;
- sock->i_stall = 0;
- ack (sock);
- }
|