tcp.c 25.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/*
 *  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>

26 27 28 29
#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
30 31 32 33

struct unacked
{
  struct unacked *next;
34
  struct unacked **prev;
35 36 37 38 39 40 41 42 43
  struct grub_net_buff *nb;
  grub_uint64_t last_try;
  int try_count;
};

enum
  {
    TCP_FIN = 0x1,
    TCP_SYN = 0x2,
44
    TCP_RST = 0x4,
45 46 47
    TCP_PUSH = 0x8,
    TCP_ACK = 0x10,
    TCP_URG = 0x20,
48 49 50 51 52
  };

struct grub_net_tcp_socket
{
  struct grub_net_tcp_socket *next;
53
  struct grub_net_tcp_socket **prev;
54 55 56 57 58 59 60

  int established;
  int i_closed;
  int they_closed;
  int in_port;
  int out_port;
  int errors;
61 62
  int they_reseted;
  int i_reseted;
63
  int i_stall;
64 65 66 67 68 69 70 71 72 73
  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);
74
  void (*fin_hook) (grub_net_tcp_socket_t sock, void *recv);
75
  void *hook_data;
76 77
  grub_net_network_level_address_t out_nla;
  grub_net_link_level_address_t ll_target_addr;
78 79 80 81 82
  struct grub_net_network_level_interface *inf;
  grub_net_packets_t packs;
  grub_priority_queue_t pq;
};

83 84 85
struct grub_net_tcp_listen
{
  struct grub_net_tcp_listen *next;
86
  struct grub_net_tcp_listen **prev;
87 88 89 90 91 92 93 94 95 96

  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;
};

97 98 99 100 101 102 103 104 105 106
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;
107
} GRUB_PACKED;
108 109 110 111 112 113 114 115

struct tcp_pseudohdr
{
  grub_uint32_t src;
  grub_uint32_t dst;
  grub_uint8_t zero;
  grub_uint8_t proto;
  grub_uint16_t tcp_length;
116
} GRUB_PACKED;
117

118 119 120 121 122 123 124
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;
125
} GRUB_PACKED;
126

127 128 129 130 131
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)
132

133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
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)
{
156
  grub_list_remove (GRUB_AS_LIST (listen));
157
}
158 159 160 161 162 163 164 165

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));
}

166 167 168 169 170
static void
error (grub_net_tcp_socket_t sock)
{
  struct unacked *unack, *next;

171
  if (sock->error_hook)
172 173 174 175 176 177 178 179 180 181 182 183 184
    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;
}

185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
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;
    }

224
  err = grub_net_send_ip_packet (socket->inf, &(socket->out_nla),
225 226
				 &(socket->ll_target_addr), nb,
				 GRUB_NET_IP_TCP);
227 228 229
  if (err)
    return err;
  nb->data = nbd;
230 231
  if (!size)
    grub_netbuff_free (nb);
232 233 234 235
  return GRUB_ERR_NONE;
}

void
236 237
grub_net_tcp_close (grub_net_tcp_socket_t sock,
		    int discard_received)
238 239 240 241 242
{
  struct grub_net_buff *nb_fin;
  struct tcphdr *tcph_fin;
  grub_err_t err;

243
  if (discard_received != GRUB_NET_TCP_CONTINUE_RECEIVING)
244 245 246 247 248 249 250 251 252 253 254 255 256
    {
      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;
257

258
  nb_fin = grub_netbuff_alloc (sizeof (*tcph_fin)
259
			       + GRUB_NET_OUR_MAX_IP_HEADER_SIZE
260
			       + GRUB_NET_MAX_LINK_HEADER_SIZE);
261 262
  if (!nb_fin)
    return;
263
  err = grub_netbuff_reserve (nb_fin, GRUB_NET_OUR_MAX_IP_HEADER_SIZE
264
			       + GRUB_NET_MAX_LINK_HEADER_SIZE);
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
  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;
282 283 284 285
  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);
286 287 288 289 290 291 292 293 294 295 296 297
  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
298
ack_real (grub_net_tcp_socket_t sock, int res)
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
{
  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;
325 326
  if (res)
    {
327 328 329
      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);
330 331 332 333
    }
  else
    {
      tcph_ack->ack = grub_cpu_to_be32 (sock->their_cur_seq);
334
      tcph_ack->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_ACK);
335 336
      tcph_ack->window = !sock->i_stall ? grub_cpu_to_be16 (sock->my_window)
	: 0;
337
    }
338 339 340 341 342 343 344 345 346 347 348
  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;
    }
}

349 350 351 352 353 354 355 356 357 358 359 360
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);
}

361 362 363 364 365 366 367 368 369 370 371 372
void
grub_net_tcp_retransmit (void)
{
  grub_net_tcp_socket_t sock;
  grub_uint64_t ctime = grub_get_time_ms ();
  grub_uint64_t limit_time = ctime - TCP_RETRANSMISSION_TIMEOUT;

  FOR_TCP_SOCKETS (sock)
  {
    struct unacked *unack;
    for (unack = sock->unack_first; unack; unack = unack->next)
      {
373
	struct tcphdr *tcph;
374 375 376 377 378 379 380 381
	grub_uint8_t *nbd;
	grub_err_t err;

	if (unack->last_try > limit_time)
	  continue;
	
	if (unack->try_count > TCP_RETRANSMISSION_COUNT)
	  {
382
	    error (sock);
383 384 385 386 387
	    break;
	  }
	unack->try_count++;
	unack->last_try = ctime;
	nbd = unack->nb->data;
388 389 390 391 392 393 394 395 396 397 398 399
	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);
	  }

400
	err = grub_net_send_ip_packet (sock->inf, &(sock->out_nla),
401
				       &(sock->ll_target_addr), unack->nb,
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
				       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)
{
419
  grub_uint16_t a, b = 0;
420 421 422 423
  grub_uint32_t c;
  a = ~grub_be_to_cpu16 (grub_net_ip_chksum ((void *) nb->data,
					     nb->tail - nb->data));

424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
  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;
    }
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478
  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)))
479 480 481 482
    {
      grub_netbuff_free (*nb_p);
      grub_priority_queue_pop (sock->pq);
    }
483 484 485 486

  grub_priority_queue_destroy (sock->pq);
}

487 488 489 490 491 492 493
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),
494 495
		     void (*fin_hook) (grub_net_tcp_socket_t sock,
				       void *data),
496 497 498 499 500
		     void *hook_data)
{
  struct grub_net_buff *nb_ack;
  struct tcphdr *tcph;
  grub_err_t err;
501 502
  grub_net_network_level_address_t gateway;
  struct grub_net_network_level_interface *inf;
503 504 505

  sock->recv_hook = recv_hook;
  sock->error_hook = error_hook;
506
  sock->fin_hook = fin_hook;
507
  sock->hook_data = hook_data;
508 509 510 511 512 513 514 515 516

  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;

517
  nb_ack = grub_netbuff_alloc (sizeof (*tcph)
518
			       + GRUB_NET_OUR_MAX_IP_HEADER_SIZE
519 520 521
			       + GRUB_NET_MAX_LINK_HEADER_SIZE);
  if (!nb_ack)
    return grub_errno;
522
  err = grub_netbuff_reserve (nb_ack, GRUB_NET_OUR_MAX_IP_HEADER_SIZE
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537
			      + 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);
538
  tcph->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_SYN | TCP_ACK);
539 540
  tcph->window = grub_cpu_to_be16 (sock->my_window);
  tcph->urgent = 0;
541
  sock->established = 1;
542 543 544 545 546 547 548 549
  tcp_socket_register (sock);
  err = tcp_send (nb_ack, sock);
  if (err)
    return err;
  sock->my_cur_seq++;
  return GRUB_ERR_NONE;
}

550 551 552 553 554 555 556 557
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),
558 559
		   void (*fin_hook) (grub_net_tcp_socket_t sock,
				     void *data),
560 561 562 563 564 565 566 567 568 569 570 571
		   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;
572
  grub_net_link_level_address_t ll_target_addr;
573 574 575 576 577

  err = grub_net_resolve_address (server, &addr);
  if (err)
    return NULL;

578 579
  if (addr.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4
      && addr.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6)
580
    {
581
      grub_error (GRUB_ERR_BUG, "not an IP address");
582 583 584 585 586 587 588
      return NULL;
    }
 
  err = grub_net_route_address (addr, &gateway, &inf);
  if (err)
    return NULL;

589 590 591 592
  err = grub_net_link_layer_resolve (inf, &gateway, &ll_target_addr);
  if (err)
    return NULL;

593 594 595 596 597 598 599
  socket = grub_zalloc (sizeof (*socket));
  if (socket == NULL)
    return NULL; 

  socket->out_port = out_port;
  socket->inf = inf;
  socket->out_nla = addr;
600
  socket->ll_target_addr = ll_target_addr;
601 602 603
  socket->in_port = in_port++;
  socket->recv_hook = recv_hook;
  socket->error_hook = error_hook;
604
  socket->fin_hook = fin_hook;
605 606 607 608
  socket->hook_data = hook_data;

  nb = grub_netbuff_alloc (sizeof (*tcph) + 128);
  if (!nb)
Andrei Borzenkov's avatar
Andrei Borzenkov committed
609 610 611 612 613
    {
      grub_free (socket);
      return NULL;
    }

614 615 616
  err = grub_netbuff_reserve (nb, 128);
  if (err)
    {
Andrei Borzenkov's avatar
Andrei Borzenkov committed
617
      grub_free (socket);
618 619 620 621 622 623 624
      grub_netbuff_free (nb);
      return NULL;
    }

  err = grub_netbuff_put (nb, sizeof (*tcph));
  if (err)
    {
Andrei Borzenkov's avatar
Andrei Borzenkov committed
625
      grub_free (socket);
626 627 628 629 630 631
      grub_netbuff_free (nb);
      return NULL;
    }
  socket->pq = grub_priority_queue_new (sizeof (struct grub_net_buff *), cmp);
  if (!socket->pq)
    {
Andrei Borzenkov's avatar
Andrei Borzenkov committed
632
      grub_free (socket);
633 634 635 636 637 638 639 640 641
      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);
642 643
  tcph->ack = grub_cpu_to_be32_compile_time (0);
  tcph->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_SYN);
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
  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;
660
      err = grub_net_send_ip_packet (socket->inf, &(socket->out_nla), 
661
				     &(socket->ll_target_addr), nb,
662 663 664
				     GRUB_NET_IP_TCP);
      if (err)
	{
665
	  grub_list_remove (GRUB_AS_LIST (socket));
666 667 668 669
	  grub_free (socket);
	  grub_netbuff_free (nb);
	  return NULL;
	}
670
      for (j = 0; (j < TCP_SYN_RETRANSMISSION_TIMEOUT / 50 
671
		   && !socket->established); j++)
672
	grub_net_poll_cards (50, &socket->established);
673 674 675 676 677
      if (socket->established)
	break;
    }
  if (!socket->established)
    {
678
      grub_list_remove (GRUB_AS_LIST (socket));
679
      if (socket->they_reseted)
680 681
	grub_error (GRUB_ERR_NET_PORT_CLOSED,
		    N_("connection refused"));
682
      else
683 684
	grub_error (GRUB_ERR_NET_NO_ANSWER,
		    N_("connection timeout"));
685 686 687

      grub_netbuff_free (nb);
      destroy_pq (socket);
688
      grub_free (socket);
689 690 691 692 693 694 695 696 697
      return NULL;
    }

  grub_netbuff_free (nb);
  return socket;
}

grub_err_t
grub_net_send_tcp_packet (const grub_net_tcp_socket_t socket,
698
			  struct grub_net_buff *nb, int push)
699 700 701
{
  struct tcphdr *tcph;
  grub_err_t err;
702
  grub_ssize_t fraglen;
703
  COMPILE_TIME_ASSERT (sizeof (struct tcphdr) == GRUB_NET_TCP_HEADER_SIZE);
704 705 706 707 708
  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;
709 710 711 712 713 714

  while (nb->tail - nb->data > fraglen)
    {
      struct grub_net_buff *nb2;

      nb2 = grub_netbuff_alloc (fraglen + sizeof (*tcph)
715
				+ GRUB_NET_OUR_MAX_IP_HEADER_SIZE
716 717 718 719
				+ GRUB_NET_MAX_LINK_HEADER_SIZE);
      if (!nb2)
	return grub_errno;
      err = grub_netbuff_reserve (nb2, GRUB_NET_MAX_LINK_HEADER_SIZE
720
				  + GRUB_NET_OUR_MAX_IP_HEADER_SIZE);
721 722 723 724 725 726 727
      if (err)
	return err;
      err = grub_netbuff_put (nb2, sizeof (*tcph));
      if (err)
	return err;

      tcph = (struct tcphdr *) nb2->data;
728 729
      tcph->ack = grub_cpu_to_be32 (socket->their_cur_seq);
      tcph->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_ACK);
730 731
      tcph->window = !socket->i_stall ? grub_cpu_to_be16 (socket->my_window)
	: 0;
732 733 734 735 736 737 738 739 740 741 742 743 744
      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;
    }
745 746 747 748 749 750

  err = grub_netbuff_push (nb, sizeof (*tcph));
  if (err)
    return err;

  tcph = (struct tcphdr *) nb->data;
751 752 753
  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));
754
  tcph->window = !socket->i_stall ? grub_cpu_to_be16 (socket->my_window) : 0;
755 756 757 758 759 760 761 762 763 764 765 766 767
  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;

768 769 770 771 772 773 774
  /* Ignore broadcast.  */
  if (!inf)
    {
      grub_netbuff_free (nb);
      return GRUB_ERR_NONE;
    }

