|
|
9723a8 |
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
|
c294fc |
From: Alexey Makhalov <amakhalov@vmware.com>
|
|
|
c294fc |
Date: Thu, 9 Jul 2020 08:10:40 +0000
|
|
|
9723a8 |
Subject: [PATCH] tftp: Do not use priority queue
|
|
|
c294fc |
|
|
|
c294fc |
There is not need to reassemble the order of blocks. Per RFC 1350,
|
|
|
c294fc |
server must wait for the ACK, before sending next block. Data packets
|
|
|
c294fc |
can be served immediately without putting them to priority queue.
|
|
|
c294fc |
|
|
|
c294fc |
Logic to handle incoming packet is this:
|
|
|
c294fc |
- if packet block id equal to expected block id, then
|
|
|
c294fc |
process the packet,
|
|
|
c294fc |
- if packet block id is less than expected - this is retransmit
|
|
|
c294fc |
of old packet, then ACK it and drop the packet,
|
|
|
c294fc |
- if packet block id is more than expected - that shouldn't
|
|
|
c294fc |
happen, just drop the packet.
|
|
|
c294fc |
|
|
|
c294fc |
It makes the tftp receive path code simpler, smaller and faster.
|
|
|
c294fc |
As a benefit, this change fixes CID# 73624 and CID# 96690, caused
|
|
|
c294fc |
by following while loop:
|
|
|
c294fc |
|
|
|
c294fc |
while (cmp_block (grub_be_to_cpu16 (tftph->u.data.block), data->block + 1) == 0)
|
|
|
c294fc |
|
|
|
c294fc |
where tftph pointer is not moving from one iteration to another, causing
|
|
|
c294fc |
to serve same packet again. Luckily, double serving didn't happen due to
|
|
|
c294fc |
data->block++ during the first iteration.
|
|
|
c294fc |
|
|
|
c294fc |
Fixes: CID 73624, CID 96690
|
|
|
c294fc |
|
|
|
c294fc |
Signed-off-by: Alexey Makhalov <amakhalov@vmware.com>
|
|
|
c294fc |
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
|
|
|
c294fc |
Upstream-commit-id: 8316694c4f7
|
|
|
c294fc |
---
|
|
|
9723a8 |
grub-core/net/tftp.c | 174 ++++++++++++++++-----------------------------------
|
|
|
c294fc |
1 file changed, 54 insertions(+), 120 deletions(-)
|
|
|
c294fc |
|
|
|
c294fc |
diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c
|
|
|
c294fc |
index e267af354f4..79c16f9b041 100644
|
|
|
c294fc |
--- a/grub-core/net/tftp.c
|
|
|
c294fc |
+++ b/grub-core/net/tftp.c
|
|
|
c294fc |
@@ -25,7 +25,6 @@
|
|
|
c294fc |
#include <grub/mm.h>
|
|
|
c294fc |
#include <grub/dl.h>
|
|
|
c294fc |
#include <grub/file.h>
|
|
|
c294fc |
-#include <grub/priority_queue.h>
|
|
|
c294fc |
#include <grub/i18n.h>
|
|
|
c294fc |
|
|
|
c294fc |
GRUB_MOD_LICENSE ("GPLv3+");
|
|
|
c294fc |
@@ -106,31 +105,8 @@ typedef struct tftp_data
|
|
|
c294fc |
int have_oack;
|
|
|
c294fc |
struct grub_error_saved save_err;
|
|
|
c294fc |
grub_net_udp_socket_t sock;
|
|
|
c294fc |
- grub_priority_queue_t pq;
|
|
|
c294fc |
} *tftp_data_t;
|
|
|
c294fc |
|
|
|
c294fc |
-static int
|
|
|
c294fc |
-cmp_block (grub_uint16_t a, grub_uint16_t b)
|
|
|
c294fc |
-{
|
|
|
c294fc |
- grub_int16_t i = (grub_int16_t) (a - b);
|
|
|
c294fc |
- if (i > 0)
|
|
|
c294fc |
- return +1;
|
|
|
c294fc |
- if (i < 0)
|
|
|
c294fc |
- return -1;
|
|
|
c294fc |
- return 0;
|
|
|
c294fc |
-}
|
|
|
c294fc |
-
|
|
|
c294fc |
-static int
|
|
|
c294fc |
-cmp (const void *a__, const void *b__)
|
|
|
c294fc |
-{
|
|
|
c294fc |
- struct grub_net_buff *a_ = *(struct grub_net_buff **) a__;
|
|
|
c294fc |
- struct grub_net_buff *b_ = *(struct grub_net_buff **) b__;
|
|
|
c294fc |
- struct tftphdr *a = (struct tftphdr *) a_->data;
|
|
|
c294fc |
- struct tftphdr *b = (struct tftphdr *) b_->data;
|
|
|
c294fc |
- /* We want the first elements to be on top. */
|
|
|
c294fc |
- return -cmp_block (grub_be_to_cpu16 (a->u.data.block), grub_be_to_cpu16 (b->u.data.block));
|
|
|
c294fc |
-}
|
|
|
c294fc |
-
|
|
|
c294fc |
static grub_err_t
|
|
|
c294fc |
ack (tftp_data_t data, grub_uint64_t block)
|
|
|
c294fc |
{
|
|
|
c294fc |
@@ -207,73 +183,60 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)),
|
|
|
c294fc |
return GRUB_ERR_NONE;
|
|
|
c294fc |
}
|
|
|
c294fc |
|
|
|
c294fc |
- err = grub_priority_queue_push (data->pq, &nb);
|
|
|
c294fc |
- if (err)
|
|
|
c294fc |
- return err;
|
|
|
c294fc |
+ /* Ack old/retransmitted block. */
|
|
|
c294fc |
+ if (grub_be_to_cpu16 (tftph->u.data.block) < data->block + 1)
|
|
|
c294fc |
+ ack (data, grub_be_to_cpu16 (tftph->u.data.block));
|
|
|
c294fc |
+ /* Ignore unexpected block. */
|
|
|
c294fc |
+ else if (grub_be_to_cpu16 (tftph->u.data.block) > data->block + 1)
|
|
|
c294fc |
+ grub_dprintf ("tftp", "TFTP unexpected block # %d\n", tftph->u.data.block);
|
|
|
c294fc |
+ else
|
|
|
c294fc |
+ {
|
|
|
c294fc |
+ unsigned size;
|
|
|
c294fc |
|
|
|
c294fc |
- {
|
|
|
c294fc |
- struct grub_net_buff **nb_top_p, *nb_top;
|
|
|
c294fc |
- while (1)
|
|
|
c294fc |
- {
|
|
|
c294fc |
- nb_top_p = grub_priority_queue_top (data->pq);
|
|
|
c294fc |
- if (!nb_top_p)
|
|
|
c294fc |
- return GRUB_ERR_NONE;
|
|
|
c294fc |
- nb_top = *nb_top_p;
|
|
|
c294fc |
- tftph = (struct tftphdr *) nb_top->data;
|
|
|
c294fc |
- if (cmp_block (grub_be_to_cpu16 (tftph->u.data.block), data->block + 1) >= 0)
|
|
|
c294fc |
- break;
|
|
|
c294fc |
- ack (data, grub_be_to_cpu16 (tftph->u.data.block));
|
|
|
c294fc |
- grub_netbuff_free (nb_top);
|
|
|
c294fc |
- grub_priority_queue_pop (data->pq);
|
|
|
c294fc |
- }
|
|
|
c294fc |
- while (cmp_block (grub_be_to_cpu16 (tftph->u.data.block), data->block + 1) == 0)
|
|
|
c294fc |
- {
|
|
|
c294fc |
- unsigned size;
|
|
|
c294fc |
-
|
|
|
c294fc |
- grub_priority_queue_pop (data->pq);
|
|
|
c294fc |
-
|
|
|
c294fc |
- if (file->device->net->packs.count < 50)
|
|
|
c294fc |
+ if (file->device->net->packs.count < 50)
|
|
|
c294fc |
+ {
|
|
|
c294fc |
err = ack (data, data->block + 1);
|
|
|
c294fc |
- else
|
|
|
c294fc |
- {
|
|
|
c294fc |
- file->device->net->stall = 1;
|
|
|
c294fc |
- err = 0;
|
|
|
c294fc |
- }
|
|
|
c294fc |
- if (err)
|
|
|
c294fc |
- return err;
|
|
|
c294fc |
+ if (err)
|
|
|
c294fc |
+ return err;
|
|
|
c294fc |
+ }
|
|
|
c294fc |
+ else
|
|
|
c294fc |
+ file->device->net->stall = 1;
|
|
|
c294fc |
|
|
|
c294fc |
- err = grub_netbuff_pull (nb_top, sizeof (tftph->opcode) +
|
|
|
c294fc |
- sizeof (tftph->u.data.block));
|
|
|
c294fc |
- if (err)
|
|
|
c294fc |
- return err;
|
|
|
c294fc |
- size = nb_top->tail - nb_top->data;
|
|
|
c294fc |
+ err = grub_netbuff_pull (nb, sizeof (tftph->opcode) +
|
|
|
c294fc |
+ sizeof (tftph->u.data.block));
|
|
|
c294fc |
+ if (err)
|
|
|
c294fc |
+ return err;
|
|
|
c294fc |
+ size = nb->tail - nb->data;
|
|
|
c294fc |
|
|
|
c294fc |
- data->block++;
|
|
|
c294fc |
- if (size < data->block_size)
|
|
|
c294fc |
- {
|
|
|
c294fc |
- if (data->ack_sent < data->block)
|
|
|
c294fc |
- ack (data, data->block);
|
|
|
c294fc |
- file->device->net->eof = 1;
|
|
|
c294fc |
- file->device->net->stall = 1;
|
|
|
c294fc |
- grub_net_udp_close (data->sock);
|
|
|
c294fc |
- data->sock = NULL;
|
|
|
c294fc |
- }
|
|
|
c294fc |
- /* Prevent garbage in broken cards. Is it still necessary
|
|
|
c294fc |
- given that IP implementation has been fixed?
|
|
|
c294fc |
- */
|
|
|
c294fc |
- if (size > data->block_size)
|
|
|
c294fc |
- {
|
|
|
c294fc |
- err = grub_netbuff_unput (nb_top, size - data->block_size);
|
|
|
c294fc |
- if (err)
|
|
|
c294fc |
- return err;
|
|
|
c294fc |
- }
|
|
|
c294fc |
- /* If there is data, puts packet in socket list. */
|
|
|
c294fc |
- if ((nb_top->tail - nb_top->data) > 0)
|
|
|
c294fc |
- grub_net_put_packet (&file->device->net->packs, nb_top);
|
|
|
c294fc |
- else
|
|
|
c294fc |
- grub_netbuff_free (nb_top);
|
|
|
c294fc |
- }
|
|
|
c294fc |
- }
|
|
|
c294fc |
+ data->block++;
|
|
|
c294fc |
+ if (size < data->block_size)
|
|
|
c294fc |
+ {
|
|
|
c294fc |
+ if (data->ack_sent < data->block)
|
|
|
c294fc |
+ ack (data, data->block);
|
|
|
c294fc |
+ file->device->net->eof = 1;
|
|
|
c294fc |
+ file->device->net->stall = 1;
|
|
|
c294fc |
+ grub_net_udp_close (data->sock);
|
|
|
c294fc |
+ data->sock = NULL;
|
|
|
c294fc |
+ }
|
|
|
c294fc |
+ /*
|
|
|
c294fc |
+ * Prevent garbage in broken cards. Is it still necessary
|
|
|
c294fc |
+ * given that IP implementation has been fixed?
|
|
|
c294fc |
+ */
|
|
|
c294fc |
+ if (size > data->block_size)
|
|
|
c294fc |
+ {
|
|
|
c294fc |
+ err = grub_netbuff_unput (nb, size - data->block_size);
|
|
|
c294fc |
+ if (err)
|
|
|
c294fc |
+ return err;
|
|
|
c294fc |
+ }
|
|
|
c294fc |
+ /* If there is data, puts packet in socket list. */
|
|
|
c294fc |
+ if ((nb->tail - nb->data) > 0)
|
|
|
c294fc |
+ {
|
|
|
c294fc |
+ grub_net_put_packet (&file->device->net->packs, nb);
|
|
|
c294fc |
+ /* Do not free nb. */
|
|
|
c294fc |
+ return GRUB_ERR_NONE;
|
|
|
c294fc |
+ }
|
|
|
c294fc |
+ }
|
|
|
c294fc |
+ grub_netbuff_free (nb);
|
|
|
c294fc |
return GRUB_ERR_NONE;
|
|
|
c294fc |
case TFTP_ERROR:
|
|
|
c294fc |
data->have_oack = 1;
|
|
|
c294fc |
@@ -287,22 +250,10 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)),
|
|
|
c294fc |
}
|
|
|
c294fc |
}
|
|
|
c294fc |
|
|
|
c294fc |
-static void
|
|
|
c294fc |
-destroy_pq (tftp_data_t data)
|
|
|
c294fc |
-{
|
|
|
c294fc |
- struct grub_net_buff **nb_p;
|
|
|
c294fc |
- while ((nb_p = grub_priority_queue_top (data->pq)))
|
|
|
c294fc |
- {
|
|
|
c294fc |
- grub_netbuff_free (*nb_p);
|
|
|
c294fc |
- grub_priority_queue_pop (data->pq);
|
|
|
c294fc |
- }
|
|
|
c294fc |
-
|
|
|
c294fc |
- grub_priority_queue_destroy (data->pq);
|
|
|
c294fc |
-}
|
|
|
c294fc |
-
|
|
|
c294fc |
-/* Create a normalized copy of the filename.
|
|
|
c294fc |
- Compress any string of consecutive forward slashes to a single forward
|
|
|
c294fc |
- slash. */
|
|
|
c294fc |
+/*
|
|
|
c294fc |
+ * Create a normalized copy of the filename. Compress any string of consecutive
|
|
|
c294fc |
+ * forward slashes to a single forward slash.
|
|
|
c294fc |
+ */
|
|
|
c294fc |
static void
|
|
|
c294fc |
grub_normalize_filename (char *normalized, const char *filename)
|
|
|
c294fc |
{
|
|
|
c294fc |
@@ -395,22 +346,9 @@ tftp_open (struct grub_file *file, const char *filename)
|
|
|
c294fc |
file->not_easily_seekable = 1;
|
|
|
c294fc |
file->data = data;
|
|
|
c294fc |
|
|
|
c294fc |
- data->pq = grub_priority_queue_new (sizeof (struct grub_net_buff *), cmp);
|
|
|
c294fc |
- if (!data->pq)
|
|
|
c294fc |
- {
|
|
|
c294fc |
- grub_free (data);
|
|
|
c294fc |
- return grub_errno;
|
|
|
c294fc |
- }
|
|
|
c294fc |
-
|
|
|
c294fc |
- grub_dprintf("tftp", "resolving address for %s\n", file->device->net->server);
|
|
|
c294fc |
err = grub_net_resolve_address (file->device->net->server, &addr);
|
|
|
c294fc |
if (err)
|
|
|
c294fc |
{
|
|
|
c294fc |
- grub_dprintf ("tftp", "Address resolution failed: %d\n", err);
|
|
|
c294fc |
- grub_dprintf ("tftp", "file_size is %llu, block_size is %llu\n",
|
|
|
c294fc |
- (unsigned long long)data->file_size,
|
|
|
c294fc |
- (unsigned long long)data->block_size);
|
|
|
c294fc |
- destroy_pq (data);
|
|
|
c294fc |
grub_free (data);
|
|
|
c294fc |
return err;
|
|
|
c294fc |
}
|
|
|
c294fc |
@@ -422,7 +360,6 @@ tftp_open (struct grub_file *file, const char *filename)
|
|
|
c294fc |
if (!data->sock)
|
|
|
c294fc |
{
|
|
|
c294fc |
grub_dprintf("tftp", "connection failed\n");
|
|
|
c294fc |
- destroy_pq (data);
|
|
|
c294fc |
grub_free (data);
|
|
|
c294fc |
return grub_errno;
|
|
|
c294fc |
}
|
|
|
c294fc |
@@ -436,7 +373,6 @@ tftp_open (struct grub_file *file, const char *filename)
|
|
|
c294fc |
if (err)
|
|
|
c294fc |
{
|
|
|
c294fc |
grub_net_udp_close (data->sock);
|
|
|
c294fc |
- destroy_pq (data);
|
|
|
c294fc |
grub_free (data);
|
|
|
c294fc |
return err;
|
|
|
c294fc |
}
|
|
|
c294fc |
@@ -453,7 +389,6 @@ tftp_open (struct grub_file *file, const char *filename)
|
|
|
c294fc |
if (grub_errno)
|
|
|
c294fc |
{
|
|
|
c294fc |
grub_net_udp_close (data->sock);
|
|
|
c294fc |
- destroy_pq (data);
|
|
|
c294fc |
grub_free (data);
|
|
|
c294fc |
return grub_errno;
|
|
|
c294fc |
}
|
|
|
c294fc |
@@ -496,7 +431,6 @@ tftp_close (struct grub_file *file)
|
|
|
c294fc |
grub_print_error ();
|
|
|
c294fc |
grub_net_udp_close (data->sock);
|
|
|
c294fc |
}
|
|
|
c294fc |
- destroy_pq (data);
|
|
|
c294fc |
grub_free (data);
|
|
|
c294fc |
return GRUB_ERR_NONE;
|
|
|
c294fc |
}
|