summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/Kconfig11
-rw-r--r--net/Makefile2
-rw-r--r--net/fastboot_tcp.c193
-rw-r--r--net/lwip/Makefile2
-rw-r--r--net/lwip/dhcp.c10
-rw-r--r--net/lwip/dns.c2
-rw-r--r--net/lwip/wget.c60
-rw-r--r--net/net-common.c14
-rw-r--r--net/net.c56
-rw-r--r--net/tcp.c1297
-rw-r--r--net/wget.c565
11 files changed, 1345 insertions, 867 deletions
diff --git a/net/Kconfig b/net/Kconfig
index 76ab7d91eeb..24508026200 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -90,7 +90,7 @@ config TFTP_WINDOWSIZE
config TFTP_TSIZE
bool "Track TFTP transfers based on file size option"
depends on CMD_TFTPBOOT
- default y if (ARCH_OMAP2PLUS || ARCH_K3)
+ default y if (ARCH_OMAP2PLUS || ARCH_K3 || ARCH_RENESAS)
help
By default, TFTP progress bar is increased for each received UDP
frame, which can lead into long time being spent for sending
@@ -244,6 +244,15 @@ config NET_RANDOM_ETHADDR
generated. It will be saved to the appropriate environment variable,
too.
+config WGET
+ bool "Enable wget"
+ select PROT_TCP if NET
+ select PROT_TCP_LWIP if NET_LWIP
+ select PROT_DNS_LWIP if NET_LWIP
+ help
+ Selecting this will enable wget, an interface to send HTTP requests
+ via the network stack.
+
config TFTP_BLOCKSIZE
int "TFTP block size"
default 1468
diff --git a/net/Makefile b/net/Makefile
index 209377aeb26..7c917b318c0 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -29,7 +29,7 @@ obj-$(CONFIG_$(PHASE_)TCP_FUNCTION_FASTBOOT) += fastboot_tcp.o
obj-$(CONFIG_CMD_WOL) += wol.o
obj-$(CONFIG_PROT_UDP) += udp.o
obj-$(CONFIG_PROT_TCP) += tcp.o
-obj-$(CONFIG_CMD_WGET) += wget.o
+obj-$(CONFIG_WGET) += wget.o
# Disable this warning as it is triggered by:
# sprintf(buf, index ? "foo%d" : "foo", index)
diff --git a/net/fastboot_tcp.c b/net/fastboot_tcp.c
index d1fccbc7238..3ea25c997fc 100644
--- a/net/fastboot_tcp.c
+++ b/net/fastboot_tcp.c
@@ -8,138 +8,111 @@
#include <net/fastboot_tcp.h>
#include <net/tcp.h>
-static char command[FASTBOOT_COMMAND_LEN] = {0};
-static char response[FASTBOOT_RESPONSE_LEN] = {0};
+#define FASTBOOT_TCP_PORT 5554
static const unsigned short handshake_length = 4;
static const uchar *handshake = "FB01";
-static u16 curr_sport;
-static u16 curr_dport;
-static u32 curr_tcp_seq_num;
-static u32 curr_tcp_ack_num;
-static unsigned int curr_request_len;
-static enum fastboot_tcp_state {
- FASTBOOT_CLOSED,
- FASTBOOT_CONNECTED,
- FASTBOOT_DISCONNECTING
-} state = FASTBOOT_CLOSED;
-
-static void fastboot_tcp_answer(u8 action, unsigned int len)
-{
- const u32 response_seq_num = curr_tcp_ack_num;
- const u32 response_ack_num = curr_tcp_seq_num +
- (curr_request_len > 0 ? curr_request_len : 1);
+static char rxbuf[sizeof(u64) + FASTBOOT_COMMAND_LEN + 1];
+static char txbuf[sizeof(u64) + FASTBOOT_RESPONSE_LEN + 1];
- net_send_tcp_packet(len, htons(curr_sport), htons(curr_dport),
- action, response_seq_num, response_ack_num);
-}
+static u32 data_read;
+static u32 tx_last_offs, tx_last_len;
-static void fastboot_tcp_reset(void)
+static void tcp_stream_on_rcv_nxt_update(struct tcp_stream *tcp, u32 rx_bytes)
{
- fastboot_tcp_answer(TCP_RST, 0);
- state = FASTBOOT_CLOSED;
+ u64 cmd_size;
+ __be64 len_be;
+ char saved;
+ int fastboot_command_id, len;
+
+ if (!data_read && rx_bytes >= handshake_length) {
+ if (memcmp(rxbuf, handshake, handshake_length)) {
+ printf("fastboot: bad handshake\n");
+ tcp_stream_close(tcp);
+ return;
+ }
+
+ tx_last_offs = 0;
+ tx_last_len = handshake_length;
+ memcpy(txbuf, handshake, handshake_length);
+
+ data_read += handshake_length;
+ rx_bytes -= handshake_length;
+ if (rx_bytes > 0)
+ memmove(rxbuf, rxbuf + handshake_length, rx_bytes);
+ return;
+ }
+
+ if (rx_bytes < sizeof(u64))
+ return;
+
+ memcpy(&cmd_size, rxbuf, sizeof(u64));
+ cmd_size = __be64_to_cpu(cmd_size);
+ if (rx_bytes < sizeof(u64) + cmd_size)
+ return;
+
+ saved = rxbuf[sizeof(u64) + cmd_size];
+ rxbuf[sizeof(u64) + cmd_size] = '\0';
+ fastboot_command_id = fastboot_handle_command(rxbuf + sizeof(u64),
+ txbuf + sizeof(u64));
+ fastboot_handle_boot(fastboot_command_id,
+ strncmp("OKAY", txbuf + sizeof(u64), 4) != 0);
+ rxbuf[sizeof(u64) + cmd_size] = saved;
+
+ len = strlen(txbuf + sizeof(u64));
+ len_be = __cpu_to_be64(len);
+ memcpy(txbuf, &len_be, sizeof(u64));
+
+ tx_last_offs += tx_last_len;
+ tx_last_len = len + sizeof(u64);
+
+ data_read += sizeof(u64) + cmd_size;
+ rx_bytes -= sizeof(u64) + cmd_size;
+ if (rx_bytes > 0)
+ memmove(rxbuf, rxbuf + sizeof(u64) + cmd_size, rx_bytes);
}
-static void fastboot_tcp_send_packet(u8 action, const uchar *data, unsigned int len)
+static int tcp_stream_rx(struct tcp_stream *tcp, u32 rx_offs, void *buf, int len)
{
- uchar *pkt = net_get_async_tx_pkt_buf();
+ memcpy(rxbuf + rx_offs - data_read, buf, len);
- memset(pkt, '\0', PKTSIZE);
- pkt += net_eth_hdr_size() + IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2;
- memcpy(pkt, data, len);
- fastboot_tcp_answer(action, len);
- memset(pkt, '\0', PKTSIZE);
+ return len;
}
-static void fastboot_tcp_send_message(const char *message, unsigned int len)
+static int tcp_stream_tx(struct tcp_stream *tcp, u32 tx_offs, void *buf, int maxlen)
{
- __be64 len_be = __cpu_to_be64(len);
- uchar *pkt = net_get_async_tx_pkt_buf();
-
- memset(pkt, '\0', PKTSIZE);
- pkt += net_eth_hdr_size() + IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2;
- // Put first 8 bytes as a big endian message length
- memcpy(pkt, &len_be, 8);
- pkt += 8;
- memcpy(pkt, message, len);
- fastboot_tcp_answer(TCP_ACK | TCP_PUSH, len + 8);
- memset(pkt, '\0', PKTSIZE);
+ /* by design: tx_offs >= tx_last_offs */
+ if (tx_offs >= tx_last_offs + tx_last_len)
+ return 0;
+
+ maxlen = tx_last_offs + tx_last_len - tx_offs;
+ memcpy(buf, txbuf + (tx_offs - tx_last_offs), maxlen);
+
+ return maxlen;
}
-static void fastboot_tcp_handler_ipv4(uchar *pkt, u16 dport,
- struct in_addr sip, u16 sport,
- u32 tcp_seq_num, u32 tcp_ack_num,
- u8 action, unsigned int len)
+static int tcp_stream_on_create(struct tcp_stream *tcp)
{
- int fastboot_command_id;
- u64 command_size;
- u8 tcp_fin = action & TCP_FIN;
- u8 tcp_push = action & TCP_PUSH;
-
- curr_sport = sport;
- curr_dport = dport;
- curr_tcp_seq_num = tcp_seq_num;
- curr_tcp_ack_num = tcp_ack_num;
- curr_request_len = len;
-
- switch (state) {
- case FASTBOOT_CLOSED:
- if (tcp_push) {
- if (len != handshake_length ||
- strlen(pkt) != handshake_length ||
- memcmp(pkt, handshake, handshake_length) != 0) {
- fastboot_tcp_reset();
- break;
- }
- fastboot_tcp_send_packet(TCP_ACK | TCP_PUSH,
- handshake, handshake_length);
- state = FASTBOOT_CONNECTED;
- }
- break;
- case FASTBOOT_CONNECTED:
- if (tcp_fin) {
- fastboot_tcp_answer(TCP_FIN | TCP_ACK, 0);
- state = FASTBOOT_DISCONNECTING;
- break;
- }
- if (tcp_push) {
- // First 8 bytes is big endian message length
- command_size = __be64_to_cpu(*(u64 *)pkt);
- len -= 8;
- pkt += 8;
-
- // Only single packet messages are supported ATM
- if (strlen(pkt) != command_size) {
- fastboot_tcp_reset();
- break;
- }
- strlcpy(command, pkt, len + 1);
- fastboot_command_id = fastboot_handle_command(command, response);
- fastboot_tcp_send_message(response, strlen(response));
- fastboot_handle_boot(fastboot_command_id,
- strncmp("OKAY", response, 4) == 0);
- }
- break;
- case FASTBOOT_DISCONNECTING:
- if (tcp_push)
- state = FASTBOOT_CLOSED;
- break;
- }
+ if (tcp->lport != FASTBOOT_TCP_PORT)
+ return 0;
- memset(command, 0, FASTBOOT_COMMAND_LEN);
- memset(response, 0, FASTBOOT_RESPONSE_LEN);
- curr_sport = 0;
- curr_dport = 0;
- curr_tcp_seq_num = 0;
- curr_tcp_ack_num = 0;
- curr_request_len = 0;
+ data_read = 0;
+ tx_last_offs = 0;
+ tx_last_len = 0;
+
+ tcp->on_rcv_nxt_update = tcp_stream_on_rcv_nxt_update;
+ tcp->rx = tcp_stream_rx;
+ tcp->tx = tcp_stream_tx;
+
+ return 1;
}
void fastboot_tcp_start_server(void)
{
+ memset(net_server_ethaddr, 0, 6);
+ tcp_stream_set_on_create_handler(tcp_stream_on_create);
+
printf("Using %s device\n", eth_get_name());
printf("Listening for fastboot command on tcp %pI4\n", &net_ip);
-
- tcp_set_tcp_handler(fastboot_tcp_handler_ipv4);
}
diff --git a/net/lwip/Makefile b/net/lwip/Makefile
index f2558f8763a..79dd6b3fb50 100644
--- a/net/lwip/Makefile
+++ b/net/lwip/Makefile
@@ -5,4 +5,4 @@ obj-$(CONFIG_CMD_DHCP) += dhcp.o
obj-$(CONFIG_CMD_DNS) += dns.o
obj-$(CONFIG_CMD_PING) += ping.o
obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o
-obj-$(CONFIG_CMD_WGET) += wget.o
+obj-$(CONFIG_WGET) += wget.o
diff --git a/net/lwip/dhcp.c b/net/lwip/dhcp.c
index 9b882cf5b87..e7d9147455c 100644
--- a/net/lwip/dhcp.c
+++ b/net/lwip/dhcp.c
@@ -3,6 +3,7 @@
#include <command.h>
#include <console.h>
+#include <log.h>
#include <dm/device.h>
#include <linux/delay.h>
#include <linux/errno.h>
@@ -112,10 +113,17 @@ static int dhcp_loop(struct udevice *udev)
int do_dhcp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
int ret;
+ struct udevice *dev;
eth_set_current();
- ret = dhcp_loop(eth_get_dev());
+ dev = eth_get_dev();
+ if (!dev) {
+ log_err("No network device\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = dhcp_loop(dev);
if (ret)
return ret;
diff --git a/net/lwip/dns.c b/net/lwip/dns.c
index 4b937feaee1..1de63c9998b 100644
--- a/net/lwip/dns.c
+++ b/net/lwip/dns.c
@@ -56,7 +56,7 @@ static int dns_loop(struct udevice *udev, const char *name, const char *var)
netif = net_lwip_new_netif(udev);
if (!netif)
- return -1;
+ return CMD_RET_FAILURE;
dns_init();
diff --git a/net/lwip/wget.c b/net/lwip/wget.c
index 5501ffdd004..b76f6c0f1d9 100644
--- a/net/lwip/wget.c
+++ b/net/lwip/wget.c
@@ -27,6 +27,8 @@ enum done_state {
};
struct wget_ctx {
+ char server_name[SERVER_NAME_SIZE];
+ u16 port;
char *path;
ulong daddr;
ulong saved_daddr;
@@ -36,6 +38,23 @@ struct wget_ctx {
enum done_state done;
};
+static void wget_lwip_fill_info(struct pbuf *hdr, u16_t hdr_len, u32_t hdr_cont_len)
+{
+ if (wget_info->headers) {
+ if (hdr_len < MAX_HTTP_HEADERS_SIZE)
+ pbuf_copy_partial(hdr, (void *)wget_info->headers, hdr_len, 0);
+ else
+ hdr_len = 0;
+ wget_info->headers[hdr_len] = 0;
+ }
+ wget_info->hdr_cont_len = (u32)hdr_cont_len;
+}
+
+static void wget_lwip_set_file_size(u32_t rx_content_len)
+{
+ wget_info->file_size = (ulong)rx_content_len;
+}
+
bool wget_validate_uri(char *uri);
int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len,
@@ -215,6 +234,13 @@ static void httpc_result_cb(void *arg, httpc_result_t httpc_result,
struct wget_ctx *ctx = arg;
ulong elapsed;
+ wget_info->status_code = (u32)srv_res;
+
+ if (err == ERR_BUF) {
+ ctx->done = FAILURE;
+ return;
+ }
+
if (httpc_result != HTTPC_RESULT_OK) {
log_err("\nHTTP client error %d\n", httpc_result);
ctx->done = FAILURE;
@@ -234,8 +260,10 @@ static void httpc_result_cb(void *arg, httpc_result_t httpc_result,
printf("%u bytes transferred in %lu ms (", rx_content_len, elapsed);
print_size(rx_content_len / elapsed * 1000, "/s)\n");
printf("Bytes transferred = %lu (%lx hex)\n", ctx->size, ctx->size);
- efi_set_bootdev("Net", "", ctx->path, map_sysmem(ctx->saved_daddr, 0),
- rx_content_len);
+ if (wget_info->set_bootdev)
+ efi_set_bootdev("Http", ctx->server_name, ctx->path, map_sysmem(ctx->saved_daddr, 0),
+ rx_content_len);
+ wget_lwip_set_file_size(rx_content_len);
if (env_set_hex("filesize", rx_content_len) ||
env_set_hex("fileaddr", ctx->saved_daddr)) {
log_err("Could not set filesize or fileaddr\n");
@@ -246,9 +274,19 @@ static void httpc_result_cb(void *arg, httpc_result_t httpc_result,
ctx->done = SUCCESS;
}
+static err_t httpc_headers_done_cb(httpc_state_t *connection, void *arg, struct pbuf *hdr,
+ u16_t hdr_len, u32_t content_len)
+{
+ wget_lwip_fill_info(hdr, hdr_len, content_len);
+
+ if (wget_info->check_buffer_size && (ulong)content_len > wget_info->buffer_size)
+ return ERR_BUF;
+
+ return ERR_OK;
+}
+
static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri)
{
- char server_name[SERVER_NAME_SIZE];
#if defined CONFIG_WGET_HTTPS
altcp_allocator_t tls_allocator;
#endif
@@ -257,7 +295,6 @@ static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri)
struct netif *netif;
struct wget_ctx ctx;
char *path;
- u16 port;
bool is_https;
ctx.daddr = dst_addr;
@@ -267,7 +304,7 @@ static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri)
ctx.prevsize = 0;
ctx.start_time = 0;
- if (parse_url(uri, server_name, &port, &path, &is_https))
+ if (parse_url(uri, ctx.server_name, &ctx.port, &path, &is_https))
return CMD_RET_USAGE;
netif = net_lwip_new_netif(udev);
@@ -279,7 +316,7 @@ static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri)
if (is_https) {
tls_allocator.alloc = &altcp_tls_alloc;
tls_allocator.arg =
- altcp_tls_create_config_client(NULL, 0, server_name);
+ altcp_tls_create_config_client(NULL, 0, ctx.server_name);
if (!tls_allocator.arg) {
log_err("error: Cannot create a TLS connection\n");
@@ -292,8 +329,9 @@ static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri)
#endif
conn.result_fn = httpc_result_cb;
+ conn.headers_done_fn = httpc_headers_done_cb;
ctx.path = path;
- if (httpc_get_file_dns(server_name, port, path, &conn, httpc_recv_cb,
+ if (httpc_get_file_dns(ctx.server_name, ctx.port, path, &conn, httpc_recv_cb,
&ctx, &state)) {
net_lwip_remove_netif(netif);
return CMD_RET_FAILURE;
@@ -314,10 +352,13 @@ static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri)
return -1;
}
-int wget_with_dns(ulong dst_addr, char *uri)
+int wget_do_request(ulong dst_addr, char *uri)
{
eth_set_current();
+ if (!wget_info)
+ wget_info = &default_wget_info;
+
return wget_loop(eth_get_dev(), dst_addr, uri);
}
@@ -344,7 +385,8 @@ int do_wget(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
if (parse_legacy_arg(url, nurl, sizeof(nurl)))
return CMD_RET_FAILURE;
- if (wget_with_dns(dst_addr, nurl))
+ wget_info = &default_wget_info;
+ if (wget_do_request(dst_addr, nurl))
return CMD_RET_FAILURE;
return CMD_RET_SUCCESS;
diff --git a/net/net-common.c b/net/net-common.c
index a7f767d5e9c..e01b0da7d7b 100644
--- a/net/net-common.c
+++ b/net/net-common.c
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
+#include <net-common.h>
void copy_filename(char *dst, const char *src, int size)
{
@@ -11,3 +12,16 @@ void copy_filename(char *dst, const char *src, int size)
*dst++ = *src++;
*dst = '\0';
}
+
+struct wget_http_info default_wget_info = {
+ .method = WGET_HTTP_METHOD_GET,
+ .set_bootdev = true,
+};
+
+struct wget_http_info *wget_info;
+
+int wget_request(ulong dst_addr, char *uri, struct wget_http_info *info)
+{
+ wget_info = info ? info : &default_wget_info;
+ return wget_do_request(dst_addr, uri);
+}
diff --git a/net/net.c b/net/net.c
index f47e9fbe33a..1828f1cca36 100644
--- a/net/net.c
+++ b/net/net.c
@@ -89,41 +89,43 @@
#include <image.h>
#include <led.h>
#include <log.h>
+#if defined(CONFIG_LED_STATUS)
+#include <miiphy.h>
+#endif
#include <net.h>
#include <net6.h>
#include <ndisc.h>
+#if defined(CONFIG_LED_STATUS)
+#include <status_led.h>
+#endif
+#include <watchdog.h>
+#include <linux/compiler.h>
#include <net/fastboot_udp.h>
#include <net/fastboot_tcp.h>
-#include <net/tftp.h>
#include <net/ncsi.h>
#if defined(CONFIG_CMD_PCAP)
#include <net/pcap.h>
#endif
-#include <net/udp.h>
-#if defined(CONFIG_LED_STATUS)
-#include <miiphy.h>
-#include <status_led.h>
-#endif
-#include <watchdog.h>
-#include <linux/compiler.h>
-#include <test/test.h>
#include <net/tcp.h>
+#include <net/tftp.h>
+#include <net/udp.h>
#include <net/wget.h>
+#include <test/test.h>
#include "arp.h"
#include "bootp.h"
#include "cdp.h"
+#include "dhcpv6.h"
#if defined(CONFIG_CMD_DNS)
#include "dns.h"
#endif
#include "link_local.h"
+#include "net_rand.h"
#include "nfs.h"
#include "ping.h"
#include "rarp.h"
#if defined(CONFIG_CMD_WOL)
#include "wol.h"
#endif
-#include "dhcpv6.h"
-#include "net_rand.h"
/** BOOTP EXTENTIONS **/
@@ -420,7 +422,7 @@ int net_init(void)
/* Only need to setup buffer pointers once. */
first_call = 0;
if (IS_ENABLED(CONFIG_PROT_TCP))
- tcp_set_tcp_state(TCP_CLOSED);
+ tcp_init();
}
return net_init_loop();
@@ -652,6 +654,9 @@ restart:
* errors that may have happened.
*/
eth_rx();
+#if defined(CONFIG_PROT_TCP)
+ tcp_streams_poll();
+#endif
/*
* Abort if ctrl-c was pressed.
@@ -908,10 +913,10 @@ int net_send_udp_packet(uchar *ether, struct in_addr dest, int dport, int sport,
}
#if defined(CONFIG_PROT_TCP)
-int net_send_tcp_packet(int payload_len, int dport, int sport, u8 action,
- u32 tcp_seq_num, u32 tcp_ack_num)
+int net_send_tcp_packet(int payload_len, struct in_addr dhost, int dport,
+ int sport, u8 action, u32 tcp_seq_num, u32 tcp_ack_num)
{
- return net_send_ip_packet(net_server_ethaddr, net_server_ip, dport,
+ return net_send_ip_packet(net_server_ethaddr, dhost, dport,
sport, payload_len, IPPROTO_TCP, action,
tcp_seq_num, tcp_ack_num);
}
@@ -924,6 +929,9 @@ int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport,
uchar *pkt;
int eth_hdr_size;
int pkt_hdr_size;
+#if defined(CONFIG_PROT_TCP)
+ struct tcp_stream *tcp;
+#endif
/* make sure the net_tx_packet is initialized (net_init() was called) */
assert(net_tx_packet != NULL);
@@ -950,10 +958,15 @@ int net_send_ip_packet(uchar *ether, struct in_addr dest, int dport, int sport,
break;
#if defined(CONFIG_PROT_TCP)
case IPPROTO_TCP:
+ tcp = tcp_stream_get(0, dest, dport, sport);
+ if (!tcp)
+ return -EINVAL;
+
pkt_hdr_size = eth_hdr_size
- + tcp_set_tcp_header(pkt + eth_hdr_size, dport, sport,
+ + tcp_set_tcp_header(tcp, pkt + eth_hdr_size,
payload_len, action, tcp_seq_num,
tcp_ack_num);
+ tcp_stream_put(tcp);
break;
#endif
default:
@@ -1723,17 +1736,6 @@ int net_parse_bootfile(struct in_addr *ipaddr, char *filename, int max_len)
return 1;
}
-void ip_to_string(struct in_addr x, char *s)
-{
- x.s_addr = ntohl(x.s_addr);
- sprintf(s, "%d.%d.%d.%d",
- (int) ((x.s_addr >> 24) & 0xff),
- (int) ((x.s_addr >> 16) & 0xff),
- (int) ((x.s_addr >> 8) & 0xff),
- (int) ((x.s_addr >> 0) & 0xff)
- );
-}
-
void vlan_to_string(ushort x, char *s)
{
x = ntohs(x);
diff --git a/net/tcp.c b/net/tcp.c
index b0cc8a1fe3e..2635727f47d 100644
--- a/net/tcp.c
+++ b/net/tcp.c
@@ -25,33 +25,29 @@
#include <net/tcp.h>
/*
- * TCP sliding window control used by us to request re-TX
+ * The start sequence number increment for the two sequently created
+ * connections within the same timer tick. This number must be:
+ * - prime (to increase the time before the same number will be generated)
+ * - larger than typical MTU (to avoid similar numbers for two sequently
+ * created connections)
*/
-static struct tcp_sack_v tcp_lost;
+#define TCP_START_SEQ_INC 2153 /* just large prime number */
-/* TCP option timestamp */
-static u32 loc_timestamp;
-static u32 rmt_timestamp;
+#define TCP_SEND_RETRY 3
+#define TCP_SEND_TIMEOUT 2000UL
+#define TCP_RX_INACTIVE_TIMEOUT 30000UL
+#if PKTBUFSRX != 0
+ #define TCP_RCV_WND_SIZE (PKTBUFSRX * TCP_MSS)
+#else
+ #define TCP_RCV_WND_SIZE (4 * TCP_MSS)
+#endif
-static u32 tcp_seq_init;
-static u32 tcp_ack_edge;
+#define TCP_PACKET_OK 0
+#define TCP_PACKET_DROP 1
-static int tcp_activity_count;
+static struct tcp_stream tcp_stream;
-/*
- * Search for TCP_SACK and review the comments before the code section
- * TCP_SACK is the number of packets at the front of the stream
- */
-
-enum pkt_state {PKT, NOPKT};
-struct sack_r {
- struct sack_edges se;
- enum pkt_state st;
-};
-
-static struct sack_r edge_a[TCP_SACK];
-static unsigned int sack_idx;
-static unsigned int prev_len;
+static int (*tcp_stream_on_create)(struct tcp_stream *tcp);
/*
* TCP lengths are stored as a rounded up number of 32 bit words.
@@ -60,52 +56,386 @@ static unsigned int prev_len;
*/
#define LEN_B_TO_DW(x) ((x) >> 2)
#define ROUND_TCPHDR_LEN(x) (LEN_B_TO_DW((x) + 3))
+#define ROUND_TCPHDR_BYTES(x) (((x) + 3) & ~3)
#define SHIFT_TO_TCPHDRLEN_FIELD(x) ((x) << 4)
#define GET_TCP_HDR_LEN_IN_BYTES(x) ((x) >> 2)
-/* TCP connection state */
-static enum tcp_state current_tcp_state;
+#define RANDOM_PORT_START 1024
+#define RANDOM_PORT_RANGE 0x4000
+
+/**
+ * random_port() - make port a little random (1024-17407)
+ *
+ * Return: random port number from 1024 to 17407
+ *
+ * This keeps the math somewhat trivial to compute, and seems to work with
+ * all supported protocols/clients/servers
+ */
+static uint random_port(void)
+{
+ return RANDOM_PORT_START + (get_timer(0) % RANDOM_PORT_RANGE);
+}
+
+static inline s32 tcp_seq_cmp(u32 a, u32 b)
+{
+ return (s32)(a - b);
+}
+
+static inline u32 tcp_get_start_seq(void)
+{
+ static u32 tcp_seq_inc;
+ u32 tcp_seq;
+
+ tcp_seq = (get_timer(0) & 0xffffffff) + tcp_seq_inc;
+ tcp_seq_inc += TCP_START_SEQ_INC;
-/* Current TCP RX packet handler */
-static rxhand_tcp *tcp_packet_handler;
+ return tcp_seq;
+}
+
+static inline ulong msec_to_ticks(ulong msec)
+{
+ return msec * CONFIG_SYS_HZ / 1000;
+}
/**
- * tcp_get_tcp_state() - get current TCP state
+ * tcp_stream_get_state() - get TCP stream state
+ * @tcp: tcp stream
*
- * Return: Current TCP state
+ * Return: TCP stream state
*/
-enum tcp_state tcp_get_tcp_state(void)
+enum tcp_state tcp_stream_get_state(struct tcp_stream *tcp)
{
- return current_tcp_state;
+ return tcp->state;
}
/**
- * tcp_set_tcp_state() - set current TCP state
+ * tcp_stream_set_state() - set TCP stream state
+ * @tcp: tcp stream
* @new_state: new TCP state
*/
-void tcp_set_tcp_state(enum tcp_state new_state)
+static void tcp_stream_set_state(struct tcp_stream *tcp,
+ enum tcp_state new_state)
{
- current_tcp_state = new_state;
+ tcp->state = new_state;
}
-static void dummy_handler(uchar *pkt, u16 dport,
- struct in_addr sip, u16 sport,
- u32 tcp_seq_num, u32 tcp_ack_num,
- u8 action, unsigned int len)
+/**
+ * tcp_stream_get_status() - get TCP stream status
+ * @tcp: tcp stream
+ *
+ * Return: TCP stream status
+ */
+enum tcp_status tcp_stream_get_status(struct tcp_stream *tcp)
{
+ return tcp->status;
}
/**
- * tcp_set_tcp_handler() - set a handler to receive data
- * @f: handler
+ * tcp_stream_set_status() - set TCP stream state
+ * @tcp: tcp stream
+ * @new_satus: new TCP stream status
*/
-void tcp_set_tcp_handler(rxhand_tcp *f)
+static void tcp_stream_set_status(struct tcp_stream *tcp,
+ enum tcp_status new_status)
+{
+ tcp->status = new_status;
+}
+
+void tcp_stream_restart_rx_timer(struct tcp_stream *tcp)
+{
+ tcp->time_last_rx = get_timer(0);
+}
+
+static void tcp_stream_init(struct tcp_stream *tcp,
+ struct in_addr rhost, u16 rport, u16 lport)
+{
+ memset(tcp, 0, sizeof(struct tcp_stream));
+ tcp->rhost.s_addr = rhost.s_addr;
+ tcp->rport = rport;
+ tcp->lport = lport;
+ tcp->state = TCP_CLOSED;
+ tcp->lost.len = TCP_OPT_LEN_2;
+ tcp->rcv_wnd = TCP_RCV_WND_SIZE;
+ tcp->max_retry_count = TCP_SEND_RETRY;
+ tcp->initial_timeout = TCP_SEND_TIMEOUT;
+ tcp->rx_inactiv_timeout = TCP_RX_INACTIVE_TIMEOUT;
+ tcp_stream_restart_rx_timer(tcp);
+}
+
+static void tcp_stream_destroy(struct tcp_stream *tcp)
+{
+ if (tcp->on_closed)
+ tcp->on_closed(tcp);
+ memset(tcp, 0, sizeof(struct tcp_stream));
+}
+
+void tcp_init(void)
+{
+ static int initialized;
+ struct tcp_stream *tcp = &tcp_stream;
+
+ tcp_stream_on_create = NULL;
+ if (!initialized) {
+ initialized = 1;
+ memset(tcp, 0, sizeof(struct tcp_stream));
+ }
+
+ tcp_stream_set_state(tcp, TCP_CLOSED);
+ tcp_stream_set_status(tcp, TCP_ERR_RST);
+ tcp_stream_destroy(tcp);
+}
+
+void tcp_stream_set_on_create_handler(int (*on_create)(struct tcp_stream *))
+{
+ tcp_stream_on_create = on_create;
+}
+
+static struct tcp_stream *tcp_stream_add(struct in_addr rhost,
+ u16 rport, u16 lport)
+{
+ struct tcp_stream *tcp = &tcp_stream;
+
+ if (!tcp_stream_on_create ||
+ tcp->state != TCP_CLOSED)
+ return NULL;
+
+ tcp_stream_init(tcp, rhost, rport, lport);
+ if (!tcp_stream_on_create(tcp))
+ return NULL;
+
+ return tcp;
+}
+
+struct tcp_stream *tcp_stream_get(int is_new, struct in_addr rhost,
+ u16 rport, u16 lport)
+{
+ struct tcp_stream *tcp = &tcp_stream;
+
+ if (tcp->rhost.s_addr == rhost.s_addr &&
+ tcp->rport == rport &&
+ tcp->lport == lport)
+ return tcp;
+
+ return is_new ? tcp_stream_add(rhost, rport, lport) : NULL;
+}
+
+void tcp_stream_put(struct tcp_stream *tcp)
+{
+ if (tcp->state == TCP_CLOSED)
+ tcp_stream_destroy(tcp);
+}
+
+u32 tcp_stream_rx_offs(struct tcp_stream *tcp)
+{
+ u32 ret;
+
+ switch (tcp->state) {
+ case TCP_CLOSED:
+ case TCP_SYN_SENT:
+ case TCP_SYN_RECEIVED:
+ return 0;
+ default:
+ break;
+ }
+
+ ret = tcp->rcv_nxt - tcp->irs - 1;
+ if (tcp->fin_rx && tcp->rcv_nxt == tcp->fin_rx_seq)
+ ret--;
+
+ return ret;
+}
+
+u32 tcp_stream_tx_offs(struct tcp_stream *tcp)
+{
+ u32 ret;
+
+ switch (tcp->state) {
+ case TCP_CLOSED:
+ case TCP_SYN_SENT:
+ case TCP_SYN_RECEIVED:
+ return 0;
+ default:
+ break;
+ }
+
+ ret = tcp->snd_una - tcp->iss - 1;
+ if (tcp->fin_tx && tcp->snd_una == tcp->fin_tx_seq + 1)
+ ret--;
+
+ return ret;
+}
+
+static void tcp_stream_set_time_handler(struct tcp_stream *tcp, ulong msec,
+ void (*handler)(struct tcp_stream *))
+{
+ if (!msec) {
+ tcp->time_handler = NULL;
+ return;
+ }
+
+ tcp->time_handler = handler;
+ tcp->time_start = get_timer(0);
+ tcp->time_delta = msec_to_ticks(msec);
+}
+
+static void tcp_send_packet(struct tcp_stream *tcp, u8 action,
+ u32 tcp_seq_num, u32 tcp_ack_num, u32 tx_len)
+{
+ tcp->tx_packets++;
+ net_send_tcp_packet(tx_len, tcp->rhost, tcp->rport,
+ tcp->lport, action, tcp_seq_num,
+ tcp_ack_num);
+}
+
+static void tcp_send_repeat(struct tcp_stream *tcp)
{
- debug_cond(DEBUG_INT_STATE, "--- net_loop TCP handler set (%p)\n", f);
- if (!f)
- tcp_packet_handler = dummy_handler;
- else
- tcp_packet_handler = f;
+ uchar *ptr;
+ u32 tcp_opts_size;
+ int ret;
+
+ if (!tcp->retry_cnt) {
+ puts("\nTCP: send retry counter exceeded\n");
+ tcp_send_packet(tcp, TCP_RST, tcp->retry_seq_num,
+ tcp->rcv_nxt, 0);
+ tcp_stream_set_status(tcp, TCP_ERR_TOUT);
+ tcp_stream_set_state(tcp, TCP_CLOSED);
+ tcp_stream_destroy(tcp);
+ return;
+ }
+ tcp->retry_cnt--;
+ tcp->retry_timeout += tcp->initial_timeout;
+
+ if (tcp->retry_tx_len > 0) {
+ tcp_opts_size = ROUND_TCPHDR_BYTES(TCP_TSOPT_SIZE +
+ tcp->lost.len);
+ ptr = net_tx_packet + net_eth_hdr_size() +
+ IP_TCP_HDR_SIZE + tcp_opts_size;
+
+ if (tcp->retry_tx_len > TCP_MSS - tcp_opts_size)
+ tcp->retry_tx_len = TCP_MSS - tcp_opts_size;
+
+ /* refill packet data */
+ ret = tcp->tx(tcp, tcp->retry_tx_offs, ptr, tcp->retry_tx_len);
+ if (ret < 0) {
+ puts("\nTCP: send failure\n");
+ tcp_send_packet(tcp, TCP_RST, tcp->retry_seq_num,
+ tcp->rcv_nxt, 0);
+ tcp_stream_set_status(tcp, TCP_ERR_IO);
+ tcp_stream_set_state(tcp, TCP_CLOSED);
+ tcp_stream_destroy(tcp);
+ return;
+ }
+ }
+ tcp_send_packet(tcp, tcp->retry_action, tcp->retry_seq_num,
+ tcp->rcv_nxt, tcp->retry_tx_len);
+
+ tcp_stream_set_time_handler(tcp, tcp->retry_timeout, tcp_send_repeat);
+}
+
+static void tcp_send_packet_with_retry(struct tcp_stream *tcp, u8 action,
+ u32 tcp_seq_num, u32 tx_len, u32 tx_offs)
+{
+ tcp->retry_cnt = tcp->max_retry_count;
+ tcp->retry_timeout = tcp->initial_timeout;
+ tcp->retry_action = action;
+ tcp->retry_seq_num = tcp_seq_num;
+ tcp->retry_tx_len = tx_len;
+ tcp->retry_tx_offs = tx_offs;
+
+ tcp_send_packet(tcp, action, tcp_seq_num, tcp->rcv_nxt, tx_len);
+ tcp_stream_set_time_handler(tcp, tcp->retry_timeout, tcp_send_repeat);
+}
+
+static inline u8 tcp_stream_fin_needed(struct tcp_stream *tcp, u32 tcp_seq_num)
+{
+ return (tcp->fin_tx && (tcp_seq_num == tcp->fin_tx_seq)) ? TCP_FIN : 0;
+}
+
+static void tcp_steam_tx_try(struct tcp_stream *tcp)
+{
+ uchar *ptr;
+ int tx_len;
+ u32 tx_offs, tcp_opts_size;
+
+ if (tcp->state != TCP_ESTABLISHED ||
+ tcp->time_handler ||
+ !tcp->tx)
+ return;
+
+ tcp_opts_size = ROUND_TCPHDR_BYTES(TCP_TSOPT_SIZE + tcp->lost.len);
+ tx_len = TCP_MSS - tcp_opts_size;
+ if (tcp->fin_tx) {
+ /* do not try to send beyonds FIN packet limits */
+ if (tcp_seq_cmp(tcp->snd_una, tcp->fin_tx_seq) >= 0)
+ return;
+
+ tx_len = tcp->fin_tx_seq - tcp->snd_una;
+ if (tx_len > TCP_MSS - tcp_opts_size)
+ tx_len = TCP_MSS - tcp_opts_size;
+ }
+
+ tx_offs = tcp_stream_tx_offs(tcp);
+ ptr = net_tx_packet + net_eth_hdr_size() +
+ IP_TCP_HDR_SIZE + tcp_opts_size;
+
+ /* fill packet data and adjust size */
+ tx_len = tcp->tx(tcp, tx_offs, ptr, tx_len);
+ if (tx_len < 0) {
+ puts("\nTCP: send failure\n");
+ tcp_send_packet(tcp, TCP_RST, tcp->retry_seq_num,
+ tcp->rcv_nxt, 0);
+ tcp_stream_set_status(tcp, TCP_ERR_IO);
+ tcp_stream_set_state(tcp, TCP_CLOSED);
+ tcp_stream_destroy(tcp);
+ return;
+ }
+ if (!tx_len)
+ return;
+
+ if (tcp_seq_cmp(tcp->snd_una + tx_len, tcp->snd_nxt) > 0)
+ tcp->snd_nxt = tcp->snd_una + tx_len;
+
+ tcp_send_packet_with_retry(tcp, TCP_ACK | TCP_PUSH,
+ tcp->snd_una, tx_len, tx_offs);
+}
+
+static void tcp_stream_poll(struct tcp_stream *tcp, ulong time)
+{
+ ulong delta;
+ void (*handler)(struct tcp_stream *tcp);
+
+ if (tcp->state == TCP_CLOSED)
+ return;
+
+ /* handle rx inactivity timeout */
+ delta = msec_to_ticks(tcp->rx_inactiv_timeout);
+ if (time - tcp->time_last_rx >= delta) {
+ puts("\nTCP: rx inactivity timeout exceeded\n");
+ tcp_stream_reset(tcp);
+ tcp_stream_set_status(tcp, TCP_ERR_TOUT);
+ tcp_stream_destroy(tcp);
+ return;
+ }
+
+ /* handle retransmit timeout */
+ if (tcp->time_handler &&
+ time - tcp->time_start >= tcp->time_delta) {
+ handler = tcp->time_handler;
+ tcp->time_handler = NULL;
+ handler(tcp);
+ }
+
+ tcp_steam_tx_try(tcp);
+}
+
+void tcp_streams_poll(void)
+{
+ ulong time;
+ struct tcp_stream *tcp;
+
+ time = get_timer(0);
+ tcp = &tcp_stream;
+ tcp_stream_poll(tcp, time);
}
/**
@@ -148,29 +478,30 @@ u16 tcp_set_pseudo_header(uchar *pkt, struct in_addr src, struct in_addr dest,
/**
* net_set_ack_options() - set TCP options in acknowledge packets
+ * @tcp: tcp stream
* @b: the packet
*
* Return: TCP header length
*/
-int net_set_ack_options(union tcp_build_pkt *b)
+int net_set_ack_options(struct tcp_stream *tcp, union tcp_build_pkt *b)
{
b->sack.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
b->sack.t_opt.kind = TCP_O_TS;
b->sack.t_opt.len = TCP_OPT_LEN_A;
- b->sack.t_opt.t_snd = htons(loc_timestamp);
- b->sack.t_opt.t_rcv = rmt_timestamp;
+ b->sack.t_opt.t_snd = htons(tcp->loc_timestamp);
+ b->sack.t_opt.t_rcv = tcp->rmt_timestamp;
b->sack.sack_v.kind = TCP_1_NOP;
b->sack.sack_v.len = 0;
if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) {
- if (tcp_lost.len > TCP_OPT_LEN_2) {
+ if (tcp->lost.len > TCP_OPT_LEN_2) {
debug_cond(DEBUG_DEV_PKT, "TCP ack opt lost.len %x\n",
- tcp_lost.len);
- b->sack.sack_v.len = tcp_lost.len;
+ tcp->lost.len);
+ b->sack.sack_v.len = tcp->lost.len;
b->sack.sack_v.kind = TCP_V_SACK;
- b->sack.sack_v.hill[0].l = htonl(tcp_lost.hill[0].l);
- b->sack.sack_v.hill[0].r = htonl(tcp_lost.hill[0].r);
+ b->sack.sack_v.hill[0].l = htonl(tcp->lost.hill[0].l);
+ b->sack.sack_v.hill[0].r = htonl(tcp->lost.hill[0].r);
/*
* These SACK structures are initialized with NOPs to
@@ -178,17 +509,17 @@ int net_set_ack_options(union tcp_build_pkt *b)
* SACK structures used for both header padding and
* internally.
*/
- b->sack.sack_v.hill[1].l = htonl(tcp_lost.hill[1].l);
- b->sack.sack_v.hill[1].r = htonl(tcp_lost.hill[1].r);
- b->sack.sack_v.hill[2].l = htonl(tcp_lost.hill[2].l);
- b->sack.sack_v.hill[2].r = htonl(tcp_lost.hill[2].r);
+ b->sack.sack_v.hill[1].l = htonl(tcp->lost.hill[1].l);
+ b->sack.sack_v.hill[1].r = htonl(tcp->lost.hill[1].r);
+ b->sack.sack_v.hill[2].l = htonl(tcp->lost.hill[2].l);
+ b->sack.sack_v.hill[2].r = htonl(tcp->lost.hill[2].r);
b->sack.sack_v.hill[3].l = TCP_O_NOP;
b->sack.sack_v.hill[3].r = TCP_O_NOP;
}
b->sack.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(ROUND_TCPHDR_LEN(TCP_HDR_SIZE +
TCP_TSOPT_SIZE +
- tcp_lost.len));
+ tcp->lost.len));
} else {
b->sack.sack_v.kind = 0;
b->sack.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(ROUND_TCPHDR_LEN(TCP_HDR_SIZE +
@@ -199,18 +530,18 @@ int net_set_ack_options(union tcp_build_pkt *b)
* This returns the actual rounded up length of the
* TCP header to add to the total packet length
*/
-
return GET_TCP_HDR_LEN_IN_BYTES(b->sack.hdr.tcp_hlen);
}
/**
- * net_set_ack_options() - set TCP options in SYN packets
+ * net_set_syn_options() - set TCP options in SYN packets
+ * @tcp: tcp stream
* @b: the packet
*/
-void net_set_syn_options(union tcp_build_pkt *b)
+void net_set_syn_options(struct tcp_stream *tcp, union tcp_build_pkt *b)
{
if (IS_ENABLED(CONFIG_PROT_TCP_SACK))
- tcp_lost.len = 0;
+ tcp->lost.len = 0;
b->ip.hdr.tcp_hlen = 0xa0;
@@ -229,17 +560,40 @@ void net_set_syn_options(union tcp_build_pkt *b)
}
b->ip.t_opt.kind = TCP_O_TS;
b->ip.t_opt.len = TCP_OPT_LEN_A;
- loc_timestamp = get_ticks();
- rmt_timestamp = 0;
+ tcp->loc_timestamp = get_ticks();
+ tcp->rmt_timestamp = 0;
b->ip.t_opt.t_snd = 0;
b->ip.t_opt.t_rcv = 0;
b->ip.end = TCP_O_END;
}
-int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len,
+const char *tcpflags_to_str(char tcpflags, char *buf, int size)
+{
+ int i;
+ static const struct {
+ int bit;
+ const char *name;
+ } desc[] = {{TCP_RST, "RST"}, {TCP_SYN, "SYN"}, {TCP_PUSH, "PSH"},
+ {TCP_FIN, "FIN"}, {TCP_ACK, "ACK"}};
+
+ *buf = '\0';
+ for (i = 0; i < ARRAY_SIZE(desc); i++) {
+ if (!(tcpflags & desc[i].bit))
+ continue;
+
+ if (*buf)
+ strlcat(buf, ",", size);
+ strlcat(buf, desc[i].name, size);
+ }
+
+ return buf;
+}
+
+int tcp_set_tcp_header(struct tcp_stream *tcp, uchar *pkt, int payload_len,
u8 action, u32 tcp_seq_num, u32 tcp_ack_num)
{
union tcp_build_pkt *b = (union tcp_build_pkt *)pkt;
+ char buf[24];
int pkt_hdr_len;
int pkt_len;
int tcp_len;
@@ -249,79 +603,42 @@ int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len,
* 4 bits reserved options
*/
b->ip.hdr.tcp_flags = action;
- pkt_hdr_len = IP_TCP_HDR_SIZE;
b->ip.hdr.tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
switch (action) {
case TCP_SYN:
debug_cond(DEBUG_DEV_PKT,
- "TCP Hdr:SYN (%pI4, %pI4, sq=%u, ak=%u)\n",
- &net_server_ip, &net_ip,
- tcp_seq_num, tcp_ack_num);
- tcp_activity_count = 0;
- net_set_syn_options(b);
- tcp_seq_num = 0;
- tcp_ack_num = 0;
+ "TCP Hdr:%s (%pI4, %pI4, s=%u, a=%u)\n",
+ tcpflags_to_str(action, buf, sizeof(buf)),
+ &tcp->rhost, &net_ip, tcp_seq_num, tcp_ack_num);
+ net_set_syn_options(tcp, b);
pkt_hdr_len = IP_TCP_O_SIZE;
- if (current_tcp_state == TCP_SYN_SENT) { /* Too many SYNs */
- action = TCP_FIN;
- current_tcp_state = TCP_FIN_WAIT_1;
- } else {
- current_tcp_state = TCP_SYN_SENT;
- }
- break;
- case TCP_SYN | TCP_ACK:
- case TCP_ACK:
- pkt_hdr_len = IP_HDR_SIZE + net_set_ack_options(b);
- b->ip.hdr.tcp_flags = action;
- debug_cond(DEBUG_DEV_PKT,
- "TCP Hdr:ACK (%pI4, %pI4, s=%u, a=%u, A=%x)\n",
- &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num,
- action);
- break;
- case TCP_FIN:
- debug_cond(DEBUG_DEV_PKT,
- "TCP Hdr:FIN (%pI4, %pI4, s=%u, a=%u)\n",
- &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num);
- payload_len = 0;
- pkt_hdr_len = IP_TCP_HDR_SIZE;
- current_tcp_state = TCP_FIN_WAIT_1;
break;
case TCP_RST | TCP_ACK:
case TCP_RST:
debug_cond(DEBUG_DEV_PKT,
- "TCP Hdr:RST (%pI4, %pI4, s=%u, a=%u)\n",
- &net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num);
- current_tcp_state = TCP_CLOSED;
+ "TCP Hdr:%s (%pI4, %pI4, s=%u, a=%u)\n",
+ tcpflags_to_str(action, buf, sizeof(buf)),
+ &tcp->rhost, &net_ip, tcp_seq_num, tcp_ack_num);
+ pkt_hdr_len = IP_TCP_HDR_SIZE;
break;
- /* Notify connection closing */
- case (TCP_FIN | TCP_ACK):
- case (TCP_FIN | TCP_ACK | TCP_PUSH):
- if (current_tcp_state == TCP_CLOSE_WAIT)
- current_tcp_state = TCP_CLOSING;
-
- debug_cond(DEBUG_DEV_PKT,
- "TCP Hdr:FIN ACK PSH(%pI4, %pI4, s=%u, a=%u, A=%x)\n",
- &net_server_ip, &net_ip,
- tcp_seq_num, tcp_ack_num, action);
- fallthrough;
default:
- pkt_hdr_len = IP_HDR_SIZE + net_set_ack_options(b);
- b->ip.hdr.tcp_flags = action | TCP_PUSH | TCP_ACK;
+ pkt_hdr_len = IP_HDR_SIZE + net_set_ack_options(tcp, b);
debug_cond(DEBUG_DEV_PKT,
- "TCP Hdr:dft (%pI4, %pI4, s=%u, a=%u, A=%x)\n",
- &net_server_ip, &net_ip,
- tcp_seq_num, tcp_ack_num, action);
+ "TCP Hdr:%s (%pI4, %pI4, s=%u, a=%u)\n",
+ tcpflags_to_str(action, buf, sizeof(buf)),
+ &tcp->rhost, &net_ip, tcp_seq_num, tcp_ack_num);
+ break;
}
pkt_len = pkt_hdr_len + payload_len;
tcp_len = pkt_len - IP_HDR_SIZE;
- tcp_ack_edge = tcp_ack_num;
+ tcp->rcv_nxt = tcp_ack_num;
/* TCP Header */
- b->ip.hdr.tcp_ack = htonl(tcp_ack_edge);
- b->ip.hdr.tcp_src = htons(sport);
- b->ip.hdr.tcp_dst = htons(dport);
+ b->ip.hdr.tcp_ack = htonl(tcp->rcv_nxt);
+ b->ip.hdr.tcp_src = htons(tcp->lport);
+ b->ip.hdr.tcp_dst = htons(tcp->rport);
b->ip.hdr.tcp_seq = htonl(tcp_seq_num);
/*
@@ -339,143 +656,133 @@ int tcp_set_tcp_header(uchar *pkt, int dport, int sport, int payload_len,
* it is, then the u-boot tftp or nfs kernel netboot should be
* considered.
*/
- b->ip.hdr.tcp_win = htons(PKTBUFSRX * TCP_MSS >> TCP_SCALE);
+ b->ip.hdr.tcp_win = htons(tcp->rcv_wnd >> TCP_SCALE);
b->ip.hdr.tcp_xsum = 0;
b->ip.hdr.tcp_ugr = 0;
- b->ip.hdr.tcp_xsum = tcp_set_pseudo_header(pkt, net_ip, net_server_ip,
+ b->ip.hdr.tcp_xsum = tcp_set_pseudo_header(pkt, net_ip, tcp->rhost,
tcp_len, pkt_len);
- net_set_ip_header((uchar *)&b->ip, net_server_ip, net_ip,
+ net_set_ip_header((uchar *)&b->ip, tcp->rhost, net_ip,
pkt_len, IPPROTO_TCP);
return pkt_hdr_len;
}
+static void tcp_update_rcv_nxt(struct tcp_stream *tcp)
+{
+ if (tcp_seq_cmp(tcp->rcv_nxt, tcp->lost.hill[0].l) >= 0) {
+ tcp->rcv_nxt = tcp->lost.hill[0].r;
+
+ memmove(&tcp->lost.hill[0], &tcp->lost.hill[1],
+ (TCP_SACK_HILLS - 1) * sizeof(struct sack_edges));
+
+ tcp->lost.len -= TCP_OPT_LEN_8;
+ tcp->lost.hill[TCP_SACK_HILLS - 1].l = TCP_O_NOP;
+ tcp->lost.hill[TCP_SACK_HILLS - 1].r = TCP_O_NOP;
+ }
+}
+
/**
* tcp_hole() - Selective Acknowledgment (Essential for fast stream transfer)
+ * @tcp: tcp stream
* @tcp_seq_num: TCP sequence start number
* @len: the length of sequence numbers
*/
-void tcp_hole(u32 tcp_seq_num, u32 len)
-{
- u32 idx_sack, sack_in;
- u32 sack_end = TCP_SACK - 1;
- u32 hill = 0;
- enum pkt_state expect = PKT;
- u32 seq = tcp_seq_num - tcp_seq_init;
- u32 hol_l = tcp_ack_edge - tcp_seq_init;
- u32 hol_r = 0;
-
- /* Place new seq number in correct place in receive array */
- if (prev_len == 0)
- prev_len = len;
-
- idx_sack = sack_idx + ((tcp_seq_num - tcp_ack_edge) / prev_len);
- if (idx_sack < TCP_SACK) {
- edge_a[idx_sack].se.l = tcp_seq_num;
- edge_a[idx_sack].se.r = tcp_seq_num + len;
- edge_a[idx_sack].st = PKT;
-
- /*
- * The fin (last) packet is not the same length as data
- * packets, and if it's length is recorded and used for
- * array index calculation, calculation breaks.
- */
- if (prev_len < len)
- prev_len = len;
- }
-
- debug_cond(DEBUG_DEV_PKT,
- "TCP 1 seq %d, edg %d, len %d, sack_idx %d, sack_end %d\n",
- seq, hol_l, len, sack_idx, sack_end);
+void tcp_hole(struct tcp_stream *tcp, u32 tcp_seq_num, u32 len)
+{
+ int i, j, cnt, cnt_move;
- /* Right edge of contiguous stream, is the left edge of first hill */
- hol_l = tcp_seq_num - tcp_seq_init;
- hol_r = hol_l + len;
+ cnt = (tcp->lost.len - TCP_OPT_LEN_2) / TCP_OPT_LEN_8;
+ for (i = 0; i < cnt; i++) {
+ if (tcp_seq_cmp(tcp->lost.hill[i].r, tcp_seq_num) < 0)
+ continue;
+ if (tcp_seq_cmp(tcp->lost.hill[i].l, tcp_seq_num + len) > 0)
+ break;
- if (IS_ENABLED(CONFIG_PROT_TCP_SACK))
- tcp_lost.len = TCP_OPT_LEN_2;
+ if (tcp_seq_cmp(tcp->lost.hill[i].l, tcp_seq_num) > 0)
+ tcp->lost.hill[i].l = tcp_seq_num;
+ if (tcp_seq_cmp(tcp->lost.hill[i].l, tcp_seq_num) < 0) {
+ len += tcp_seq_num - tcp->lost.hill[i].l;
+ tcp_seq_num = tcp->lost.hill[i].l;
+ }
+ if (tcp_seq_cmp(tcp->lost.hill[i].r, tcp_seq_num + len) >= 0) {
+ tcp_update_rcv_nxt(tcp);
+ return;
+ }
- debug_cond(DEBUG_DEV_PKT,
- "TCP 1 in %d, seq %d, pkt_l %d, pkt_r %d, sack_idx %d, sack_end %d\n",
- idx_sack, seq, hol_l, hol_r, sack_idx, sack_end);
-
- for (sack_in = sack_idx; sack_in < sack_end && hill < TCP_SACK_HILLS;
- sack_in++) {
- switch (expect) {
- case NOPKT:
- switch (edge_a[sack_in].st) {
- case NOPKT:
- debug_cond(DEBUG_INT_STATE, "N");
- break;
- case PKT:
- debug_cond(DEBUG_INT_STATE, "n");
- if (IS_ENABLED(CONFIG_PROT_TCP_SACK)) {
- tcp_lost.hill[hill].l =
- edge_a[sack_in].se.l;
- tcp_lost.hill[hill].r =
- edge_a[sack_in].se.r;
- }
- expect = PKT;
- break;
- }
- break;
- case PKT:
- switch (edge_a[sack_in].st) {
- case NOPKT:
- debug_cond(DEBUG_INT_STATE, "p");
- if (sack_in > sack_idx &&
- hill < TCP_SACK_HILLS) {
- hill++;
- if (IS_ENABLED(CONFIG_PROT_TCP_SACK))
- tcp_lost.len += TCP_OPT_LEN_8;
- }
- expect = NOPKT;
- break;
- case PKT:
- debug_cond(DEBUG_INT_STATE, "P");
-
- if (tcp_ack_edge == edge_a[sack_in].se.l) {
- tcp_ack_edge = edge_a[sack_in].se.r;
- edge_a[sack_in].st = NOPKT;
- sack_idx++;
- } else {
- if (IS_ENABLED(CONFIG_PROT_TCP_SACK) &&
- hill < TCP_SACK_HILLS)
- tcp_lost.hill[hill].r =
- edge_a[sack_in].se.r;
- if (IS_ENABLED(CONFIG_PROT_TCP_SACK) &&
- sack_in == sack_end - 1)
- tcp_lost.hill[hill].r =
- edge_a[sack_in].se.r;
- }
+ /* check overlapping with next hills */
+ cnt_move = 0;
+ tcp->lost.hill[i].r = tcp_seq_num + len;
+ for (j = i + 1; j < cnt; j++) {
+ if (tcp_seq_cmp(tcp->lost.hill[j].l, tcp->lost.hill[i].r) > 0)
break;
+
+ tcp->lost.hill[i].r = tcp->lost.hill[j].r;
+ cnt_move++;
+ }
+
+ if (cnt_move > 0) {
+ if (cnt > i + cnt_move + 1)
+ memmove(&tcp->lost.hill[i + 1],
+ &tcp->lost.hill[i + cnt_move + 1],
+ cnt_move * sizeof(struct sack_edges));
+
+ cnt -= cnt_move;
+ tcp->lost.len = TCP_OPT_LEN_2 + cnt * TCP_OPT_LEN_8;
+ for (j = cnt; j < TCP_SACK_HILLS; j++) {
+ tcp->lost.hill[j].l = TCP_O_NOP;
+ tcp->lost.hill[j].r = TCP_O_NOP;
}
- break;
}
+
+ tcp_update_rcv_nxt(tcp);
+ return;
}
- debug_cond(DEBUG_INT_STATE, "\n");
- if (!IS_ENABLED(CONFIG_PROT_TCP_SACK) || tcp_lost.len <= TCP_OPT_LEN_2)
- sack_idx = 0;
-}
+
+ if (i == TCP_SACK_HILLS) {
+ tcp_update_rcv_nxt(tcp);
+ return;
+ }
+
+ if (cnt < TCP_SACK_HILLS) {
+ cnt_move = cnt - i;
+ cnt++;
+ } else {
+ cnt = TCP_SACK_HILLS;
+ cnt_move = TCP_SACK_HILLS - i;
+ }
+
+ if (cnt_move > 0)
+ memmove(&tcp->lost.hill[i + 1],
+ &tcp->lost.hill[i],
+ cnt_move * sizeof(struct sack_edges));
+
+ tcp->lost.hill[i].l = tcp_seq_num;
+ tcp->lost.hill[i].r = tcp_seq_num + len;
+ tcp->lost.len = TCP_OPT_LEN_2 + cnt * TCP_OPT_LEN_8;
+
+ tcp_update_rcv_nxt(tcp);
+};
/**
* tcp_parse_options() - parsing TCP options
+ * @tcp: tcp stream
* @o: pointer to the option field.
* @o_len: length of the option field.
*/
-void tcp_parse_options(uchar *o, int o_len)
+void tcp_parse_options(struct tcp_stream *tcp, uchar *o, int o_len)
{
struct tcp_t_opt *tsopt;
+ struct tcp_scale *wsopt;
uchar *p = o;
/*
* NOPs are options with a zero length, and thus are special.
* All other options have length fields.
*/
- for (p = o; p < (o + o_len); p = p + p[1]) {
+ for (p = o; p < (o + o_len); ) {
if (!p[1])
return; /* Finished processing options */
@@ -483,150 +790,365 @@ void tcp_parse_options(uchar *o, int o_len)
case TCP_O_END:
return;
case TCP_O_MSS:
- case TCP_O_SCL:
case TCP_P_SACK:
case TCP_V_SACK:
break;
+ case TCP_O_SCL:
+ wsopt = (struct tcp_scale *)p;
+ tcp->rmt_win_scale = wsopt->scale;
+ break;
case TCP_O_TS:
tsopt = (struct tcp_t_opt *)p;
- rmt_timestamp = tsopt->t_snd;
- return;
+ tcp->rmt_timestamp = tsopt->t_snd;
+ break;
}
/* Process optional NOPs */
if (p[0] == TCP_O_NOP)
p++;
+ else
+ p += p[1];
}
}
-static u8 tcp_state_machine(u8 tcp_flags, u32 tcp_seq_num, int payload_len)
+static int tcp_seg_in_wnd(struct tcp_stream *tcp,
+ u32 tcp_seq_num, int payload_len)
{
- u8 tcp_fin = tcp_flags & TCP_FIN;
- u8 tcp_syn = tcp_flags & TCP_SYN;
- u8 tcp_rst = tcp_flags & TCP_RST;
- u8 tcp_push = tcp_flags & TCP_PUSH;
- u8 tcp_ack = tcp_flags & TCP_ACK;
- u8 action = TCP_DATA;
- int i;
+ if (!payload_len && !tcp->rcv_wnd) {
+ if (tcp_seq_num == tcp->rcv_nxt)
+ return 1;
+ }
+ if (!payload_len && tcp->rcv_wnd > 0) {
+ if (tcp_seq_cmp(tcp->rcv_nxt, tcp_seq_num) <= 0 &&
+ tcp_seq_cmp(tcp_seq_num, tcp->rcv_nxt + tcp->rcv_wnd) < 0)
+ return 1;
+ }
+ if (payload_len > 0 && tcp->rcv_wnd > 0) {
+ if (tcp_seq_cmp(tcp->rcv_nxt, tcp_seq_num) <= 0 &&
+ tcp_seq_cmp(tcp_seq_num, tcp->rcv_nxt + tcp->rcv_wnd) < 0)
+ return 1;
+ tcp_seq_num += payload_len - 1;
+ if (tcp_seq_cmp(tcp->rcv_nxt, tcp_seq_num) <= 0 &&
+ tcp_seq_cmp(tcp_seq_num, tcp->rcv_nxt + tcp->rcv_wnd) < 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int tcp_rx_check_ack_num(struct tcp_stream *tcp, u32 tcp_seq_num,
+ u32 tcp_ack_num, u32 tcp_win_size)
+{
+ u32 old_offs, new_offs;
+ u8 action;
+
+ switch (tcp->state) {
+ case TCP_SYN_RECEIVED:
+ if (tcp_seq_cmp(tcp->snd_una, tcp_ack_num) >= 0 ||
+ tcp_seq_cmp(tcp_ack_num, tcp->snd_nxt) > 0) {
+ // segment acknowledgment is not acceptable
+ tcp_send_packet(tcp, TCP_RST, tcp_ack_num, 0, 0);
+ return TCP_PACKET_DROP;
+ }
+
+ tcp_stream_set_state(tcp, TCP_ESTABLISHED);
+ tcp->snd_wnd = tcp_win_size;
+ tcp->snd_wl1 = tcp_seq_num;
+ tcp->snd_wl2 = tcp_ack_num;
+
+ if (tcp->on_established)
+ tcp->on_established(tcp);
+
+ fallthrough;
+
+ case TCP_ESTABLISHED:
+ case TCP_FIN_WAIT_1:
+ case TCP_FIN_WAIT_2:
+ case TCP_CLOSE_WAIT:
+ case TCP_CLOSING:
+ if (tcp_seq_cmp(tcp_ack_num, tcp->snd_nxt) > 0) {
+ // ACK acks something not yet sent
+ action = tcp_stream_fin_needed(tcp, tcp->snd_una) | TCP_ACK;
+ tcp_send_packet(tcp, action, tcp->snd_una, tcp->rcv_nxt, 0);
+ return TCP_PACKET_DROP;
+ }
+
+ if (tcp_seq_cmp(tcp->snd_una, tcp_ack_num) < 0) {
+ old_offs = tcp_stream_tx_offs(tcp);
+ tcp->snd_una = tcp_ack_num;
+ new_offs = tcp_stream_tx_offs(tcp);
+ if (tcp->time_handler &&
+ tcp_seq_cmp(tcp->snd_una, tcp->retry_seq_num) > 0) {
+ tcp_stream_set_time_handler(tcp, 0, NULL);
+ }
+ if (tcp->on_snd_una_update &&
+ old_offs != new_offs)
+ tcp->on_snd_una_update(tcp, new_offs);
+ }
+
+ if (tcp_seq_cmp(tcp->snd_una, tcp_ack_num) <= 0) {
+ if (tcp_seq_cmp(tcp->snd_wl1, tcp_seq_num) < 0 ||
+ (tcp->snd_wl1 == tcp_seq_num &&
+ tcp_seq_cmp(tcp->snd_wl2, tcp_seq_num) <= 0)) {
+ tcp->snd_wnd = tcp_win_size;
+ tcp->snd_wl1 = tcp_seq_num;
+ tcp->snd_wl2 = tcp_ack_num;
+ }
+ }
+
+ if (tcp->state == TCP_FIN_WAIT_1) {
+ if (tcp->snd_una == tcp->snd_nxt)
+ tcp_stream_set_state(tcp, TCP_FIN_WAIT_2);
+ }
+
+ if (tcp->state == TCP_CLOSING) {
+ if (tcp->snd_una == tcp->snd_nxt)
+ tcp_stream_set_state(tcp, TCP_CLOSED);
+ }
+ return TCP_PACKET_OK;
+
+ case TCP_LAST_ACK:
+ if (tcp_ack_num == tcp->snd_nxt)
+ tcp_stream_set_state(tcp, TCP_CLOSED);
+ return TCP_PACKET_OK;
+
+ default:
+ return TCP_PACKET_DROP;
+ }
+}
+static int tcp_rx_user_data(struct tcp_stream *tcp, u32 tcp_seq_num,
+ char *buf, int len)
+{
+ int tmp_len;
+ u32 buf_offs, old_offs, new_offs;
+ u8 action;
+
+ if (!len)
+ return TCP_PACKET_OK;
+
+ switch (tcp->state) {
+ case TCP_ESTABLISHED:
+ case TCP_FIN_WAIT_1:
+ case TCP_FIN_WAIT_2:
+ break;
+ default:
+ return TCP_PACKET_DROP;
+ }
+
+ tmp_len = len;
+ old_offs = tcp_stream_rx_offs(tcp);
+ buf_offs = tcp_seq_num - tcp->irs - 1;
+ if (tcp->rx) {
+ tmp_len = tcp->rx(tcp, buf_offs, buf, len);
+ if (tmp_len < 0) {
+ puts("\nTCP: receive failure\n");
+ tcp_send_packet(tcp, TCP_RST, tcp->snd_una,
+ tcp->rcv_nxt, 0);
+ tcp_stream_set_status(tcp, TCP_ERR_IO);
+ tcp_stream_set_state(tcp, TCP_CLOSED);
+ tcp_stream_destroy(tcp);
+ return TCP_PACKET_DROP;
+ }
+ }
+ if (tmp_len)
+ tcp_hole(tcp, tcp_seq_num, tmp_len);
+
+ new_offs = tcp_stream_rx_offs(tcp);
+ if (tcp->on_rcv_nxt_update && old_offs != new_offs)
+ tcp->on_rcv_nxt_update(tcp, new_offs);
+
+ action = tcp_stream_fin_needed(tcp, tcp->snd_una) | TCP_ACK;
+ tcp_send_packet(tcp, action, tcp->snd_una, tcp->rcv_nxt, 0);
+
+ return TCP_PACKET_OK;
+}
+
+void tcp_rx_state_machine(struct tcp_stream *tcp,
+ union tcp_build_pkt *b, unsigned int pkt_len)
+{
+ int tcp_len = pkt_len - IP_HDR_SIZE;
+ u32 tcp_seq_num, tcp_ack_num, tcp_win_size;
+ int tcp_hdr_len, payload_len;
+ u8 tcp_flags, action;
+
+ tcp_hdr_len = GET_TCP_HDR_LEN_IN_BYTES(b->ip.hdr.tcp_hlen);
+ payload_len = tcp_len - tcp_hdr_len;
+
+ if (tcp_hdr_len > TCP_HDR_SIZE)
+ tcp_parse_options(tcp, (uchar *)b + IP_TCP_HDR_SIZE,
+ tcp_hdr_len - TCP_HDR_SIZE);
/*
- * tcp_flags are examined to determine TX action in a given state
- * tcp_push is interpreted to mean "inform the app"
- * urg, ece, cer and nonce flags are not supported.
- *
- * exe and crw are use to signal and confirm knowledge of congestion.
- * This TCP only sends a file request and acks. If it generates
- * congestion, the network is broken.
+ * Incoming sequence and ack numbers are server's view of the numbers.
+ * The app must swap the numbers when responding.
*/
- debug_cond(DEBUG_INT_STATE, "TCP STATE ENTRY %x\n", action);
- if (tcp_rst) {
- action = TCP_DATA;
- current_tcp_state = TCP_CLOSED;
- net_set_state(NETLOOP_FAIL);
- debug_cond(DEBUG_INT_STATE, "TCP Reset %x\n", tcp_flags);
- return TCP_RST;
- }
+ tcp_seq_num = ntohl(b->ip.hdr.tcp_seq);
+ tcp_ack_num = ntohl(b->ip.hdr.tcp_ack);
+ tcp_win_size = ntohs(b->ip.hdr.tcp_win) << tcp->rmt_win_scale;
- switch (current_tcp_state) {
+ tcp_flags = b->ip.hdr.tcp_flags;
+
+// printf("pkt: seq=%d, ack=%d, flags=%x, len=%d\n",
+// tcp_seq_num - tcp->irs, tcp_ack_num - tcp->iss, tcp_flags, pkt_len);
+// printf("tcp: rcv_nxt=%d, snd_una=%d, snd_nxt=%d\n\n",
+// tcp->rcv_nxt - tcp->irs, tcp->snd_una - tcp->iss, tcp->snd_nxt - tcp->iss);
+
+ switch (tcp->state) {
case TCP_CLOSED:
- debug_cond(DEBUG_INT_STATE, "TCP CLOSED %x\n", tcp_flags);
- if (tcp_syn) {
- action = TCP_SYN | TCP_ACK;
- tcp_seq_init = tcp_seq_num;
- tcp_ack_edge = tcp_seq_num + 1;
- current_tcp_state = TCP_SYN_RECEIVED;
- } else if (tcp_ack || tcp_fin) {
- action = TCP_DATA;
+ if (tcp_flags & TCP_RST)
+ return;
+
+ if (tcp_flags & TCP_ACK) {
+ tcp_send_packet(tcp, TCP_RST, tcp_ack_num, 0, 0);
+ return;
}
- break;
- case TCP_SYN_RECEIVED:
+
+ if (!(tcp_flags & TCP_SYN))
+ return;
+
+ tcp->irs = tcp_seq_num;
+ tcp->rcv_nxt = tcp->irs + 1;
+
+ tcp->iss = tcp_get_start_seq();
+ tcp->snd_una = tcp->iss;
+ tcp->snd_nxt = tcp->iss + 1;
+ tcp->snd_wnd = tcp_win_size;
+
+ tcp_stream_restart_rx_timer(tcp);
+
+ tcp_stream_set_state(tcp, TCP_SYN_RECEIVED);
+ tcp_send_packet_with_retry(tcp, TCP_SYN | TCP_ACK,
+ tcp->iss, 0, 0);
+ return;
+
case TCP_SYN_SENT:
- debug_cond(DEBUG_INT_STATE, "TCP_SYN_SENT | TCP_SYN_RECEIVED %x, %u\n",
- tcp_flags, tcp_seq_num);
- if (tcp_fin) {
- action = action | TCP_PUSH;
- current_tcp_state = TCP_CLOSE_WAIT;
- } else if (tcp_ack || (tcp_syn && tcp_ack)) {
- action |= TCP_ACK;
- tcp_seq_init = tcp_seq_num;
- tcp_ack_edge = tcp_seq_num + 1;
- sack_idx = 0;
- edge_a[sack_idx].se.l = tcp_ack_edge;
- edge_a[sack_idx].se.r = tcp_ack_edge;
- prev_len = 0;
- current_tcp_state = TCP_ESTABLISHED;
- for (i = 0; i < TCP_SACK; i++)
- edge_a[i].st = NOPKT;
-
- if (tcp_syn && tcp_ack)
- action |= TCP_PUSH;
- } else {
- action = TCP_DATA;
+ if (!(tcp_flags & TCP_ACK))
+ return;
+
+ if (tcp_seq_cmp(tcp_ack_num, tcp->iss) <= 0 ||
+ tcp_seq_cmp(tcp_ack_num, tcp->snd_nxt) > 0) {
+ if (!(tcp_flags & TCP_RST))
+ tcp_send_packet(tcp, TCP_RST, tcp_ack_num, 0, 0);
+ return;
}
- break;
+
+ if (tcp_flags & TCP_RST) {
+ tcp_stream_set_status(tcp, TCP_ERR_RST);
+ tcp_stream_set_state(tcp, TCP_CLOSED);
+ return;
+ }
+
+ if (!(tcp_flags & TCP_SYN))
+ return;
+
+ /* stop retransmit of SYN */
+ tcp_stream_set_time_handler(tcp, 0, NULL);
+
+ tcp->irs = tcp_seq_num;
+ tcp->rcv_nxt = tcp->irs + 1;
+ tcp->snd_una = tcp_ack_num;
+
+ tcp_stream_restart_rx_timer(tcp);
+
+ /* our SYN has been ACKed */
+ tcp_stream_set_state(tcp, TCP_ESTABLISHED);
+
+ if (tcp->on_established)
+ tcp->on_established(tcp);
+
+ action = tcp_stream_fin_needed(tcp, tcp->snd_una) | TCP_ACK;
+ tcp_send_packet(tcp, action, tcp->snd_una, tcp->rcv_nxt, 0);
+ tcp_rx_user_data(tcp, tcp_seq_num,
+ ((char *)b) + pkt_len - payload_len,
+ payload_len);
+ return;
+
+ case TCP_SYN_RECEIVED:
case TCP_ESTABLISHED:
- debug_cond(DEBUG_INT_STATE, "TCP_ESTABLISHED %x\n", tcp_flags);
- if (payload_len > 0) {
- tcp_hole(tcp_seq_num, payload_len);
- tcp_fin = TCP_DATA; /* cause standalone FIN */
+ case TCP_FIN_WAIT_1:
+ case TCP_FIN_WAIT_2:
+ case TCP_CLOSE_WAIT:
+ case TCP_CLOSING:
+ case TCP_LAST_ACK:
+ if (!tcp_seg_in_wnd(tcp, tcp_seq_num, payload_len)) {
+ if (tcp_flags & TCP_RST)
+ return;
+ action = tcp_stream_fin_needed(tcp, tcp->snd_una) | TCP_ACK;
+ tcp_send_packet(tcp, action, tcp->snd_una, tcp->rcv_nxt, 0);
+ return;
}
- if ((tcp_fin) &&
- (!IS_ENABLED(CONFIG_PROT_TCP_SACK) ||
- tcp_lost.len <= TCP_OPT_LEN_2)) {
- action = action | TCP_FIN | TCP_PUSH | TCP_ACK;
- current_tcp_state = TCP_CLOSE_WAIT;
- } else if (tcp_ack) {
- action = TCP_DATA;
+ tcp_stream_restart_rx_timer(tcp);
+
+ if (tcp_flags & TCP_RST) {
+ tcp_stream_set_status(tcp, TCP_ERR_RST);
+ tcp_stream_set_state(tcp, TCP_CLOSED);
+ return;
}
- if (tcp_syn)
- action = TCP_ACK + TCP_RST;
- else if (tcp_push)
- action = action | TCP_PUSH;
- break;
- case TCP_CLOSE_WAIT:
- debug_cond(DEBUG_INT_STATE, "TCP_CLOSE_WAIT (%x)\n", tcp_flags);
- action = TCP_DATA;
- break;
- case TCP_FIN_WAIT_2:
- debug_cond(DEBUG_INT_STATE, "TCP_FIN_WAIT_2 (%x)\n", tcp_flags);
- if (tcp_ack) {
- action = TCP_PUSH | TCP_ACK;
- current_tcp_state = TCP_CLOSED;
- puts("\n");
- } else if (tcp_syn) {
- action = TCP_DATA;
- } else if (tcp_fin) {
- action = TCP_DATA;
+ if (tcp_flags & TCP_SYN) {
+ tcp_send_packet(tcp, TCP_RST, tcp_ack_num, 0, 0);
+ tcp_stream_set_status(tcp, TCP_ERR_RST);
+ tcp_stream_set_state(tcp, TCP_CLOSED);
+ return;
}
- break;
- case TCP_FIN_WAIT_1:
- debug_cond(DEBUG_INT_STATE, "TCP_FIN_WAIT_1 (%x)\n", tcp_flags);
- if (tcp_fin) {
- tcp_ack_edge++;
- action = TCP_ACK | TCP_FIN;
- current_tcp_state = TCP_FIN_WAIT_2;
+
+ if (!(tcp_flags & TCP_ACK))
+ return;
+
+ if (tcp_rx_check_ack_num(tcp, tcp_seq_num, tcp_ack_num,
+ tcp_win_size) == TCP_PACKET_DROP) {
+ return;
}
- if (tcp_syn)
- action = TCP_RST;
- if (tcp_ack)
- current_tcp_state = TCP_CLOSED;
- break;
- case TCP_CLOSING:
- debug_cond(DEBUG_INT_STATE, "TCP_CLOSING (%x)\n", tcp_flags);
- if (tcp_ack) {
- action = TCP_PUSH;
- current_tcp_state = TCP_CLOSED;
- puts("\n");
- } else if (tcp_syn) {
- action = TCP_RST;
- } else if (tcp_fin) {
- action = TCP_DATA;
+
+ if (tcp_rx_user_data(tcp, tcp_seq_num,
+ ((char *)b) + pkt_len - payload_len,
+ payload_len) == TCP_PACKET_DROP) {
+ return;
+ }
+
+ if (tcp_flags & TCP_FIN) {
+ tcp->fin_rx = 1;
+ tcp->fin_rx_seq = tcp_seq_num + payload_len + 1;
+ tcp_hole(tcp, tcp_seq_num + payload_len, 1);
+ action = tcp_stream_fin_needed(tcp, tcp->snd_una) | TCP_ACK;
+ tcp_send_packet(tcp, action, tcp->snd_una, tcp->rcv_nxt, 0);
+ }
+
+ if (tcp->fin_rx &&
+ tcp->fin_rx_seq == tcp->rcv_nxt) {
+ /* all rx data were processed */
+ switch (tcp->state) {
+ case TCP_ESTABLISHED:
+ tcp_stream_set_state(tcp, TCP_LAST_ACK);
+ tcp_send_packet_with_retry(tcp, TCP_ACK | TCP_FIN,
+ tcp->snd_nxt, 0, 0);
+ tcp->snd_nxt++;
+ break;
+
+ case TCP_FIN_WAIT_1:
+ if (tcp_ack_num == tcp->snd_nxt)
+ tcp_stream_set_state(tcp, TCP_CLOSED);
+ else
+ tcp_stream_set_state(tcp, TCP_CLOSING);
+ break;
+
+ case TCP_FIN_WAIT_2:
+ tcp_stream_set_state(tcp, TCP_CLOSED);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (tcp->state == TCP_FIN_WAIT_1 &&
+ tcp_stream_fin_needed(tcp, tcp->snd_una)) {
+ /* all tx data were acknowledged */
+ tcp_send_packet_with_retry(tcp, TCP_ACK | TCP_FIN,
+ tcp->snd_una, 0, 0);
}
- break;
}
- return action;
}
/**
@@ -638,22 +1160,27 @@ void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len)
{
int tcp_len = pkt_len - IP_HDR_SIZE;
u16 tcp_rx_xsum = b->ip.hdr.ip_sum;
- u8 tcp_action = TCP_DATA;
- u32 tcp_seq_num, tcp_ack_num;
- int tcp_hdr_len, payload_len;
+ struct tcp_stream *tcp;
+ struct in_addr src;
/* Verify IP header */
debug_cond(DEBUG_DEV_PKT,
"TCP RX in RX Sum (to=%pI4, from=%pI4, len=%d)\n",
&b->ip.hdr.ip_src, &b->ip.hdr.ip_dst, pkt_len);
- b->ip.hdr.ip_src = net_server_ip;
+ /*
+ * src IP address will be destroyed by TCP checksum verification
+ * algorithm (see tcp_set_pseudo_header()), so remember it before
+ * it was garbaged.
+ */
+ src.s_addr = b->ip.hdr.ip_src.s_addr;
+
b->ip.hdr.ip_dst = net_ip;
b->ip.hdr.ip_sum = 0;
if (tcp_rx_xsum != compute_ip_checksum(b, IP_HDR_SIZE)) {
debug_cond(DEBUG_DEV_PKT,
"TCP RX IP xSum Error (%pI4, =%pI4, len=%d)\n",
- &net_ip, &net_server_ip, pkt_len);
+ &net_ip, &src, pkt_len);
return;
}
@@ -665,54 +1192,70 @@ void rxhand_tcp_f(union tcp_build_pkt *b, unsigned int pkt_len)
pkt_len)) {
debug_cond(DEBUG_DEV_PKT,
"TCP RX TCP xSum Error (%pI4, %pI4, len=%d)\n",
- &net_ip, &net_server_ip, tcp_len);
+ &net_ip, &src, tcp_len);
return;
}
- tcp_hdr_len = GET_TCP_HDR_LEN_IN_BYTES(b->ip.hdr.tcp_hlen);
- payload_len = tcp_len - tcp_hdr_len;
+ tcp = tcp_stream_get(b->ip.hdr.tcp_flags & TCP_SYN,
+ src,
+ ntohs(b->ip.hdr.tcp_src),
+ ntohs(b->ip.hdr.tcp_dst));
+ if (!tcp)
+ return;
- if (tcp_hdr_len > TCP_HDR_SIZE)
- tcp_parse_options((uchar *)b + IP_TCP_HDR_SIZE,
- tcp_hdr_len - TCP_HDR_SIZE);
- /*
- * Incoming sequence and ack numbers are server's view of the numbers.
- * The app must swap the numbers when responding.
- */
- tcp_seq_num = ntohl(b->ip.hdr.tcp_seq);
- tcp_ack_num = ntohl(b->ip.hdr.tcp_ack);
+ tcp->rx_packets++;
+ tcp_rx_state_machine(tcp, b, pkt_len);
+ tcp_stream_put(tcp);
+}
- /* Packets are not ordered. Send to app as received. */
- tcp_action = tcp_state_machine(b->ip.hdr.tcp_flags,
- tcp_seq_num, payload_len);
+struct tcp_stream *tcp_stream_connect(struct in_addr rhost, u16 rport)
+{
+ struct tcp_stream *tcp;
- tcp_activity_count++;
- if (tcp_activity_count > TCP_ACTIVITY) {
- puts("| ");
- tcp_activity_count = 0;
- }
+ tcp = tcp_stream_add(rhost, rport, random_port());
+ if (!tcp)
+ return NULL;
- if ((tcp_action & TCP_PUSH) || payload_len > 0) {
- debug_cond(DEBUG_DEV_PKT,
- "TCP Notify (action=%x, Seq=%u,Ack=%u,Pay%d)\n",
- tcp_action, tcp_seq_num, tcp_ack_num, payload_len);
+ tcp->iss = tcp_get_start_seq();
+ tcp->snd_una = tcp->iss;
+ tcp->snd_nxt = tcp->iss + 1;
- (*tcp_packet_handler) ((uchar *)b + pkt_len - payload_len, b->ip.hdr.tcp_dst,
- b->ip.hdr.ip_src, b->ip.hdr.tcp_src, tcp_seq_num,
- tcp_ack_num, tcp_action, payload_len);
+ tcp_stream_set_state(tcp, TCP_SYN_SENT);
+ tcp_send_packet_with_retry(tcp, TCP_SYN, tcp->snd_una, 0, 0);
- } else if (tcp_action != TCP_DATA) {
- debug_cond(DEBUG_DEV_PKT,
- "TCP Action (action=%x,Seq=%u,Ack=%u,Pay=%d)\n",
- tcp_action, tcp_ack_num, tcp_ack_edge, payload_len);
-
- /*
- * Warning: Incoming Ack & Seq sequence numbers are transposed
- * here to outgoing Seq & Ack sequence numbers
- */
- net_send_tcp_packet(0, ntohs(b->ip.hdr.tcp_src),
- ntohs(b->ip.hdr.tcp_dst),
- (tcp_action & (~TCP_PUSH)),
- tcp_ack_num, tcp_ack_edge);
+ return tcp;
+}
+
+void tcp_stream_reset(struct tcp_stream *tcp)
+{
+ if (tcp->state == TCP_CLOSED)
+ return;
+
+ tcp_stream_set_time_handler(tcp, 0, NULL);
+ tcp_send_packet(tcp, TCP_RST, tcp->snd_una, 0, 0);
+ tcp_stream_set_status(tcp, TCP_ERR_RST);
+ tcp_stream_set_state(tcp, TCP_CLOSED);
+}
+
+void tcp_stream_close(struct tcp_stream *tcp)
+{
+ switch (tcp->state) {
+ case TCP_SYN_SENT:
+ tcp_stream_reset(tcp);
+ break;
+ case TCP_SYN_RECEIVED:
+ case TCP_ESTABLISHED:
+ tcp->fin_tx = 1;
+ tcp->fin_tx_seq = tcp->snd_nxt;
+ if (tcp_stream_fin_needed(tcp, tcp->snd_una)) {
+ /* all tx data were acknowledged */
+ tcp_send_packet_with_retry(tcp, TCP_ACK | TCP_FIN,
+ tcp->snd_una, 0, 0);
+ }
+ tcp_stream_set_state(tcp, TCP_FIN_WAIT_1);
+ tcp->snd_nxt++;
+ break;
+ default:
+ break;
}
}
diff --git a/net/wget.c b/net/wget.c
index 361817ace65..0b082c61947 100644
--- a/net/wget.c
+++ b/net/wget.c
@@ -22,47 +22,26 @@ DECLARE_GLOBAL_DATA_PTR;
/* The default, change with environment variable 'httpdstp' */
#define SERVER_PORT 80
-static const char bootfile1[] = "GET ";
-static const char bootfile3[] = " HTTP/1.0\r\n\r\n";
-static const char http_eom[] = "\r\n\r\n";
-static const char http_ok[] = "200";
-static const char linefeed[] = "\r\n";
-static struct in_addr web_server_ip;
-static int our_port;
-static int wget_timeout_count;
+#define HASHES_PER_LINE 65
-struct pkt_qd {
- uchar *pkt;
- unsigned int tcp_seq_num;
- unsigned int len;
-};
+#define HTTP_MAX_HDR_LEN 2048
-/*
- * This is a control structure for out of order packets received.
- * The actual packet bufers are in the kernel space, and are
- * expected to be overwritten by the downloaded image.
- */
-#define PKTQ_SZ (PKTBUFSRX / 4)
-static struct pkt_qd pkt_q[PKTQ_SZ];
-static int pkt_q_idx;
-static unsigned int packets;
-
-static unsigned int initial_data_seq_num;
-static unsigned int next_data_seq_num;
+#define HTTP_STATUS_BAD 0
+#define HTTP_STATUS_OK 200
-static enum wget_state current_wget_state;
+static const char http_proto[] = "HTTP/1.0";
+static const char http_eom[] = "\r\n\r\n";
+static const char content_len[] = "Content-Length:";
+static const char linefeed[] = "\r\n";
+static struct in_addr web_server_ip;
+static unsigned int server_port;
+static unsigned long content_length;
+static u32 http_hdr_size, max_rx_pos;
+static int wget_tsize_num_hash;
static char *image_url;
-static unsigned int wget_timeout = WGET_TIMEOUT;
-
static enum net_loop_state wget_loop_state;
-/* Timeout retry parameters */
-static u8 retry_action; /* actions for TCP retry */
-static unsigned int retry_tcp_ack_num; /* TCP retry acknowledge number*/
-static unsigned int retry_tcp_seq_num; /* TCP retry sequence number */
-static int retry_len; /* TCP retry length */
-
/**
* store_block() - store block in memory
* @src: source of data
@@ -72,10 +51,9 @@ static int retry_len; /* TCP retry length */
static inline int store_block(uchar *src, unsigned int offset, unsigned int len)
{
ulong store_addr = image_load_addr + offset;
- ulong newsize = offset + len;
uchar *ptr;
- if (CONFIG_IS_ENABLED(LMB)) {
+ if (CONFIG_IS_ENABLED(LMB) && wget_info->set_bootdev) {
if (store_addr < image_load_addr ||
lmb_read_check(store_addr, len)) {
printf("\nwget error: ");
@@ -88,330 +66,221 @@ static inline int store_block(uchar *src, unsigned int offset, unsigned int len)
memcpy(ptr, src, len);
unmap_sysmem(ptr);
- if (net_boot_file_size < (offset + len))
- net_boot_file_size = newsize;
-
return 0;
}
-/**
- * wget_send_stored() - wget response dispatcher
- *
- * WARNING, This, and only this, is the place in wget.c where
- * SEQUENCE NUMBERS are swapped between incoming (RX)
- * and outgoing (TX).
- * Procedure wget_handler() is correct for RX traffic.
- */
-static void wget_send_stored(void)
+static void show_block_marker(u32 packets)
{
- u8 action = retry_action;
- int len = retry_len;
- unsigned int tcp_ack_num = retry_tcp_seq_num + (len == 0 ? 1 : len);
- unsigned int tcp_seq_num = retry_tcp_ack_num;
- unsigned int server_port;
- uchar *ptr, *offset;
+ int cnt;
- server_port = env_get_ulong("httpdstp", 10, SERVER_PORT) & 0xffff;
+ if (content_length != -1) {
+ if (net_boot_file_size > content_length)
+ content_length = net_boot_file_size;
- switch (current_wget_state) {
- case WGET_CLOSED:
- debug_cond(DEBUG_WGET, "wget: send SYN\n");
- current_wget_state = WGET_CONNECTING;
- net_send_tcp_packet(0, server_port, our_port, action,
- tcp_seq_num, tcp_ack_num);
- packets = 0;
- break;
- case WGET_CONNECTING:
- pkt_q_idx = 0;
- net_send_tcp_packet(0, server_port, our_port, action,
- tcp_seq_num, tcp_ack_num);
-
- ptr = net_tx_packet + net_eth_hdr_size() +
- IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2;
- offset = ptr;
-
- memcpy(offset, &bootfile1, strlen(bootfile1));
- offset += strlen(bootfile1);
-
- memcpy(offset, image_url, strlen(image_url));
- offset += strlen(image_url);
-
- memcpy(offset, &bootfile3, strlen(bootfile3));
- offset += strlen(bootfile3);
- net_send_tcp_packet((offset - ptr), server_port, our_port,
- TCP_PUSH, tcp_seq_num, tcp_ack_num);
- current_wget_state = WGET_CONNECTED;
- break;
- case WGET_CONNECTED:
- case WGET_TRANSFERRING:
- case WGET_TRANSFERRED:
- net_send_tcp_packet(0, server_port, our_port, action,
- tcp_seq_num, tcp_ack_num);
- break;
+ cnt = net_boot_file_size * 50 / content_length;
+ while (wget_tsize_num_hash < cnt) {
+ putc('#');
+ wget_tsize_num_hash++;
+ }
+ } else {
+ if ((packets % 10) == 0)
+ putc('#');
+ else if (((packets + 1) % (10 * HASHES_PER_LINE)) == 0)
+ puts("\n");
}
}
-static void wget_send(u8 action, unsigned int tcp_seq_num,
- unsigned int tcp_ack_num, int len)
+static void tcp_stream_on_closed(struct tcp_stream *tcp)
{
- retry_action = action;
- retry_tcp_ack_num = tcp_ack_num;
- retry_tcp_seq_num = tcp_seq_num;
- retry_len = len;
+ if (tcp->status != TCP_ERR_OK)
+ wget_loop_state = NETLOOP_FAIL;
+
+ net_set_state(wget_loop_state);
+ if (wget_loop_state != NETLOOP_SUCCESS) {
+ net_boot_file_size = 0;
+ if (wget_info->status_code == HTTP_STATUS_OK) {
+ wget_info->status_code = HTTP_STATUS_BAD;
+ wget_info->hdr_cont_len = 0;
+ if (wget_info->headers)
+ wget_info->headers[0] = 0;
+ }
+ printf("\nwget: Transfer Fail, TCP status - %d\n", tcp->status);
+ return;
+ }
- wget_send_stored();
+ printf("\nPackets received %d, Transfer Successful\n", tcp->rx_packets);
+ wget_info->file_size = net_boot_file_size;
+ if (wget_info->method == WGET_HTTP_METHOD_GET && wget_info->set_bootdev) {
+ efi_set_bootdev("Http", NULL, image_url,
+ map_sysmem(image_load_addr, 0),
+ net_boot_file_size);
+ env_set_hex("filesize", net_boot_file_size);
+ }
}
-void wget_fail(char *error_message, unsigned int tcp_seq_num,
- unsigned int tcp_ack_num, u8 action)
+static void tcp_stream_on_rcv_nxt_update(struct tcp_stream *tcp, u32 rx_bytes)
{
- printf("wget: Transfer Fail - %s\n", error_message);
- net_set_timeout_handler(0, NULL);
- wget_send(action, tcp_seq_num, tcp_ack_num, 0);
-}
+ char *pos, *tail;
+ uchar saved, *ptr;
+ int reply_len;
+
+ if (http_hdr_size) {
+ net_boot_file_size = rx_bytes - http_hdr_size;
+ show_block_marker(tcp->rx_packets);
+ return;
+ }
-/*
- * Interfaces of U-BOOT
- */
-static void wget_timeout_handler(void)
-{
- if (++wget_timeout_count > WGET_RETRY_COUNT) {
- puts("\nRetry count exceeded; starting again\n");
- wget_send(TCP_RST, 0, 0, 0);
- net_start_again();
- } else {
- puts("T ");
- net_set_timeout_handler(wget_timeout +
- WGET_TIMEOUT * wget_timeout_count,
- wget_timeout_handler);
- wget_send_stored();
+ ptr = map_sysmem(image_load_addr, rx_bytes + 1);
+
+ saved = ptr[rx_bytes];
+ ptr[rx_bytes] = '\0';
+ pos = strstr((char *)ptr, http_eom);
+ ptr[rx_bytes] = saved;
+
+ if (!pos) {
+ if (rx_bytes < HTTP_MAX_HDR_LEN &&
+ tcp->state == TCP_ESTABLISHED)
+ goto end;
+
+ printf("ERROR: misssed HTTP header\n");
+ tcp_stream_close(tcp);
+ goto end;
}
-}
-#define PKT_QUEUE_OFFSET 0x20000
-#define PKT_QUEUE_PACKET_SIZE 0x800
+ http_hdr_size = pos - (char *)ptr + strlen(http_eom);
+ *pos = '\0';
-static void wget_connected(uchar *pkt, unsigned int tcp_seq_num,
- u8 action, unsigned int tcp_ack_num, unsigned int len)
-{
- uchar *pkt_in_q;
- char *pos;
- int hlen, i;
- uchar *ptr1;
+ if (wget_info->headers && http_hdr_size < MAX_HTTP_HEADERS_SIZE)
+ strcpy(wget_info->headers, ptr);
- pkt[len] = '\0';
- pos = strstr((char *)pkt, http_eom);
+ /* check for HTTP proto */
+ if (strncasecmp((char *)ptr, "HTTP/", 5)) {
+ debug_cond(DEBUG_WGET, "wget: Connected Bad Xfer "
+ "(no HTTP Status Line found)\n");
+ tcp_stream_close(tcp);
+ goto end;
+ }
- if (!pos) {
- debug_cond(DEBUG_WGET,
- "wget: Connected, data before Header %p\n", pkt);
- pkt_in_q = (void *)image_load_addr + PKT_QUEUE_OFFSET +
- (pkt_q_idx * PKT_QUEUE_PACKET_SIZE);
+ /* get HTTP reply len */
+ pos = strstr((char *)ptr, linefeed);
+ if (pos)
+ reply_len = pos - (char *)ptr;
+ else
+ reply_len = http_hdr_size - strlen(http_eom);
+
+ pos = strchr((char *)ptr, ' ');
+ if (!pos || pos - (char *)ptr > reply_len) {
+ debug_cond(DEBUG_WGET, "wget: Connected Bad Xfer "
+ "(no HTTP Status Code found)\n");
+ tcp_stream_close(tcp);
+ goto end;
+ }
- ptr1 = map_sysmem((ulong)pkt_in_q, len);
- memcpy(ptr1, pkt, len);
- unmap_sysmem(ptr1);
+ wget_info->status_code = (u32)simple_strtoul(pos + 1, &tail, 10);
+ if (tail == pos + 1 || *tail != ' ') {
+ debug_cond(DEBUG_WGET, "wget: Connected Bad Xfer "
+ "(bad HTTP Status Code)\n");
+ tcp_stream_close(tcp);
+ goto end;
+ }
- pkt_q[pkt_q_idx].pkt = pkt_in_q;
- pkt_q[pkt_q_idx].tcp_seq_num = tcp_seq_num;
- pkt_q[pkt_q_idx].len = len;
- pkt_q_idx++;
+ debug_cond(DEBUG_WGET,
+ "wget: HTTP Status Code %d\n", wget_info->status_code);
- if (pkt_q_idx >= PKTQ_SZ) {
- printf("wget: Fatal error, queue overrun!\n");
- net_set_state(NETLOOP_FAIL);
+ if (wget_info->status_code != HTTP_STATUS_OK) {
+ debug_cond(DEBUG_WGET, "wget: Connected Bad Xfer\n");
+ tcp_stream_close(tcp);
+ goto end;
+ }
- return;
- }
- } else {
- debug_cond(DEBUG_WGET, "wget: Connected HTTP Header %p\n", pkt);
- /* sizeof(http_eom) - 1 is the string length of (http_eom) */
- hlen = pos - (char *)pkt + sizeof(http_eom) - 1;
- pos = strstr((char *)pkt, linefeed);
- if (pos > 0)
- i = pos - (char *)pkt;
- else
- i = hlen;
- printf("%.*s", i, pkt);
-
- current_wget_state = WGET_TRANSFERRING;
-
- initial_data_seq_num = tcp_seq_num + hlen;
- next_data_seq_num = tcp_seq_num + len;
-
- if (strstr((char *)pkt, http_ok) == 0) {
- debug_cond(DEBUG_WGET,
- "wget: Connected Bad Xfer\n");
- wget_loop_state = NETLOOP_FAIL;
- wget_send(action, tcp_seq_num, tcp_ack_num, len);
- } else {
- debug_cond(DEBUG_WGET,
- "wget: Connected Pkt %p hlen %x\n",
- pkt, hlen);
-
- net_boot_file_size = 0;
-
- if (len > hlen) {
- if (store_block(pkt + hlen, 0, len - hlen) != 0) {
- wget_loop_state = NETLOOP_FAIL;
- wget_fail("wget: store error\n", tcp_seq_num, tcp_ack_num, action);
- net_set_state(NETLOOP_FAIL);
- return;
- }
- }
-
- for (i = 0; i < pkt_q_idx; i++) {
- int err;
-
- ptr1 = map_sysmem((ulong)pkt_q[i].pkt,
- pkt_q[i].len);
- err = store_block(ptr1,
- pkt_q[i].tcp_seq_num -
- initial_data_seq_num,
- pkt_q[i].len);
- unmap_sysmem(ptr1);
- debug_cond(DEBUG_WGET,
- "wget: Conncted pkt Q %p len %x\n",
- pkt_q[i].pkt, pkt_q[i].len);
- if (err) {
- wget_loop_state = NETLOOP_FAIL;
- wget_fail("wget: store error\n", tcp_seq_num, tcp_ack_num, action);
- net_set_state(NETLOOP_FAIL);
- return;
- }
- }
- }
+ debug_cond(DEBUG_WGET, "wget: Connctd pkt %p hlen %x\n",
+ ptr, http_hdr_size);
+
+ content_length = -1;
+ pos = strstr((char *)ptr, content_len);
+ if (pos) {
+ pos += strlen(content_len) + 1;
+ while (*pos == ' ')
+ pos++;
+ content_length = simple_strtoul(pos, &tail, 10);
+ if (*tail != '\r' && *tail != '\n' && *tail != '\0')
+ content_length = -1;
+ }
+
+ if (content_length >= 0) {
+ debug_cond(DEBUG_WGET,
+ "wget: Connected Len %lu\n",
+ content_length);
+ wget_info->hdr_cont_len = content_length;
}
- wget_send(action, tcp_seq_num, tcp_ack_num, len);
+
+ net_boot_file_size = rx_bytes - http_hdr_size;
+ memmove(ptr, ptr + http_hdr_size, max_rx_pos + 1 - http_hdr_size);
+ wget_loop_state = NETLOOP_SUCCESS;
+
+end:
+ unmap_sysmem(ptr);
}
-/**
- * wget_handler() - TCP handler of wget
- * @pkt: pointer to the application packet
- * @dport: destination TCP port
- * @sip: source IP address
- * @sport: source TCP port
- * @tcp_seq_num: TCP sequential number
- * @tcp_ack_num: TCP acknowledgment number
- * @action: TCP action (SYN, ACK, FIN, etc)
- * @len: packet length
- *
- * In the "application push" invocation, the TCP header with all
- * its information is pointed to by the packet pointer.
- */
-static void wget_handler(uchar *pkt, u16 dport,
- struct in_addr sip, u16 sport,
- u32 tcp_seq_num, u32 tcp_ack_num,
- u8 action, unsigned int len)
+static int tcp_stream_rx(struct tcp_stream *tcp, u32 rx_offs, void *buf, int len)
{
- enum tcp_state wget_tcp_state = tcp_get_tcp_state();
+ if ((max_rx_pos == (u32)(-1)) || (max_rx_pos < rx_offs + len - 1))
+ max_rx_pos = rx_offs + len - 1;
- net_set_timeout_handler(wget_timeout, wget_timeout_handler);
- packets++;
+ store_block(buf, rx_offs - http_hdr_size, len);
- switch (current_wget_state) {
- case WGET_CLOSED:
- debug_cond(DEBUG_WGET, "wget: Handler: Error!, State wrong\n");
- break;
- case WGET_CONNECTING:
- debug_cond(DEBUG_WGET,
- "wget: Connecting In len=%x, Seq=%u, Ack=%u\n",
- len, tcp_seq_num, tcp_ack_num);
- if (!len) {
- if (wget_tcp_state == TCP_ESTABLISHED) {
- debug_cond(DEBUG_WGET,
- "wget: Cting, send, len=%x\n", len);
- wget_send(action, tcp_seq_num, tcp_ack_num,
- len);
- } else {
- printf("%.*s", len, pkt);
- wget_fail("wget: Handler Connected Fail\n",
- tcp_seq_num, tcp_ack_num, action);
- }
- }
- break;
- case WGET_CONNECTED:
- debug_cond(DEBUG_WGET, "wget: Connected seq=%u, len=%x\n",
- tcp_seq_num, len);
- if (!len) {
- wget_fail("Image not found, no data returned\n",
- tcp_seq_num, tcp_ack_num, action);
- } else {
- wget_connected(pkt, tcp_seq_num, action, tcp_ack_num, len);
- }
- break;
- case WGET_TRANSFERRING:
- debug_cond(DEBUG_WGET,
- "wget: Transferring, seq=%x, ack=%x,len=%x\n",
- tcp_seq_num, tcp_ack_num, len);
+ return len;
+}
- if (next_data_seq_num != tcp_seq_num) {
- debug_cond(DEBUG_WGET, "wget: seq=%x packet was lost\n", next_data_seq_num);
- return;
- }
- next_data_seq_num = tcp_seq_num + len;
+static int tcp_stream_tx(struct tcp_stream *tcp, u32 tx_offs, void *buf, int maxlen)
+{
+ int ret;
+ const char *method;
- if (store_block(pkt, tcp_seq_num - initial_data_seq_num, len) != 0) {
- wget_fail("wget: store error\n",
- tcp_seq_num, tcp_ack_num, action);
- net_set_state(NETLOOP_FAIL);
- return;
- }
+ if (tx_offs)
+ return 0;
- switch (wget_tcp_state) {
- case TCP_FIN_WAIT_2:
- wget_send(TCP_ACK, tcp_seq_num, tcp_ack_num, len);
- fallthrough;
- case TCP_SYN_SENT:
- case TCP_SYN_RECEIVED:
- case TCP_CLOSING:
- case TCP_FIN_WAIT_1:
- case TCP_CLOSED:
- net_set_state(NETLOOP_FAIL);
- break;
- case TCP_ESTABLISHED:
- wget_send(TCP_ACK, tcp_seq_num, tcp_ack_num,
- len);
- wget_loop_state = NETLOOP_SUCCESS;
- break;
- case TCP_CLOSE_WAIT: /* End of transfer */
- current_wget_state = WGET_TRANSFERRED;
- wget_send(action | TCP_ACK | TCP_FIN,
- tcp_seq_num, tcp_ack_num, len);
- break;
- }
+ switch (wget_info->method) {
+ case WGET_HTTP_METHOD_HEAD:
+ method = "HEAD";
break;
- case WGET_TRANSFERRED:
- printf("Packets received %d, Transfer Successful\n", packets);
- net_set_state(wget_loop_state);
- efi_set_bootdev("Net", "", image_url,
- map_sysmem(image_load_addr, 0),
- net_boot_file_size);
- env_set_hex("filesize", net_boot_file_size);
+ case WGET_HTTP_METHOD_GET:
+ default:
+ method = "GET";
break;
}
-}
-#define RANDOM_PORT_START 1024
-#define RANDOM_PORT_RANGE 0x4000
+ ret = snprintf(buf, maxlen, "%s %s %s\r\n\r\n",
+ method, image_url, http_proto);
-/**
- * random_port() - make port a little random (1024-17407)
- *
- * Return: random port number from 1024 to 17407
- *
- * This keeps the math somewhat trivial to compute, and seems to work with
- * all supported protocols/clients/servers
- */
-static unsigned int random_port(void)
+ return ret;
+}
+
+static int tcp_stream_on_create(struct tcp_stream *tcp)
{
- return RANDOM_PORT_START + (get_timer(0) % RANDOM_PORT_RANGE);
+ if (tcp->rhost.s_addr != web_server_ip.s_addr ||
+ tcp->rport != server_port)
+ return 0;
+
+ tcp->max_retry_count = WGET_RETRY_COUNT;
+ tcp->initial_timeout = WGET_TIMEOUT;
+ tcp->on_closed = tcp_stream_on_closed;
+ tcp->on_rcv_nxt_update = tcp_stream_on_rcv_nxt_update;
+ tcp->rx = tcp_stream_rx;
+ tcp->tx = tcp_stream_tx;
+
+ return 1;
}
#define BLOCKSIZE 512
void wget_start(void)
{
+ struct tcp_stream *tcp;
+
+ if (!wget_info)
+ wget_info = &default_wget_info;
+
image_url = strchr(net_boot_file_name, ':');
if (image_url > 0) {
web_server_ip = string_to_ip(net_boot_file_name);
@@ -449,14 +318,6 @@ void wget_start(void)
debug_cond(DEBUG_WGET,
"\nwget:Load address: 0x%lx\nLoading: *\b", image_load_addr);
- net_set_timeout_handler(wget_timeout, wget_timeout_handler);
- tcp_set_tcp_handler(wget_handler);
-
- wget_timeout_count = 0;
- current_wget_state = WGET_CLOSED;
-
- our_port = random_port();
-
/*
* Zero out server ether to force arp resolution in case
* the server ip for the previous u-boot command, for example dns
@@ -465,11 +326,30 @@ void wget_start(void)
memset(net_server_ethaddr, 0, 6);
- wget_send(TCP_SYN, 0, 0, 0);
+ max_rx_pos = (u32)(-1);
+ net_boot_file_size = 0;
+ http_hdr_size = 0;
+ wget_tsize_num_hash = 0;
+ wget_loop_state = NETLOOP_FAIL;
+
+ wget_info->status_code = HTTP_STATUS_BAD;
+ wget_info->file_size = 0;
+ wget_info->hdr_cont_len = 0;
+ if (wget_info->headers)
+ wget_info->headers[0] = 0;
+
+ server_port = env_get_ulong("httpdstp", 10, SERVER_PORT) & 0xffff;
+ tcp_stream_set_on_create_handler(tcp_stream_on_create);
+ tcp = tcp_stream_connect(web_server_ip, server_port);
+ if (!tcp) {
+ printf("No free tcp streams\n");
+ net_set_state(NETLOOP_FAIL);
+ return;
+ }
+ tcp_stream_put(tcp);
}
-#if (IS_ENABLED(CONFIG_CMD_DNS))
-int wget_with_dns(ulong dst_addr, char *uri)
+int wget_do_request(ulong dst_addr, char *uri)
{
int ret;
char *s, *host_name, *file_name, *str_copy;
@@ -488,24 +368,32 @@ int wget_with_dns(ulong dst_addr, char *uri)
s = str_copy + strlen("http://");
host_name = strsep(&s, "/");
if (!s) {
- log_err("Error: invalied uri, no file path\n");
ret = -EINVAL;
goto out;
}
file_name = s;
- /* TODO: If the given uri has ip address for the http server, skip dns */
- net_dns_resolve = host_name;
- net_dns_env_var = "httpserverip";
- if (net_loop(DNS) < 0) {
- log_err("Error: dns lookup of %s failed, check setup\n", net_dns_resolve);
- ret = -EINVAL;
- goto out;
- }
- s = env_get("httpserverip");
- if (!s) {
+ host_name = strsep(&host_name, ":");
+
+ if (string_to_ip(host_name).s_addr) {
+ s = host_name;
+ } else {
+#if IS_ENABLED(CONFIG_CMD_DNS)
+ net_dns_resolve = host_name;
+ net_dns_env_var = "httpserverip";
+ if (net_loop(DNS) < 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+ s = env_get("httpserverip");
+ if (!s) {
+ ret = -EINVAL;
+ goto out;
+ }
+#else
ret = -EINVAL;
goto out;
+#endif
}
strlcpy(net_boot_file_name, s, sizeof(net_boot_file_name));
@@ -517,9 +405,8 @@ int wget_with_dns(ulong dst_addr, char *uri)
out:
free(str_copy);
- return ret;
+ return ret < 0 ? ret : 0;
}
-#endif
/**
* wget_validate_uri() - validate the uri for wget