775 776 777 778 779 780 781 782 783 784 785 786
  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",
787
		    (grub_size_t) (nb->tail - nb->data));
788 789 790 791 792 793
      grub_netbuff_free (nb);
      return GRUB_ERR_NONE;
    }

  FOR_TCP_SOCKETS (sock)
  {
794 795 796
    if (!(grub_be_to_cpu16 (tcph->dst) == sock->in_port
	  && grub_be_to_cpu16 (tcph->src) == sock->out_port
	  && inf == sock->inf
797
	  && grub_net_addr_cmp (source, &sock->out_nla) == 0))
798 799
      continue;
    if (tcph->checksum)
800
      {
801 802 803 804 805 806 807
	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)
808
	  {
809 810 811 812 813 814
	    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;
815
	  }
816 817
	tcph->checksum = chk;
      }
818

819 820 821 822 823 824 825 826
    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;
      }
827

828 829
    if (grub_be_to_cpu16 (tcph->flags) & TCP_RST)
      {
830
	sock->they_reseted = 1;
831

832
	error (sock);
833

834
	grub_netbuff_free (nb);
835

836 837
	return GRUB_ERR_NONE;
      }
838

839 840 841 842 843
    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)
844
	  {
845
	    grub_uint32_t seqnr;
846
	    struct tcphdr *unack_tcph;
847 848 849
	    next = unack->next;
	    seqnr = grub_be_to_cpu32 (((struct tcphdr *) unack->nb->data)
				      ->seqnr);
850 851 852 853
	    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)
854 855 856 857 858 859
	      seqnr++;

	    if (seqnr > acked)
	      break;
	    grub_netbuff_free (unack->nb);
	    grub_free (unack);
860
	  }
861
	sock->unack_first = unack;
862
	if (!sock->unack_first)
863 864
	  sock->unack_last = NULL;
      }
865

866 867 868 869 870 871
    if (grub_be_to_cpu32 (tcph->seqnr) < sock->their_cur_seq)
      {
	ack (sock);
	grub_netbuff_free (nb);
	return GRUB_ERR_NONE;
      }
872 873 874
    if (sock->i_reseted && (nb->tail - nb->data
			    - (grub_be_to_cpu16 (tcph->flags)
			       >> 12) * sizeof (grub_uint32_t)) > 0)
875 876 877
      {
	reset (sock);
      }
878

879 880
    err = grub_priority_queue_push (sock->pq, &nb);
    if (err)
881 882 883 884
      {
	grub_netbuff_free (nb);
	return err;
      }
885 886 887 888

    {
      struct grub_net_buff **nb_top_p, *nb_top;
      int do_ack = 0;
889
      int just_closed = 0;
890
      while (1)
891
	{
892 893 894 895 896 897 898
	  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;
899
	  grub_netbuff_free (nb_top);
900 901 902
	  grub_priority_queue_pop (sock->pq);
	}
      if (grub_be_to_cpu32 (tcph->seqnr) != sock->their_cur_seq)
903 904 905 906
	{
	  ack (sock);
	  return GRUB_ERR_NONE;
	}
907 908 909 910 911 912 913 914 915 916 917 918
      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);

919 920
	  err = grub_netbuff_pull (nb_top, (grub_be_to_cpu16 (tcph->flags)
					    >> 12) * sizeof (grub_uint32_t));
921
	  if (err)
922 923 924 925
	    {
	      grub_netbuff_free (nb_top);
	      return err;
	    }
926 927 928

	  sock->their_cur_seq += (nb_top->tail - nb_top->data);
	  if (grub_be_to_cpu16 (tcph->flags) & TCP_FIN)
929
	    {
930
	      sock->they_closed = 1;
931
	      just_closed = 1;
932 933
	      sock->their_cur_seq++;
	      do_ack = 1;
934
	    }
935 936
	  /* If there is data, puts packet in socket list. */
	  if ((nb_top->tail - nb_top->data) > 0)
937
	    {
938 939
	      grub_net_put_packet (&sock->packs, nb_top);
	      do_ack = 1;
940
	    }
941
	  else
942
	    grub_netbuff_free (nb_top);
943
	}
944 945
      if (do_ack)
	ack (sock);
946 947 948 949 950 951 952 953 954 955 956 957
      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);
958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986
    }
	
    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)
987
	  {
Andrei Borzenkov's avatar
Andrei Borzenkov committed
988
	    grub_free (sock);
989 990
	    grub_netbuff_free (nb);
	    return grub_errno;
991
	  }
992 993 994 995 996 997

	err = listen->listen_hook (listen, sock, listen->hook_data);

	grub_netbuff_free (nb);
	return err;

998
      }
999
    }
1000 1001 1002
  grub_netbuff_free (nb);
  return GRUB_ERR_NONE;
}
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020

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);
}