All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] Request For Comments: wget and TCP listener
       [not found] <1166665933.7178985.1506025978766.ref@mail.yahoo.com>
@ 2017-09-21 20:32 ` Duncan Hare
  2017-09-25  2:14   ` Simon Glass
  0 siblings, 1 reply; 4+ messages in thread
From: Duncan Hare @ 2017-09-21 20:32 UTC (permalink / raw)
  To: u-boot

Mods to:
cmd/net.cnet/Makefilenet/net.cinclude/net.hnet/wget.cnet/wget.hnet/ping.c
I do not know how to do patches, I'm a noobat this:
cmd/net.c
Additions
static int do_wget(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]){        return netboot_common(WGET, cmdtp, argc, argv);}
U_BOOT_CMD(        wget,    3,      1,      do_wget,        "boot image via network using HTTP protocol",        "[loadAddress] [[hostIPaddr:]bootfilename]");
net/Makefile addition
obj-$(CONFIG_CMD_NFS)  += wget.o

net.c  complete, look for #ifdef TCP
 /*
 *	Copied from Linux Monitor (LiMon) - Networking.
 *
 *	Copyright 1994 - 2000 Neil Russell.
 *	(See License)
 *	Copyright 2000 Roland Borde
 *	Copyright 2000 Paolo Scaffardi
 *	Copyright 2000-2002 Wolfgang Denk, wd at denx.de
 *	Copyright 2017-2018 Duncan Hare, dh at synoia.com
 *	SPDX-License-Identifier:	GPL-2.0
 */
#define TCP 1
/*
 * General Desription:
 *
 * The user interface supports commands for BOOTP, RARP, and TFTP.
 * Also, we support ARP internally. Depending on available data,
 * these interact as follows:
 *
 * BOOTP:
 *
 *	Prerequisites:	- own ethernet address
 *	We want:	- own IP address
 *			- TFTP server IP address
 *			- name of bootfile
 *	Next step:	ARP
 *
 * LINK_LOCAL:
 *
 *	Prerequisites:	- own ethernet address
 *	We want:	- own IP address
 *	Next step:	ARP
 *
 * RARP:
 *
 *	Prerequisites:	- own ethernet address
 *	We want:	- own IP address
 *			- TFTP server IP address
 *	Next step:	ARP
 *
 * ARP:
 *
 *	Prerequisites:	- own ethernet address
 *			- own IP address
 *			- TFTP server IP address
 *	We want:	- TFTP server ethernet address
 *	Next step:	TFTP
 *
 * DHCP:
 *
 *     Prerequisites:	- own ethernet address
 *     We want:		- IP, Netmask, ServerIP, Gateway IP
 *			- bootfilename, lease time
 *     Next step:	- TFTP
 *
 * TFTP:
 *
 *	Prerequisites:	- own ethernet address
 *			- own IP address
 *			- TFTP server IP address
 *			- TFTP server ethernet address
 *			- name of bootfile (if unknown, we use a default name
 *			  derived from our own IP address)
 *	We want:	- load the boot file
 *	Next step:	none
 *
 * NFS:
 *
 *	Prerequisites:	- own ethernet address
 *			- own IP address
 *			- name of bootfile (if unknown, we use a default name
 *			  derived from our own IP address)
 *	We want:	- load the boot file
 *	Next step:	none
 *
 * SNTP:
 *
 *	Prerequisites:	- own ethernet address
 *			- own IP address
 *	We want:	- network time
 *	Next step:	none
 *
 * HTTP/TCP Receiver:
 *
 * 	Prequeisites:	- own ethernet adress
 *			- own IP address
 *			- Server IP address
 *			- HTP client
 *			- Bootfile path & name
 *	We want:	- Load the Boot file
 *	Next Step	HTTPS?
 */
#define DEBUG_DCH_PKT 1
#define DEBUG_TCP_PKT 0

#include <common.h>
#include <command.h>
#include <console.h>
#include <environment.h>
#include <errno.h>
#include <net.h>
#include <net/tftp.h>
#if defined(CONFIG_STATUS_LED)
#include <miiphy.h>
#include <status_led.h>
#endif
#include <watchdog.h>
#include <linux/compiler.h>
#include "arp.h"
#include "bootp.h"
#include "cdp.h"
#if defined(CONFIG_CMD_DNS)
#include "dns.h"
#endif
#include "link_local.h"
#include "nfs.h"
#include "wget.h"
#include "ping.h"
#include "rarp.h"
#if defined(CONFIG_CMD_SNTP)
#include "sntp.h"
#endif

DECLARE_GLOBAL_DATA_PTR;

/** BOOTP EXTENTIONS **/

/* Our subnet mask (0=unknown) */
struct in_addr net_netmask;
/* Our gateways IP address */
struct in_addr net_gateway;
/* Our DNS IP address */
struct in_addr net_dns_server;
#if defined(CONFIG_BOOTP_DNS2)
/* Our 2nd DNS IP address */
struct in_addr net_dns_server2;
#endif

#ifdef CONFIG_MCAST_TFTP	/* Multicast TFTP */
struct in_addr net_mcast_addr;
#endif

/** END OF BOOTP EXTENTIONS **/

/* Our ethernet address */
u8 net_ethaddr[6];
/* Boot server enet address */
u8 net_server_ethaddr[6];
/* Our IP addr (0 = unknown) */
struct in_addr	net_ip;
/* Server IP addr (0 = unknown) */
struct in_addr	net_server_ip;
/* Port numbers			*/
int dport;
int sport;
/* Current receive packet */
uchar *net_rx_packet;
/* Current rx max packet length */
int net_rx_packet_len;
/* IP packet ID */
static unsigned	net_ip_id; 
/* Ethernet bcast address */
const u8 net_bcast_ethaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
const u8 net_null_ethaddr[6];
#if defined(CONFIG_API) || defined(CONFIG_EFI_LOADER)
void (*push_packet)(void *, int len) = 0;
#endif
/* Network loop state */
enum net_loop_state net_state;
/* Tried all network devices */
int		net_restart_wrap;
/* Network loop restarted */
static int	net_restarted;
/* At least one device configured */
static int	net_dev_exists;

#ifdef TCP

/* TCP sliding window  control  */
static u32 tcp_next_expected_seq_num;
static struct tcp_sack_v tcp_lost;			/* used by us to request re-TX		*/

struct w_manage {
	struct sack_edges tcp_hole;
	enum acked { FALSE, TRUE, FILLED } sent;
	int age;
};

static int tcp_max_hole;

struct w_manage holes[TCP_STREAM_HOLES];

/* TCP option timestamp */

static u32 loc_timestamp;
static u32 rmt_timestamp;

/* TCP connection state */
static enum TCP_STATE tcp_state;

#endif

/* XXX in both little & big endian machines 0xFFFF == ntohs(-1) */
/* default is without VLAN */
ushort		net_our_vlan = 0xFFFF;
/* ditto */
ushort		net_native_vlan = 0xFFFF;

/* Boot File name */
char net_boot_file_name[1024];
/* The actual transferred size of the bootfile (in bytes) */
u32 net_boot_file_size;
/* Boot file size in blocks as reported by the DHCP server */
u32 net_boot_file_expected_size_in_blocks;

#if defined(CONFIG_CMD_SNTP)
/* NTP server IP address */
struct in_addr	net_ntp_server;
/* offset time from UTC */
int		net_ntp_time_offset;
#endif

static uchar net_pkt_buf[(PKTBUFSRX+1) * PKTSIZE_ALIGN + PKTALIGN];
/* Receive packets */
uchar *net_rx_packets[PKTBUFSRX];

/*
 * An incoming TCP packet handler for the TCP protocol.
 * There is also a dymanic function pointer for TCP based commads to
 * receive incoming traffic after the TCP protocol code has done its work.
*/

void rxhand_tcp_f( union tcp_build_pkt *b, unsigned len);

/* Current TCP RX packet handler */
static rxhand_f *tcp_packet_handler;
/* Current UDP RX packet handler */
static rxhand_f *udp_packet_handler;
/* Current ARP RX packet handler */
static rxhand_f *arp_packet_handler;
#ifdef CONFIG_CMD_TFTPPUT
/* Current ICMP rx handler */
static rxhand_icmp_f *packet_icmp_handler;
#endif
/* Current timeout handler */
static thand_f *time_handler;
/* Time base value */
static ulong	time_start;
/* Current timeout value */
static ulong	time_delta;
/* THE transmit packet */
uchar *net_tx_packet;

static int net_check_prereq(enum proto_t protocol);

static int net_try_count;

int __maybe_unused net_busy_flag;

/**********************************************************************/

static int on_bootfile(const char *name, const char *value, enum env_op op,
	int flags)
{
	if (flags & H_PROGRAMMATIC)
		return 0;

	switch (op) {
	case env_op_create:
	case env_op_overwrite:
		copy_filename(net_boot_file_name, value,
			      sizeof(net_boot_file_name));
		break;
	default:
		break;
	}

	return 0;
}
U_BOOT_ENV_CALLBACK(bootfile, on_bootfile);

static int on_ipaddr(const char *name, const char *value, enum env_op op,
	int flags)
{
	if (flags & H_PROGRAMMATIC)
		return 0;

	net_ip = string_to_ip(value);

	return 0;
}
U_BOOT_ENV_CALLBACK(ipaddr, on_ipaddr);

static int on_gatewayip(const char *name, const char *value, enum env_op op,
	int flags)
{
	if (flags & H_PROGRAMMATIC)
		return 0;

	net_gateway = string_to_ip(value);

	return 0;
}
U_BOOT_ENV_CALLBACK(gatewayip, on_gatewayip);

static int on_netmask(const char *name, const char *value, enum env_op op,
	int flags)
{
	if (flags & H_PROGRAMMATIC)
		return 0;

	net_netmask = string_to_ip(value);

	return 0;
}
U_BOOT_ENV_CALLBACK(netmask, on_netmask);

static int on_serverip(const char *name, const char *value, enum env_op op,
	int flags)
{
	if (flags & H_PROGRAMMATIC)
		return 0;

	net_server_ip = string_to_ip(value);

	return 0;
}
U_BOOT_ENV_CALLBACK(serverip, on_serverip);

static int on_nvlan(const char *name, const char *value, enum env_op op,
	int flags)
{
	if (flags & H_PROGRAMMATIC)
		return 0;

	net_native_vlan = string_to_vlan(value);

	return 0;
}
U_BOOT_ENV_CALLBACK(nvlan, on_nvlan);

static int on_vlan(const char *name, const char *value, enum env_op op,
	int flags)
{
	if (flags & H_PROGRAMMATIC)
		return 0;

	net_our_vlan = string_to_vlan(value);

	return 0;
}
U_BOOT_ENV_CALLBACK(vlan, on_vlan);

#if defined(CONFIG_CMD_DNS)
static int on_dnsip(const char *name, const char *value, enum env_op op,
	int flags)
{
	if (flags & H_PROGRAMMATIC)
		return 0;

	net_dns_server = string_to_ip(value);

	return 0;
}
U_BOOT_ENV_CALLBACK(dnsip, on_dnsip);
#endif

/*
 * Check if autoload is enabled. If so, use either NFS or TFTP to download
 * the boot file.
 */
void net_auto_load(void)
{
#if defined(CONFIG_CMD_NFS)
	const char *s = getenv("autoload");

	if (s != NULL && strcmp(s, "NFS") == 0) {
		/*
		 * Use NFS to load the bootfile.
		 */
		nfs_start();
		return;
	}
#endif
	if (getenv_yesno("autoload") == 0) {
		/*
		 * Just use BOOTP/RARP to configure system;
		 * Do not use TFTP to load the bootfile.
		 */
		net_set_state(NETLOOP_SUCCESS);
		return;
	}
	tftp_start(TFTPGET);
}

static void net_init_loop(void)
{
	if (eth_get_dev())
		memcpy(net_ethaddr, eth_get_ethaddr(), 6);

	return;
}

static void net_clear_handlers(void)
{
	net_set_tcp_handler(NULL);
	net_set_udp_handler(NULL);
	net_set_arp_handler(NULL);
	net_set_timeout_handler(0, NULL);
}

static void net_cleanup_loop(void)
{
	net_clear_handlers();
}

void net_set_ports( int server_port, int our_port )
{
	dport = server_port;
	sport = our_port;
}


#ifdef TCP

enum TCP_STATE net_get_tcp_state( void )
{
	return(tcp_state);
}

void net_print_buffer( uchar raw[], int pkt_len, int payload_len, int hdr_len, bool hide )
{
	int i;
	for ( i = pkt_len - payload_len; i < pkt_len; i++ )
	{
		if ( i <= hdr_len )
		{
			printf("%02X", raw[i]);
		}
		else if (( raw[i] > 0x19 ) && ( raw[i] < 0x7f ))
		{
			putc( raw[i] );
		}
		else if (hide == 0 ) putc( raw[i] );
		else printf("%02X", raw[i]);

//		else printf ("%s",".");
	}
	printf ( "%s", "\n" );
}

int net_find_in_buffer( uchar raw[], int payload_len, uchar field[], int field_len  )
{
	int i,j;

	for (i = 0; i < payload_len; i ++ )
	{
		if ( raw[i] == field[0] )
		{
			for (j = 1; j < field_len; j++ )
			{
				if ( raw[i+j] != field[j] ) break;
			}
			if ( j == field_len ) return( i );
		}

	}
	return ( 0 );
}

u16 net_set_psuedo_header( uchar * pkt, struct in_addr src, struct in_addr dest, int tcp_len, int pkt_len )
{
	union tcp_build_pkt *b = (union tcp_build_pkt *) pkt;
	int checksum_len;
	/*
	 * Psuedo header
	*/
	pkt[ pkt_len ] = 0x00;

	net_copy_ip((void *)&b->ph.p_src, &src);
	net_copy_ip((void *)&b->ph.p_dst, &dest);
	b->ph.rsvd	= 0x00;
	b->ph.p		= IPPROTO_TCP;
	b->ph.len	= htons(tcp_len);
	checksum_len	= tcp_len + PSUEDO_HDR_SIZE;

		debug_cond(DEBUG_DEV_PKT,
			"TCP Psuedo  Header  (to=%pI4, from=%pI4, CheckLen=%d)\n",
			&b->ph.p_dst, &b->ph.p_src, checksum_len );
	/*
	 *      If the data is an odd number of bytes, zero the
	 *      byte after the last byte so that the header checksum
	 *      will work.
	 */

	return( compute_ip_checksum( pkt + PSUEDO_PAD_SIZE, checksum_len ));

}

int net_set_ack_options( union tcp_build_pkt *b )
{
	b->sack.hdr.tcp_hlen  = ( TCP_HDR_SIZE >>2 ) << 4;

	b->sack.TSopt.kind		= TCP_O_TS;
	b->sack.TSopt.len		= TCP_OPT_LEN_A;
	b->sack.TSopt.TSsnd		= htons(loc_timestamp);
	b->sack.TSopt.TSrcv		= rmt_timestamp;
	b->sack.sack_v.kind             = 0x01;
	b->sack.sack_v.len		= 0x00;

	if ( tcp_lost.len  > 0 )
	{
		b->sack.sack_v.len              = tcp_lost.len;
		b->sack.sack_v.kind		= TCP_V_SACK;
		b->sack.sack_v.hole[1].l	= htonl(tcp_lost.hole[1].l);
		b->sack.sack_v.hole[1].r	= htonl(tcp_lost.hole[1].r);

		/*
		* These fields are initialized with NOPs to
		* provide TCP header alignment padding
		*/

		b->sack.sack_v.hole[2].l	= htonl(tcp_lost.hole[1].l);
		b->sack.sack_v.hole[2].r	= htonl(tcp_lost.hole[1].r);
		b->sack.sack_v.hole[3].l	= htonl(tcp_lost.hole[2].l);
		b->sack.sack_v.hole[3].r	= htonl(tcp_lost.hole[2].r);

		tcp_lost.len			= 0;
	}
	b->sack.hdr.tcp_hlen		= (((TCP_HDR_SIZE + TCP_TSOPT_SIZE + tcp_lost.len + 3)  >> 2) << 4);

	return( b->sack.hdr.tcp_hlen >> 2);
}

void net_set_syn_options( union tcp_build_pkt *b )
{
	tcp_lost.len		= 0;
	b->ip.hdr.tcp_hlen      = 0xa0;                 /* hdr 10 32 bit words  */

	b->ip.mss.kind          = TCP_O_MSS;
	b->ip.mss.len           = TCP_OPT_LEN_4;
	b->ip.mss.mss           = htons(TCP_MSS);       /* tunable parameter    */
	b->ip.scale.kind        = TCP_O_SCL;
	b->ip.scale.scale       = TCP_SCALE;            /* tunable parameter    */
	b->ip.scale.len         = TCP_OPT_LEN_3;
	b->ip.sack_p.kind       = TCP_P_SACK ;          /* SACK supported       */
	b->ip.sack_p.len        = TCP_OPT_LEN_2;
	b->ip.TSopt.kind        = TCP_O_TS;
	b->ip.TSopt.len         = TCP_OPT_LEN_A;
	loc_timestamp           = get_ticks() % 3072;
	rmt_timestamp           = 0x00000000;
	b->ip.TSopt.TSsnd	= 0;
	b->ip.TSopt.TSrcv       = 0x00000000;
	b->ip.end               = TCP_O_END;
}

int net_set_tcp_header(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;
	int	pkt_hdr_len;
	int	pkt_len;
	int	tcp_len;

	b->ip.hdr.tcp_flags	= action;
	pkt_hdr_len		= IP_TCP_HDR_SIZE;
	b->ip.hdr.tcp_hlen      = 0x50;				/* Header is 5 32 bit words     */
								/* 4 bits TCP header Length/4	*/
								/* 4 bits Reserved              */
	switch (action)						/* For options			*/
	{
		case TCP_SYN:
			debug_cond(DEBUG_TCP_PKT,
				"TCP Header:SYN  (to=%pI4, from=%pI4, TCPseq=%d, TCPack=%d)\n",
					&net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num);

			net_set_syn_options( b );
			tcp_seq_num             = 0;                    /* TCP sequence number  */
        		tcp_ack_num             = 0;                    /* TCP Ack Number       */
			pkt_hdr_len		= IP_TCP_O_SIZE;
			if ( tcp_state == TCP_SYN_SENT)			/* Too many sins 	*/
			{
				action 	  	= TCP_FIN;
				tcp_state 	= TCP_FIN_WAIT_1;
			}
 			else	tcp_state 	= TCP_SYN_SENT;
		break;
		case TCP_ACK:
			pkt_hdr_len		= IP_HDR_SIZE + net_set_ack_options( b );
			b->ip.hdr.tcp_flags	= action;
			debug_cond(DEBUG_TCP_PKT,
				"TCP Header:ACK (to=%pI4, from=%pI4, TCPseq=%d, TCPack=%d, Action=%x)\n",
				&net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num, b->ip.hdr.tcp_flags);

		break;
		case TCP_FIN:
			debug_cond(DEBUG_TCP_PKT,
				"TCP Header:FIN  (to=%pI4, from=%pI4, TCPseq=%d, TCPack=%d)\n",
				&net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num);
			payload_len		= 0;
			pkt_hdr_len     	= IP_TCP_HDR_SIZE;
			tcp_state 		= TCP_FIN_WAIT_1;
			tcp_seq_num++;
		break;
		case (TCP_FIN + TCP_ACK):
		case (TCP_FIN + TCP_ACK + TCP_PUSH):
			if ( tcp_state == TCP_CLOSE_WAIT ) tcp_state = TCP_CLOSING;
		default:
			pkt_hdr_len		= IP_HDR_SIZE + net_set_ack_options( b );
			b->ip.hdr.tcp_flags	= action | TCP_PUSH | TCP_ACK;
			debug_cond(DEBUG_TCP_PKT,
				"TCP Header:default  (to=%pI4, from=%pI4, TCPseq=%d, TCPack=%d, Action=%x)\n",
				&net_server_ip, &net_ip, tcp_seq_num, tcp_ack_num, b->ip.hdr.tcp_flags);
	}
	pkt_len		= pkt_hdr_len + payload_len;
	tcp_len		= pkt_len - IP_HDR_SIZE;

	/*
	 * TCP Header
	*/
	b->ip.hdr.tcp_ack       = htonl( tcp_ack_num);
	b->ip.hdr.tcp_src	= htons(sport);
	b->ip.hdr.tcp_dst	= htons(dport);
	b->ip.hdr.tcp_seq	= htonl(tcp_seq_num);
	tcp_seq_num		= tcp_seq_num + payload_len;

	/*
	 * TCP window size - TCP header variable tcp_win.
	 * Chage tcp_win only if you have an understanding of network overruun, congestion,
	 * TCP segment sizes, TCP windows, TCP scale, queuing theory  and packet buffering.
	 * If there are too few buffers, there will be data loss, recovery may work or the
	 * sending TCP, the server, could abort the stream transmission.
	 * MSS is governed by maximum Ethernet frame langth.
	 * The number of buffers is governed by the desire to have a queue of full buffers
	 * to be processed at the destination to maximize throughput.
	 * Temporary  memory use for the boot phase on modern SOCs is not considered a constraint to
	 * buffer space.
	*/

	b->ip.hdr.tcp_win	= htons( PKTBUFSRX * TCP_MSS >> TCP_SCALE );

	b->ip.hdr.tcp_xsum	= 0x0000;			/* Checksum                     */
	b->ip.hdr.tcp_ugr	= 0x0000;			/* Pointer to urgent data       */

	b->ip.hdr.tcp_xsum = net_set_psuedo_header( pkt, net_ip, net_server_ip, tcp_len, pkt_len );

	/*
	 * IP Header
	*/

	net_set_ip_header((uchar *) &b->ip, net_server_ip, net_ip, pkt_len, IPPROTO_TCP);

	return ( pkt_hdr_len );
}

#endif

int net_send_ip_packet( int payload_len, int proto, u8 action, u32 tcp_seq_num, u32 tcp_ack_num )
{
	uchar *pkt;
	int eth_hdr_size;
	int pkt_hdr_size;
	uchar * ether = net_server_ethaddr;


	if ( proto == IPPROTO_UDP )
	{ debug_cond(DEBUG_DEV_PKT,
                           "UDP Send  (to=%pI4, from=%pI4, len=%d)\n",
       	                   &net_server_ip, &net_ip, payload_len);
	}
#ifdef TCP
	else
	{ debug_cond(DEBUG_TCP_PKT,
                           "TCP Send  (to=%pI4, from=%pI4, len=%d, Action=%x)\n",
                           &net_server_ip, &net_ip, payload_len, action );
	}
#endif

	/* make sure the net_tx_packet is initialized (net_init() was called) */
	assert(net_tx_packet != NULL);
	if (net_tx_packet == NULL) return -1;

        /* convert to new style broadcast */
	if (net_server_ip.s_addr == 0) net_server_ip.s_addr = 0xFFFFFFFF;

	/* if broadcast, make the ether address a broadcast and don't do ARP */
	if (net_server_ip.s_addr == 0xFFFFFFFF) ether = (uchar *)net_bcast_ethaddr;

	pkt = (uchar *)net_tx_packet;

	/*
	 *Get ethernet header size and write the ethernet header
	*/

	eth_hdr_size  = net_set_ether(pkt, ether, PROT_IP);

#ifdef TCP
	if (proto == IPPROTO_UDP )
	{
#endif
	        net_set_udp_header(pkt + eth_hdr_size, net_server_ip, dport, sport, payload_len);
		pkt_hdr_size = IP_UDP_HDR_SIZE;
		eth_hdr_size = eth_hdr_size + pkt_hdr_size;
#ifdef TCP
	}
	else pkt_hdr_size = eth_hdr_size + net_set_tcp_header(pkt + eth_hdr_size,
		payload_len, action, tcp_seq_num, tcp_ack_num );
#endif
	/* if MAC address was not discovered yet, do an ARP request */
	if (memcmp(ether, net_null_ethaddr, 6) == 0) {
		debug_cond(DEBUG_DEV_PKT, "sending ARP for %pI4\n", &net_server_ip);

		/* save the ip and eth addr for the packet to send after arp */
		net_arp_wait_packet_ip = net_server_ip;
		arp_wait_packet_ethaddr = ether;

		/* size of the waiting packet */
		arp_wait_tx_packet_size = pkt_hdr_size + payload_len;

		/* and do the ARP request */
		arp_wait_try = 1;
		arp_wait_timer_start = get_timer(0);
		arp_request();
		return 1;       /* waiting */
	} else {
                debug_cond(DEBUG_DEV_PKT, "sending TCP to %pI4/%pM/%d\n",
                           &net_server_ip, ether, pkt_hdr_size + payload_len);
		net_send_packet(net_tx_packet, pkt_hdr_size + payload_len);
		return 0;       /* transmitted */
	}
}

#ifdef TCP

int tcp_hole_age ( int age )
{
	int i, j = 0, filled = 0;

	tcp_lost.len		= TCP_OPT_LEN_2;
	tcp_lost.hole[1].l      = TCP_O_NOP;
	tcp_lost.hole[1].r      = TCP_O_NOP;
	tcp_lost.hole[2].l      = TCP_O_NOP;
	tcp_lost.hole[2].r      = TCP_O_NOP;

	tcp_lost.kind           = TCP_V_SACK;

	for (i = 0; (( i == tcp_max_hole ) || ( j == 2 )) ; i++ )
	{
		if (( holes[i].tcp_hole.l != 0 ) && ( holes[i].sent == FALSE ))
		{
			if ( holes[i].age <= TCP_HOLE_AGE ) holes[i].age++;
			else
			{
				/*
		 		* Ask for hole to be filled by sender on next ack response.
				*/
				tcp_lost.len            = tcp_lost.len + TCP_OPT_LEN_8;
				tcp_lost.hole[j].l      = holes[i].tcp_hole.l;
				tcp_lost.hole[j].l      = holes[i].tcp_hole.r;
				holes[i].sent		= TRUE;
				j++;
			}
		}
		if (( holes[i].tcp_hole.l == 0 ) || ( holes[i].sent == FILLED )) filled++;

		switch (holes[i].sent)
		{
			case FALSE:
				debug_cond( DEBUG_DCH_PKT, ".");
			break;
			case TRUE:
				debug_cond( DEBUG_DCH_PKT, "o");
			break;
			case FILLED:
				debug_cond( DEBUG_DCH_PKT, "|");
			break;
		}
	}
	debug_cond( DEBUG_DCH_PKT, "\n");

	/*
	 * No holes found? Set return code.
	*/

	if ( tcp_lost.len == TCP_OPT_LEN_2 )
	{
		tcp_lost.len = 0;
		return ( filled - tcp_max_hole );
	}
	else return ( 1 );
}

void tcp_hole_fill( u32 tcp_left, u32 tcp_right )
{
	int i ,j;

	for ( i = 0; (( i = tcp_max_hole ) || ( tcp_left != holes[i].tcp_hole.l )); i++ );

	if ( tcp_left == holes[i].tcp_hole.l )
	{
		holes[i].sent = FILLED;
		/*
		 * Move holes to down hole array as preceeding holes become filled
		*/
		for ( j = i; (j < tcp_max_hole); j++ )
		{
			holes[j] = holes[j + 1];
		}
	}
	else debug_cond( DEBUG_DCH_PKT, "Error: Hole not found: Left=%d\n", tcp_left );
}

void tcp_hole_create( u32 tcp_left, u32 tcp_right )
{
	debug_cond( 1, "Hole Created Left=%d\n", tcp_left );

	tcp_lost.len            = tcp_lost.len + TCP_OPT_LEN_A;
	tcp_lost.hole[0].l      = tcp_left;
	tcp_lost.hole[0].l      = tcp_right;

	tcp_lost.hole[1].l      = TCP_O_NOP;
	tcp_lost.hole[1].r      = TCP_O_NOP;
	tcp_lost.hole[2].l      = TCP_O_NOP;
	tcp_lost.hole[2].r      = TCP_O_NOP;

}

void tcp_hole_create_complex( u32 tcp_left, u32 tcp_right )
{
	int i;

	for (i = 0; i < TCP_STREAM_HOLES; i++ )
	{
		/*
		 * Find empty hole struct and fill it
		*/
		if ( holes[i].tcp_hole.l == 0 )
		holes[i].tcp_hole.l 	= tcp_left;
		holes[i].tcp_hole.r 	= tcp_right;
		holes[i].sent		= FALSE;
		holes[i].age		= 0;
		if ( i > tcp_max_hole ) tcp_max_hole = i;
		return;
	}
	debug_cond( 1, "Error: Too many TCP erorrs, increase TCP_STREAM_HOLES or get a better network\n");
	net_set_state(NETLOOP_FAIL);

}


void init_tcp_holes( u32 tcp_seq_num )
{
	int i;

	for (i = 0; i < TCP_STREAM_HOLES; i++)
	{
		holes[i].tcp_hole.l 	= 0;
		holes[i].tcp_hole.r 	= 0;
		holes[i].sent		= FALSE;
		holes[i].age		= 0;
	}

	tcp_next_expected_seq_num 	= tcp_seq_num;
	/*
	 * // For production set to 0.
	 * // For testingt set to TCP_STREAM_HOLES - 1
	*/
	tcp_max_hole 			= TCP_STREAM_HOLES - 1;		// For testing.
									// For production set 0.
}

void tcp_parse_options( uchar *o, int o_len)
{
	struct tcp_TSopt  *tsopt;
	uchar *p = o;

	for ( p = o; p < (o + o_len); p = p + p[1])
	{
		if (p[1] == 0) return;
		else
		{
			switch ( p[0] )
			{
				case TCP_O_END: return;
				case TCP_O_MSS:
				break;
				case TCP_O_SCL:
				break;
				case TCP_P_SACK:
				break;
				case TCP_V_SACK:
				break;
				case TCP_O_TS :
					tsopt = (struct tcp_TSopt *) p;
					rmt_timestamp = tsopt->TSsnd;
					return;
				break;
			}
			if (p[0] == TCP_O_NOP) p++;
		}
	}
	return;
}

u8 tcp_state_machine( u8 tcp_flags, 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  tcp_urg     = tcp_flags & TCP_URG;
 	u8  tcp_ece     = tcp_flags & TCP_ECE;
 	u8  tcp_cwr     = tcp_flags & TCP_CWR;
	u8  action	= TCP_DATA;

	/*
	 * tcp_flags are examined to determine TX action in a given state
	 * tcp_options, if not zero, is a pointer to the TCP options,
	 * to extract the receive timestamp value
	*/

	/*
	 * These flags are not supported.
	*/
	if (tcp_urg)    action = action;
	if (tcp_ece)    action = action;
	if (tcp_cwr)    action = action;

	debug_cond(DEBUG_INT_STATE, "TCP STATE ENTRY (%x)\n", action);
	if (tcp_rst)
	{
		action 		= TCP_DATA;
		tcp_state 	= TCP_CLOSED;
		net_set_state( NETLOOP_FAIL );
		debug_cond(DEBUG_DCH_PKT, "TCP Reset (%x)\n", tcp_flags);
	}
	else switch  (tcp_state)
	{
		case	TCP_CLOSED:					/* Should never happen	*/
		debug_cond(DEBUG_TCP_PKT, "TCP CLOSED (%x)\n", tcp_flags);
			if (tcp_fin)  action = TCP_DATA;
			if (tcp_syn)  action = TCP_RST;
			if (tcp_ack)  action = TCP_DATA;
		break;
		case	TCP_SYN_SENT:
			debug_cond(DEBUG_TCP_PKT, "TCP_SYN_SENT (%x), %d\n", tcp_flags, *tcp_seq_num);
 			if (tcp_fin) {action = action | TCP_PUSH; tcp_state = TCP_CLOSE_WAIT;}
 			if (tcp_syn)
			{
				action = action |  TCP_ACK;
				if (tcp_ack)
				{
					*tcp_seq_num = *tcp_seq_num + 1;
					tcp_state	= TCP_ESTABLISHED;	/* SACK         */
					action		= action | TCP_PUSH;	/* Notify app	*/
					init_tcp_holes( *tcp_seq_num );
				}
			}
			else
			if (tcp_ack)  action = TCP_DATA;
                break;
			case TCP_ESTABLISHED:
			debug_cond(DEBUG_TCP_PKT, "TCP_ESTABLISHED (%x)\n", tcp_flags );
			if (tcp_fin)
			{	/*
				 * Check for holes !!
				*/
				*tcp_seq_num = *tcp_seq_num + 1;
				tcp_next_expected_seq_num ++;
				action = action | TCP_FIN | TCP_PUSH | TCP_ACK;
				tcp_state 	= TCP_CLOSE_WAIT;
			}
			else if (tcp_ack) action = TCP_DATA;
			if (tcp_push) action = action | TCP_PUSH;
 			if (tcp_syn)  action = TCP_ACK + TCP_RST;

/*			tcp_hole_age( TCP_HOLE_AGE );

			if (*tcp_seq_num < tcp_next_expected_seq_num )
			tcp_hole_fill( *tcp_seq_num, payload_len );
			else
			{
*/				if (*tcp_seq_num > tcp_next_expected_seq_num )
				{
					tcp_hole_create( tcp_next_expected_seq_num, *tcp_seq_num );
					debug_cond(DEBUG_DCH_PKT,
						"TCP_ESTABLISHED Seq In %x, Exp %x, action %x\n",
						*tcp_seq_num, tcp_next_expected_seq_num, action);
				}
				if (*tcp_seq_num != tcp_next_expected_seq_num )
				net_set_state( NETLOOP_FAIL );
				else tcp_next_expected_seq_num = *tcp_seq_num + payload_len;
/*			}
*/
			debug_cond(DEBUG_TCP_PKT, "TCP_ESTABLISHED (%x), %d\n", action, *tcp_seq_num);
		break;
		case	TCP_CLOSE_WAIT:
			debug_cond(DEBUG_TCP_PKT, "TCP_CLOSE_WAIT (%x)\n", tcp_flags);
			action = TCP_DATA;					/* Wait for app	*/
		break;
		case	TCP_FIN_WAIT_2:
			debug_cond(DEBUG_TCP_PKT, "TCP_FIN_WAIT_2 (%x)\n", tcp_flags);
			if (tcp_fin)  action =  TCP_DATA;
			if (tcp_syn)  action =  TCP_DATA;
			if (tcp_ack) {action =  TCP_PUSH | TCP_ACK; tcp_state = TCP_CLOSED;}
		break;
		case	TCP_FIN_WAIT_1:
			debug_cond(DEBUG_DCH_PKT, "TCP_FIN_WAIT_1 (%x)\n", tcp_flags);
			if (tcp_fin)  action =  TCP_ACK | TCP_FIN; tcp_state = TCP_FIN_WAIT_2 ;
			if (tcp_syn)  action =  TCP_RST;
			if (tcp_ack) {tcp_state = TCP_CLOSED; *tcp_seq_num = *tcp_seq_num + 1;}
			net_set_state( NETLOOP_FAIL );
		break;
		case	TCP_CLOSING:
			debug_cond(DEBUG_TCP_PKT, "TCP_CLOSING (%x)\n", tcp_flags);
 			if (tcp_fin)  action = TCP_DATA;
			if (tcp_syn)  action = TCP_RST;
			if (tcp_ack) {action = TCP_DATA; tcp_state = TCP_CLOSED;}
		break;
	}
	return( action );
}

void rxhand_tcp_f( union tcp_build_pkt *b, unsigned pkt_len )
{
        int tcp_len 	= pkt_len - IP_HDR_SIZE;
	u16 tcp_rx_xsum = b->ip.hdr.ip_sum;
	u8  tcp_action 	= TCP_DATA;
	u8  net_action	= TCP_DATA;
	u32 tcp_seq_num;
	u32 tcp_ack_num;
	struct in_addr action_and_state;

	int tcp_hdr_len;
	int payload_len;

	/*
	 * Verify ip header
	*/
                debug_cond(DEBUG_TCP_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);

		debug_cond(DEBUG_TCP_PKT,
			"____________________________________________\n");



	b->ip.hdr.ip_src	= net_server_ip;
	b->ip.hdr.ip_dst	= net_ip;
	b->ip.hdr.ip_sum	= 0x0000;
        if (tcp_rx_xsum != compute_ip_checksum(b, IP_HDR_SIZE))
	{
		debug_cond(DEBUG_TCP_PKT,
			"TCP RX IP xum Error (to=%pI4, from=%pI4, len=%d)\n",
			&net_ip, &net_server_ip, pkt_len);
		return;
	}

	/*
	 * Build Pseudo header and Verify TCP header
	*/
	tcp_rx_xsum = b->ip.hdr.tcp_xsum;
	b->ip.hdr.tcp_xsum = 0x0000;
	if (tcp_rx_xsum != net_set_psuedo_header((uchar *)b, b->ip.hdr.ip_src,
				b->ip.hdr.ip_dst, tcp_len, pkt_len ))
	{
		debug_cond(DEBUG_TCP_PKT,
			"TCP RX TCP xSum Error (to=%pI4, from=%pI4, len=%d)\n",
			&net_ip, &net_server_ip, tcp_len);
		return;
	}


	tcp_hdr_len = ( b->ip.hdr.tcp_hlen >> 2);

        payload_len = tcp_len - tcp_hdr_len;

	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_action  = tcp_state_machine( b->ip.hdr.tcp_flags, &tcp_seq_num, payload_len );

        /*
         * State altering command to be sent.
	 * The packet sequence and ack numbers are in the tcp_seq_num and tcp_ack_num variables.
	 * The current packet, its position in the date stream, is the in the range of those variables.
	 *
	 * In the "application push" invocation the TCP header with all its information is pointed to by the
	 * packet pointer, and the other variable "repurposed" (or misused) to carry sequence numbers
	 * and  TCP state.
	 *
	 * TCP_PUSH from the state machine with a payload length of 0 is a connect or disconnect event
	*/

	if (( tcp_action && TCP_PUSH ) || ( payload_len > 0))
	{
		debug_cond(DEBUG_TCP_PKT,
                        "TCP App Notify (action=%x, Seq=%d, Ack=%d, Payload=%d)\n",
				tcp_action, tcp_seq_num, tcp_ack_num, payload_len );

		action_and_state.s_addr = tcp_action;
		(*tcp_packet_handler) ((uchar *) b + pkt_len - payload_len,
			tcp_seq_num, action_and_state, tcp_ack_num, payload_len );
	}
	else if ( tcp_action != TCP_DATA )
	{
		debug_cond(DEBUG_TCP_PKT,
			"TCP Net Action (action=%x, Seq=%d, Ack=%d,Payload=%d)\n",
                                tcp_action, tcp_seq_num, tcp_ack_num, payload_len );

	/*
	 * Warning Incoming sequence number are transposed here to TX sequence numbers
	*/

		net_action = (tcp_action & (~ TCP_PUSH));
		net_send_ip_packet( 0, IPPROTO_TCP, net_action, tcp_ack_num, tcp_seq_num );
	}
}

#endif

void net_init(void)
{
	static int first_call = 1;

	if (first_call) {
		/*
		 *	Setup packet wbuffers, aligned correctly.
		 */
		int i;

		net_tx_packet = &net_pkt_buf[0] + (PKTALIGN - 1);
		net_tx_packet -= (ulong)net_tx_packet % PKTALIGN;
		for (i = 0; i < PKTBUFSRX; i++) {
			net_rx_packets[i] = net_tx_packet +
				(i + 1) * PKTSIZE_ALIGN;
		}
		arp_init();
		net_clear_handlers();

		/* Only need to setup buffer pointers once. */
		first_call = 0;
#ifdef TCP
		tcp_state = TCP_CLOSED;
#endif
	}

	net_init_loop();
}

/**********************************************************************/
/*
 *	Main network processing loop.
 */

int net_loop(enum proto_t protocol)
{
	int ret = -EINVAL;

	net_restarted = 0;
	net_dev_exists = 0;
	net_try_count = 1;
	debug_cond(DEBUG_INT_STATE, "--- net_loop Entry\n");

	bootstage_mark_name(BOOTSTAGE_ID_ETH_START, "eth_start");
	net_init();
	if (eth_is_on_demand_init() || protocol != NETCONS) {
		eth_halt();
		eth_set_current();
		ret = eth_init();
		if (ret < 0) {
			eth_halt();
			return ret;
		}
	} else {
		eth_init_state_only();
	}
restart:
#ifdef CONFIG_USB_KEYBOARD
	net_busy_flag = 0;
#endif
	net_set_state(NETLOOP_CONTINUE);

	/*
	 *	Start the ball rolling with the given start function.  From
	 *	here on, this code is a state machine driven by received
	 *	packets and timer events.
	 */
	debug_cond(DEBUG_INT_STATE, "--- net_loop Init\n");
	net_init_loop();

	switch (net_check_prereq(protocol)) {
	case 1:
		/* network not configured */
		eth_halt();
		return -ENODEV;

	case 2:
		/* network device not configured */
		break;

	case 0:
		net_dev_exists = 1;
		net_boot_file_size = 0;
		switch (protocol) {
		case TFTPGET:
#ifdef CONFIG_CMD_TFTPPUT
		case TFTPPUT:
#endif
			/* always use ARP to get server ethernet address */
			tftp_start(protocol);
			break;
#ifdef CONFIG_CMD_TFTPSRV
		case TFTPSRV:
			tftp_start_server();
			break;
#endif
#if defined(CONFIG_CMD_DHCP)
		case DHCP:
			bootp_reset();
			net_ip.s_addr = 0;
			dhcp_request();		/* Basically same as BOOTP */
			break;
#endif

		case BOOTP:
			bootp_reset();
			net_ip.s_addr = 0;
			bootp_request();
			break;

#if defined(CONFIG_CMD_RARP)
		case RARP:
			rarp_try = 0;
			net_ip.s_addr = 0;
			rarp_request();
			break;
#endif
#if defined(CONFIG_CMD_PING)
		case PING:
			ping_start();
			break;
#endif
#if defined(CONFIG_CMD_NFS)
		case NFS:
			nfs_start();
			break;
#endif
#if defined(CONFIG_CMD_CDP)
		case CDP:
			cdp_start();
			break;
#endif
#if defined(CONFIG_NETCONSOLE) && !(CONFIG_SPL_BUILD)
		case NETCONS:
			nc_start();
			break;
#endif
#if defined(CONFIG_CMD_SNTP)
		case SNTP:
			sntp_start();
			break;
#endif
#if defined(CONFIG_CMD_DNS)
		case DNS:
			dns_start();
			break;
#endif
#if defined(CONFIG_CMD_LINK_LOCAL)
		case LINKLOCAL:
			link_local_start();
			break;
#endif
#ifdef TCP
//#if defined(CONFIG_CMD_WGET)
		case WGET:
			wget_start();
			break;
//#endif
#endif

		default:
			break;
		}

		break;
	}

#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
#if	defined(CONFIG_SYS_FAULT_ECHO_LINK_DOWN)	&& \
	defined(CONFIG_STATUS_LED)			&& \
	defined(STATUS_LED_RED)
	/*
	 * Echo the inverted link state to the fault LED.
	 */
	if (miiphy_link(eth_get_dev()->name, CONFIG_SYS_FAULT_MII_ADDR))
		status_led_set(STATUS_LED_RED, STATUS_LED_OFF);
	else
		status_led_set(STATUS_LED_RED, STATUS_LED_ON);
#endif /* CONFIG_SYS_FAULT_ECHO_LINK_DOWN, ... */
#endif /* CONFIG_MII, ... */
#ifdef CONFIG_USB_KEYBOARD
	net_busy_flag = 1;
#endif

	/*
	 *	Main packet reception loop.  Loop receiving packets until
	 *	someone sets `net_state' to a state that terminates.
	 */
	for (;;) {
		WATCHDOG_RESET();
#ifdef CONFIG_SHOW_ACTIVITY
		show_activity(1);
#endif
		if (arp_timeout_check() > 0)
			time_start = get_timer(0);

		/*
		 *	Check the ethernet for a new packet.  The ethernet
		 *	receive routine will process it.
		 *	Most drivers return the most recent packet size, but not
		 *	errors that may have happened.
		 */
		eth_rx();

		/*
		 *	Abort if ctrl-c was pressed.
		 */
		if (ctrlc()) {
			/* cancel any ARP that may not have completed */
			net_arp_wait_packet_ip.s_addr = 0;

			net_cleanup_loop();
			eth_halt();
			/* Invalidate the last protocol */
			eth_set_last_protocol(BOOTP);

			puts("\nAbort\n");
			/* include a debug print as well incase the debug
			   messages are directed to stderr */
			debug_cond(DEBUG_INT_STATE, "--- net_loop Abort!\n");
			ret = -EINTR;
			goto done;
		}

		/*
		 *	Check for a timeout, and run the timeout handler
		 *	if we have one.
		 */
		if (time_handler &&
		    ((get_timer(0) - time_start) > time_delta)) {
			thand_f *x;

#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
#if	defined(CONFIG_SYS_FAULT_ECHO_LINK_DOWN)	&& \
	defined(CONFIG_STATUS_LED)			&& \
	defined(STATUS_LED_RED)
			/*
			 * Echo the inverted link state to the fault LED.
			 */
			if (miiphy_link(eth_get_dev()->name,
					CONFIG_SYS_FAULT_MII_ADDR))
				status_led_set(STATUS_LED_RED, STATUS_LED_OFF);
			else
				status_led_set(STATUS_LED_RED, STATUS_LED_ON);
#endif /* CONFIG_SYS_FAULT_ECHO_LINK_DOWN, ... */
#endif /* CONFIG_MII, ... */
			debug_cond(DEBUG_INT_STATE, "--- net_loop timeout\n");
			x = time_handler;
			time_handler = (thand_f *)0;
			(*x)();
		}

		if (net_state == NETLOOP_FAIL)
			ret = net_start_again();

		switch (net_state) {
		case NETLOOP_RESTART:
			net_restarted = 1;
			goto restart;

		case NETLOOP_SUCCESS:
			net_cleanup_loop();
			if (net_boot_file_size > 0) {
				printf("Bytes transferred = %d (%x hex)\n",
				       net_boot_file_size, net_boot_file_size);
				setenv_hex("filesize", net_boot_file_size);
				setenv_hex("fileaddr", load_addr);
			}
			if (protocol != NETCONS)
				eth_halt();
			else
				eth_halt_state_only();

			eth_set_last_protocol(protocol);

			ret = net_boot_file_size;
			debug_cond(DEBUG_INT_STATE, "--- net_loop Success!\n");
			goto done;

		case NETLOOP_FAIL:
			net_cleanup_loop();
			/* Invalidate the last protocol */
			eth_set_last_protocol(BOOTP);
			debug_cond(DEBUG_INT_STATE, "--- net_loop Fail!\n");
			goto done;

		case NETLOOP_CONTINUE:
			continue;
		}
	}

done:
#ifdef CONFIG_USB_KEYBOARD
	net_busy_flag = 0;
#endif
#ifdef CONFIG_CMD_TFTPPUT
	/* Clear out the handlers */
	net_set_udp_handler(NULL);
	net_set_icmp_handler(NULL);
#endif
	return ret;
}

/**********************************************************************/

static void start_again_timeout_handler(void)
{
	net_set_state(NETLOOP_RESTART);
}

int net_start_again(void)
{
	char *nretry;
	int retry_forever = 0;
	unsigned long retrycnt = 0;
	int ret;

	nretry = getenv("netretry");
	if (nretry) {
		if (!strcmp(nretry, "yes"))
			retry_forever = 1;
		else if (!strcmp(nretry, "no"))
			retrycnt = 0;
		else if (!strcmp(nretry, "once"))
			retrycnt = 1;
		else
			retrycnt = simple_strtoul(nretry, NULL, 0);
	} else {
		retrycnt = 0;
		retry_forever = 0;
	}

	if ((!retry_forever) && (net_try_count >= retrycnt)) {
		eth_halt();
		net_set_state(NETLOOP_FAIL);
		/*
		 * We don't provide a way for the protocol to return an error,
		 * but this is almost always the reason.
		 */
		return -ETIMEDOUT;
	}

	net_try_count++;

	eth_halt();
#if !defined(CONFIG_NET_DO_NOT_TRY_ANOTHER)
	eth_try_another(!net_restarted);
#endif
	ret = eth_init();
	if (net_restart_wrap) {
		net_restart_wrap = 0;
		if (net_dev_exists) {
			net_set_timeout_handler(10000UL,
						start_again_timeout_handler);
			net_set_udp_handler(NULL);
		} else {
			net_set_state(NETLOOP_FAIL);
		}
	} else {
		net_set_state(NETLOOP_RESTART);
	}
	return ret;
}

/**********************************************************************/
/*
 *	Miscelaneous bits.
 */

static void dummy_handler(uchar *pkt, unsigned dport,
			struct in_addr sip, unsigned sport,
			unsigned len)
{
}

rxhand_f *net_get_udp_handler(void)
{
	return udp_packet_handler;
}

void	net_set_tcp_handler(rxhand_f *f)
{
        debug_cond(DEBUG_INT_STATE, "--- net_loop TCP handler set (%p)\n", f);
        if (f == NULL)
                tcp_packet_handler = dummy_handler;
        else
                tcp_packet_handler = f;

}

void net_set_udp_handler(rxhand_f *f)
{
	debug_cond(DEBUG_INT_STATE, "--- net_loop UDP handler set (%p)\n", f);
	if (f == NULL)
		udp_packet_handler = dummy_handler;
	else
		udp_packet_handler = f;
}

rxhand_f *net_get_arp_handler(void)
{
	return arp_packet_handler;
}

void net_set_arp_handler(rxhand_f *f)
{
	debug_cond(DEBUG_INT_STATE, "--- net_loop ARP handler set (%p)\n", f);
	if (f == NULL)
		arp_packet_handler = dummy_handler;
	else
		arp_packet_handler = f;
}

#ifdef CONFIG_CMD_TFTPPUT
void net_set_icmp_handler(rxhand_icmp_f *f)
{
	packet_icmp_handler = f;
}
#endif

void net_set_timeout_handler(ulong iv, thand_f *f)
{
	if (iv == 0) {
		debug_cond(DEBUG_INT_STATE,
			   "--- net_loop timeout handler cancelled\n");
		time_handler = (thand_f *)0;
	} else {
		debug_cond(DEBUG_INT_STATE,
			   "--- net_loop timeout handler set (%p)\n", f);
		time_handler = f;
		time_start = get_timer(0);
		time_delta = iv * CONFIG_SYS_HZ / 1000;
	}
}

int net_send_udp_packet(uchar *ether, struct in_addr dest, int dport, int sport,
                int payload_len)
{
//	net_send_udp_orig_packet( ether, dest, dport, sport, payload_len );
//	return(0);
	net_set_ports( dport, sport );
        return(net_send_ip_packet( payload_len, IPPROTO_UDP, IPPROTO_UDP, 0, 0));
}


#ifdef CONFIG_IP_DEFRAG
/*
 * This function collects fragments in a single packet, according
 * to the algorithm in RFC815. It returns NULL or the pointer to
 * a complete packet, in static storage
 */
#ifndef CONFIG_NET_MAXDEFRAG
#define CONFIG_NET_MAXDEFRAG 16384
#endif
#define IP_PKTSIZE (CONFIG_NET_MAXDEFRAG)

#define IP_MAXUDP (IP_PKTSIZE - IP_HDR_SIZE)

/*
 * this is the packet being assembled, either data or frag control.
 * Fragments go by 8 bytes, so this union must be 8 bytes long
 */
struct hole {
	/* first_byte is address of this structure */
	u16 last_byte;	/* last byte in this hole + 1 (begin of next hole) */
	u16 next_hole;	/* index of next (in 8-b blocks), 0 == none */
	u16 prev_hole;	/* index of prev, 0 == none */
	u16 unused;
};

static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp)
{
	static uchar pkt_buff[IP_PKTSIZE] __aligned(PKTALIGN);
	static u16 first_hole, total_len;
	struct hole *payload, *thisfrag, *h, *newh;
	struct ip_udp_hdr *localip = (struct ip_udp_hdr *)pkt_buff;
	uchar *indata = (uchar *)ip;
	int offset8, start, len, done = 0;
	u16 ip_off = ntohs(ip->ip_off);

	/* payload starts after IP header, this fragment is in there */
	payload = (struct hole *)(pkt_buff + IP_HDR_SIZE);
	offset8 =  (ip_off & IP_OFFS);
	thisfrag = payload + offset8;
	start = offset8 * 8;
	len = ntohs(ip->ip_len) - IP_HDR_SIZE;

	if (start + len > IP_MAXUDP) /* fragment extends too far */
		return NULL;

	if (!total_len || localip->ip_id != ip->ip_id) {
		/* new (or different) packet, reset structs */
		total_len = 0xffff;
		payload[0].last_byte = ~0;
		payload[0].next_hole = 0;
		payload[0].prev_hole = 0;
		first_hole = 0;
		/* any IP header will work, copy the first we received */
		memcpy(localip, ip, IP_HDR_SIZE);
	}

	/*
	 * What follows is the reassembly algorithm. We use the payload
	 * array as a linked list of hole descriptors, as each hole starts
	 * at a multiple of 8 bytes. However, last byte can be whatever value,
	 * so it is represented as byte count, not as 8-byte blocks.
	 */

	h = payload + first_hole;
	while (h->last_byte < start) {
		if (!h->next_hole) {
			/* no hole that far away */
			return NULL;
		}
		h = payload + h->next_hole;
	}

	/* last fragment may be 1..7 bytes, the "+7" forces acceptance */
	if (offset8 + ((len + 7) / 8) <= h - payload) {
		/* no overlap with holes (dup fragment?) */
		return NULL;
	}

	if (!(ip_off & IP_FLAGS_MFRAG)) {
		/* no more fragmentss: truncate this (last) hole */
		total_len = start + len;
		h->last_byte = start + len;
	}

	/*
	 * There is some overlap: fix the hole list. This code doesn't
	 * deal with a fragment that overlaps with two different holes
	 * (thus being a superset of a previously-received fragment).
	 */

	if ((h >= thisfrag) && (h->last_byte <= start + len)) {
		/* complete overlap with hole: remove hole */
		if (!h->prev_hole && !h->next_hole) {
			/* last remaining hole */
			done = 1;
		} else if (!h->prev_hole) {
			/* first hole */
			first_hole = h->next_hole;
			payload[h->next_hole].prev_hole = 0;
		} else if (!h->next_hole) {
			/* last hole */
			payload[h->prev_hole].next_hole = 0;
		} else {
			/* in the middle of the list */
			payload[h->next_hole].prev_hole = h->prev_hole;
			payload[h->prev_hole].next_hole = h->next_hole;
		}

	} else if (h->last_byte <= start + len) {
		/* overlaps with final part of the hole: shorten this hole */
		h->last_byte = start;

	} else if (h >= thisfrag) {
		/* overlaps with initial part of the hole: move this hole */
		newh = thisfrag + (len / 8);
		*newh = *h;
		h = newh;
		if (h->next_hole)
			payload[h->next_hole].prev_hole = (h - payload);
		if (h->prev_hole)
			payload[h->prev_hole].next_hole = (h - payload);
		else
			first_hole = (h - payload);

	} else {
		/* fragment sits in the middle: split the hole */
		newh = thisfrag + (len / 8);
		*newh = *h;
		h->last_byte = start;
		h->next_hole = (newh - payload);
		newh->prev_hole = (h - payload);
		if (newh->next_hole)
			payload[newh->next_hole].prev_hole = (newh - payload);
	}

	/* finally copy this fragment and possibly return whole packet */
	memcpy((uchar *)thisfrag, indata + IP_HDR_SIZE, len);
	if (!done)
		return NULL;

	localip->ip_len = htons(total_len);
	*lenp = total_len + IP_HDR_SIZE;
	return localip;
}

static inline struct ip_udp_hdr *net_defragment(struct ip_udp_hdr *ip,
	int *lenp)
{
	u16 ip_off = ntohs(ip->ip_off);
	if (!(ip_off & (IP_OFFS | IP_FLAGS_MFRAG)))
		return ip; /* not a fragment */
	return __net_defragment(ip, lenp);
}

#else /* !CONFIG_IP_DEFRAG */

static inline struct ip_udp_hdr *net_defragment(struct ip_udp_hdr *ip,
	int *lenp)
{
	u16 ip_off = ntohs(ip->ip_off);
	if (!(ip_off & (IP_OFFS | IP_FLAGS_MFRAG)))
		return ip; /* not a fragment */
	return NULL;
}
#endif

/**
 * Receive an ICMP packet. We deal with REDIRECT and PING here, and silently
 * drop others.
 *
 * @parma ip	IP packet containing the ICMP
 */
static void receive_icmp(struct ip_udp_hdr *ip, int len,
			struct in_addr src_ip, struct ethernet_hdr *et)
{
	struct icmp_hdr *icmph = (struct icmp_hdr *)&ip->udp_src;

	switch (icmph->type) {
	case ICMP_REDIRECT:
		if (icmph->code != ICMP_REDIR_HOST)
			return;
		printf(" ICMP Host Redirect to %pI4 ",
		       &icmph->un.gateway);
		break;
	default:
#if defined(CONFIG_CMD_PING)
		ping_receive(et, ip, len);
#endif
#ifdef CONFIG_CMD_TFTPPUT
		if (packet_icmp_handler)
			packet_icmp_handler(icmph->type, icmph->code,
					    ntohs(ip->udp_dst), src_ip,
					    ntohs(ip->udp_src), icmph->un.data,
					    ntohs(ip->udp_len));
#endif
		break;
	}
}

void net_process_received_packet(uchar *in_packet, int len)
{
	struct ethernet_hdr *et;
	struct ip_udp_hdr *ip;
	struct in_addr dst_ip;
	struct in_addr src_ip;
	int eth_proto;
#if defined(CONFIG_CMD_CDP)
	int iscdp;
#endif
	ushort cti = 0, vlanid = VLAN_NONE, myvlanid, mynvlanid;

	debug_cond(DEBUG_NET_PKT, "packet received\n");

	net_rx_packet = in_packet;
	net_rx_packet_len = len;
	et = (struct ethernet_hdr *)in_packet;

	/* too small packet? */
	if (len < ETHER_HDR_SIZE)
		return;

#if defined(CONFIG_API) || defined(CONFIG_EFI_LOADER)
	if (push_packet) {
		(*push_packet)(in_packet, len);
		return;
	}
#endif

#if defined(CONFIG_CMD_CDP)
	/* keep track if packet is CDP */
	iscdp = is_cdp_packet(et->et_dest);
#endif

	myvlanid = ntohs(net_our_vlan);
	if (myvlanid == (ushort)-1)
		myvlanid = VLAN_NONE;
	mynvlanid = ntohs(net_native_vlan);
	if (mynvlanid == (ushort)-1)
		mynvlanid = VLAN_NONE;

	eth_proto = ntohs(et->et_protlen);

	if (eth_proto < 1514) {
		struct e802_hdr *et802 = (struct e802_hdr *)et;
		/*
		 *	Got a 802.2 packet.  Check the other protocol field.
		 *	XXX VLAN over 802.2+SNAP not implemented!
		 */
		eth_proto = ntohs(et802->et_prot);

		ip = (struct ip_udp_hdr *)(in_packet + E802_HDR_SIZE);
		len -= E802_HDR_SIZE;

	} else if (eth_proto != PROT_VLAN) {	/* normal packet */
		ip = (struct ip_udp_hdr *)(in_packet + ETHER_HDR_SIZE);
		len -= ETHER_HDR_SIZE;

	} else {			/* VLAN packet */
		struct vlan_ethernet_hdr *vet =
			(struct vlan_ethernet_hdr *)et;

		debug_cond(DEBUG_NET_PKT, "VLAN packet received\n");

		/* too small packet? */
		if (len < VLAN_ETHER_HDR_SIZE)
			return;

		/* if no VLAN active */
		if ((ntohs(net_our_vlan) & VLAN_IDMASK) == VLAN_NONE
#if defined(CONFIG_CMD_CDP)
				&& iscdp == 0
#endif
				)
			return;

		cti = ntohs(vet->vet_tag);
		vlanid = cti & VLAN_IDMASK;
		eth_proto = ntohs(vet->vet_type);

		ip = (struct ip_udp_hdr *)(in_packet + VLAN_ETHER_HDR_SIZE);
		len -= VLAN_ETHER_HDR_SIZE;
	}

	debug_cond(DEBUG_NET_PKT, "Receive from protocol 0x%x\n", eth_proto);

#if defined(CONFIG_CMD_CDP)
	if (iscdp) {
		cdp_receive((uchar *)ip, len);
		return;
	}
#endif

	if ((myvlanid & VLAN_IDMASK) != VLAN_NONE) {
		if (vlanid == VLAN_NONE)
			vlanid = (mynvlanid & VLAN_IDMASK);
		/* not matched? */
		if (vlanid != (myvlanid & VLAN_IDMASK))
			return;
	}

	switch (eth_proto) {
	case PROT_ARP:
		arp_receive(et, ip, len);
		break;

#ifdef CONFIG_CMD_RARP
	case PROT_RARP:
		rarp_receive(ip, len);
		break;
#endif
	case PROT_IP:
		debug_cond(DEBUG_NET_PKT, "Got IP\n");
		/* Before we start poking the header, make sure it is there */
		if (len < IP_UDP_HDR_SIZE) {
			debug("len bad %d < %lu\n", len,
			      (ulong)IP_UDP_HDR_SIZE);
			return;
		}
		/* Check the packet length */
		if (len < ntohs(ip->ip_len)) {
			debug("len bad %d < %d\n", len, ntohs(ip->ip_len));
			return;
		}
		len = ntohs(ip->ip_len);
		debug_cond(DEBUG_NET_PKT, "len=%d, v=%02x\n",
			   len, ip->ip_hl_v & 0xff);

		/* Can't deal with anything except IPv4 */
		if ((ip->ip_hl_v & 0xf0) != 0x40)
			return;
		/* Can't deal with IP options (headers != 20 bytes) */
		if ((ip->ip_hl_v & 0x0f) > 0x05)
			return;
		/* Check the Checksum of the header */
		if (!ip_checksum_ok((uchar *)ip, IP_HDR_SIZE)) {
			debug("checksum bad\n");
			return;
		}
		/* If it is not for us, ignore it */
		dst_ip = net_read_ip(&ip->ip_dst);
		if (net_ip.s_addr && dst_ip.s_addr != net_ip.s_addr &&
		    dst_ip.s_addr != 0xFFFFFFFF) {
#ifdef CONFIG_MCAST_TFTP
			if (net_mcast_addr != dst_ip)
#endif
				return;
		}
		/* Read source IP address for later use */
		src_ip = net_read_ip(&ip->ip_src);
		/*
		 * The function returns the unchanged packet if it's not
		 * a fragment, and either the complete packet or NULL if
		 * it is a fragment (if !CONFIG_IP_DEFRAG, it returns NULL)
		 */
		ip = net_defragment(ip, &len);
		if (!ip)
			return;
		/*
		 * watch for ICMP host redirects
		 *
		 * There is no real handler code (yet). We just watch
		 * for ICMP host redirect messages. In case anybody
		 * sees these messages: please contact me
		 * (wd at denx.de), or - even better - send me the
		 * necessary fixes :-)
		 *
		 * Note: in all cases where I have seen this so far
		 * it was a problem with the router configuration,
		 * for instance when a router was configured in the
		 * BOOTP reply, but the TFTP server was on the same
		 * subnet. So this is probably a warning that your
		 * configuration might be wrong. But I'm not really
		 * sure if there aren't any other situations.
		 *
		 * Simon Glass <sjg@chromium.org>: We get an ICMP when
		 * we send a tftp packet to a dead connection, or when
		 * there is no server at the other end.
		 */
		if (ip->ip_p == IPPROTO_ICMP) {
			receive_icmp(ip, len, src_ip, et);
			return;
		} else if (ip->ip_p == IPPROTO_UDP) {	/* Only UDP packets */

			debug_cond(DEBUG_DEV_PKT,
			   "received UDP (to=%pI4, from=%pI4, len=%d)\n",
			   &dst_ip, &src_ip, len);

#ifdef CONFIG_UDP_CHECKSUM
			if (ip->udp_xsum != 0) {
				ulong   xsum;
				ushort *sumptr;
				ushort  sumlen;

				xsum  = ip->ip_p;
				xsum += (ntohs(ip->udp_len));
				xsum += (ntohl(ip->ip_src.s_addr) >> 16) & 0x0000ffff;
				xsum += (ntohl(ip->ip_src.s_addr) >>  0) & 0x0000ffff;
				xsum += (ntohl(ip->ip_dst.s_addr) >> 16) & 0x0000ffff;
				xsum += (ntohl(ip->ip_dst.s_addr) >>  0) & 0x0000ffff;

				sumlen = ntohs(ip->udp_len);
				sumptr = (ushort *)&(ip->udp_src);

				while (sumlen > 1) {
					ushort sumdata;

					sumdata = *sumptr++;
					xsum += ntohs(sumdata);
					sumlen -= 2;
				}
				if (sumlen > 0) {
					ushort sumdata;

					sumdata = *(unsigned char *)sumptr;
					sumdata = (sumdata << 8) & 0xff00;
					xsum += sumdata;
				}
				while ((xsum >> 16) != 0) {
					xsum = (xsum & 0x0000ffff) +
					       ((xsum >> 16) & 0x0000ffff);
				}
				if ((xsum != 0x00000000) && (xsum != 0x0000ffff)) {
					printf(" UDP wrong checksum %08lx %08x\n",
					       xsum, ntohs(ip->udp_xsum));
					return;
				}
			}
#endif

#if defined(CONFIG_NETCONSOLE) && !(CONFIG_SPL_BUILD)
			nc_input_packet((uchar *)ip + IP_UDP_HDR_SIZE,
					src_ip,
					ntohs(ip->udp_dst),
					ntohs(ip->udp_src),
					ntohs(ip->udp_len) - UDP_HDR_SIZE);
#endif
			/*
		 	* IP header OK.  Pass the packet to the current handler.
		 	*/
			debug_cond(DEBUG_DEV_PKT,
                           "UDP PH (to=%pI4, from=%pI4, len=%d)\n",
                           &dst_ip, &src_ip, len);
			(*udp_packet_handler)((uchar *)ip + IP_UDP_HDR_SIZE,
				      ntohs(ip->udp_dst),
				      src_ip,
				      ntohs(ip->udp_src),
				      ntohs(ip->udp_len) - UDP_HDR_SIZE);

                }
#ifdef TCP
		else if (ip->ip_p == IPPROTO_TCP) {   /* Only TCP packets */


			debug_cond(DEBUG_DEV_PKT, 
				"TCP PH (to=%pI4, from=%pI4, len=%d)\n",
				&dst_ip, &src_ip, len);

			rxhand_tcp_f((union tcp_build_pkt *) ip, len);

                }
#endif

	}
}

/**********************************************************************/

static int net_check_prereq(enum proto_t protocol)
{
	switch (protocol) {
		/* Fall through */
#if defined(CONFIG_CMD_PING)
	case PING:
		if (net_ping_ip.s_addr == 0) {
			puts("*** ERROR: ping address not given\n");
			return 1;
		}
		goto common;
#endif
#if defined(CONFIG_CMD_SNTP)
	case SNTP:
		if (net_ntp_server.s_addr == 0) {
			puts("*** ERROR: NTP server address not given\n");
			return 1;
		}
		goto common;
#endif
#if defined(CONFIG_CMD_DNS)
	case DNS:
		if (net_dns_server.s_addr == 0) {
			puts("*** ERROR: DNS server address not given\n");
			return 1;
		}
		goto common;
#endif
#if defined(CONFIG_CMD_NFS)
	case NFS:
#endif
		/* Fall through */
	case TFTPGET:
	case TFTPPUT:
		if (net_server_ip.s_addr == 0) {
			puts("*** ERROR: `serverip' not set\n");
			return 1;
		}
#if	defined(CONFIG_CMD_PING) || defined(CONFIG_CMD_SNTP) || \
	defined(CONFIG_CMD_DNS)
common:
#endif
		/* Fall through */

	case NETCONS:
	case TFTPSRV:
		if (net_ip.s_addr == 0) {
			puts("*** ERROR: `ipaddr' not set\n");
			return 1;
		}
		/* Fall through */

#ifdef CONFIG_CMD_RARP
	case RARP:
#endif
	case BOOTP:
	case CDP:
	case DHCP:
	case LINKLOCAL:
		if (memcmp(net_ethaddr, "\0\0\0\0\0\0", 6) == 0) {
			int num = eth_get_dev_index();

			switch (num) {
			case -1:
				puts("*** ERROR: No ethernet found.\n");
				return 1;
			case 0:
				puts("*** ERROR: `ethaddr' not set\n");
				break;
			default:
				printf("*** ERROR: `eth%daddr' not set\n",
				       num);
				break;
			}

			net_start_again();
			return 2;
		}
		/* Fall through */
	default:
		return 0;
	}
	return 0;		/* OK */
}
/**********************************************************************/

int
net_eth_hdr_size(void)
{
	ushort myvlanid;

	myvlanid = ntohs(net_our_vlan);
	if (myvlanid == (ushort)-1)
		myvlanid = VLAN_NONE;

	return ((myvlanid & VLAN_IDMASK) == VLAN_NONE) ? ETHER_HDR_SIZE :
		VLAN_ETHER_HDR_SIZE;
}

int net_set_ether(uchar *xet, const uchar *dest_ethaddr, uint prot)
{
	struct ethernet_hdr *et = (struct ethernet_hdr *)xet;
	ushort myvlanid;

	myvlanid = ntohs(net_our_vlan);
	if (myvlanid == (ushort)-1)
		myvlanid = VLAN_NONE;

	memcpy(et->et_dest, dest_ethaddr, 6);
	memcpy(et->et_src, net_ethaddr, 6);
	if ((myvlanid & VLAN_IDMASK) == VLAN_NONE) {
		et->et_protlen = htons(prot);
		return ETHER_HDR_SIZE;
	} else {
		struct vlan_ethernet_hdr *vet =
			(struct vlan_ethernet_hdr *)xet;

		vet->vet_vlan_type = htons(PROT_VLAN);
		vet->vet_tag = htons((0 << 5) | (myvlanid & VLAN_IDMASK));
		vet->vet_type = htons(prot);
		return VLAN_ETHER_HDR_SIZE;
	}
}

int net_update_ether(struct ethernet_hdr *et, uchar *addr, uint prot)
{
	ushort protlen;

	memcpy(et->et_dest, addr, 6);
	memcpy(et->et_src, net_ethaddr, 6);
	protlen = ntohs(et->et_protlen);
	if (protlen == PROT_VLAN) {
		struct vlan_ethernet_hdr *vet =
			(struct vlan_ethernet_hdr *)et;
		vet->vet_type = htons(prot);
		return VLAN_ETHER_HDR_SIZE;
	} else if (protlen > 1514) {
		et->et_protlen = htons(prot);
		return ETHER_HDR_SIZE;
	} else {
		/* 802.2 + SNAP */
		struct e802_hdr *et802 = (struct e802_hdr *)et;
		et802->et_prot = htons(prot);
		return E802_HDR_SIZE;
	}
}

void net_set_ip_header(uchar *pkt, struct in_addr dest, struct in_addr source,
						u16  pkt_len, u8 prot)
{
	struct ip_hdr *ip = (struct ip_hdr *)pkt;

	/*
	 *	Construct an IP header.
	 */
	/* IP_HDR_SIZE / 4 (not including UDP) */
	ip->ip_hl_v  = 0x45;
	ip->ip_tos   = 0;
	ip->ip_len   = htons(pkt_len);
	ip->ip_id    = htons(net_ip_id++);
	ip->ip_off   = htons(IP_FLAGS_DFRAG);	/* Don't fragment */
	ip->ip_ttl   = 255;
	ip->ip_p     = prot;
	ip->ip_sum   = 0;
	/* already in network byte order */
	net_copy_ip((void *)&ip->ip_src, &source);
	/* already in network byte order */
	net_copy_ip((void *)&ip->ip_dst, &dest);
        ip->ip_sum   = compute_ip_checksum(ip, IP_HDR_SIZE);
}

void net_set_udp_header(uchar *pkt, struct in_addr dest, int dport, int sport,
			int len)
{
	struct ip_udp_hdr *ip = (struct ip_udp_hdr *)pkt;

	/*
	 *	If the data is an odd number of bytes, zero the
	 *	byte after the last byte so that the checksum
	 *	will work.
	 */
	if (len & 1)
		pkt[IP_UDP_HDR_SIZE + len] = 0;

	net_set_ip_header(pkt, dest, net_ip, IP_UDP_HDR_SIZE + len, IPPROTO_UDP);

	ip->udp_src  = htons(sport);
	ip->udp_dst  = htons(dport);
	ip->udp_len  = htons(UDP_HDR_SIZE + len);
	ip->udp_xsum = 0;
}

void copy_filename(char *dst, const char *src, int size)
{
	if (*src && (*src == '"')) {
		++src;
		--size;
	}

	while ((--size > 0) && *src && (*src != '"'))
		*dst++ = *src++;
	*dst = '\0';
}

#if	defined(CONFIG_CMD_NFS)		|| \
	defined(CONFIG_CMD_SNTP)	|| \
	defined(CONFIG_CMD_DNS)		|| \
	defined(CONFIG_CMD_WGET)
/*
 * make port a little random (1024-17407)
 * This keeps the math somewhat trivial to compute, and seems to work with
 * all supported protocols/clients/servers
 */
unsigned int random_port(void)
{
	return 1024 + (get_timer(0) % 0x4000);
}
#endif

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

	if (x == (ushort)-1)
		x = VLAN_NONE;

	if (x == VLAN_NONE)
		strcpy(s, "none");
	else
		sprintf(s, "%d", x & VLAN_IDMASK);
}

ushort string_to_vlan(const char *s)
{
	ushort id;

	if (s == NULL)
		return htons(VLAN_NONE);

	if (*s < '0' || *s > '9')
		id = VLAN_NONE;
	else
		id = (ushort)simple_strtoul(s, NULL, 10);

	return htons(id);
}

ushort getenv_vlan(char *var)
{
	return string_to_vlan(getenv(var));
}include/net.h
 /*
 *	LiMon Monitor (LiMon) - Network.
 *
 *	Copyright 1994 - 2000 Neil Russell.
 *	(See License)
 *	SPDX-License-Identifier:	GPL-2.0
 *
 * History
 *	9/16/00	  bor  adapted to TQM823L/STK8xxL board, RARP/TFTP boot added
 * 	9/20/2017 dch	Added TCP listener
 */

#ifndef __NET_H__
#define __NET_H__

#if defined(CONFIG_8xx)
#include <commproc.h>
#endif	/* CONFIG_8xx */

#include <asm/cache.h>
#include <asm/byteorder.h>	/* for nton* / ntoh* stuff */
#define DEBUG_LL_STATE 0	/* Link local state machine changes */
#define DEBUG_DEV_PKT 0		/* Packets or info directed to the device */
#define DEBUG_NET_PKT 0		/* Packets on info on the network at large */
#define DEBUG_INT_STATE 0	/* Internal network state changes */

/*
 *	The number of receive packet buffers, and the required packet buffer
 *	alignment in memory.
 *
 */

#define TCP 1

#ifdef TCP
#define CONFIG_SYS_RX_ETH_BUFFER 50	/* For TCP */
#endif

#ifdef CONFIG_SYS_RX_ETH_BUFFER
# define PKTBUFSRX	CONFIG_SYS_RX_ETH_BUFFER
#else
# define PKTBUFSRX	4
#endif

#define PKTALIGN	ARCH_DMA_MINALIGN

/* IPv4 addresses are always 32 bits in size */
struct in_addr {
	__be32 s_addr;
};

/**
 * An incoming packet handler.
 * @param pkt    pointer to the application packet
 * @param dport  destination UDP port
 * @param sip    source IP address
 * @param sport  source UDP port
 * @param len    packet length
 */
typedef void rxhand_f(uchar *pkt, unsigned dport,
		      struct in_addr sip, unsigned sport,
		      unsigned len);

/**
 * An incoming ICMP packet handler.
 * @param type	ICMP type
 * @param code	ICMP code
 * @param dport	destination UDP port
 * @param sip	source IP address
 * @param sport	source UDP port
 * @param pkt	pointer to the ICMP packet data
 * @param len	packet length
 */
typedef void rxhand_icmp_f(unsigned type, unsigned code, unsigned dport,
		struct in_addr sip, unsigned sport, uchar *pkt, unsigned len);

/*
 *	A timeout handler.  Called after time interval has expired.
 */
typedef void	thand_f(void);

enum eth_state_t {
	ETH_STATE_INIT,
	ETH_STATE_PASSIVE,
	ETH_STATE_ACTIVE
};

#ifdef CONFIG_DM_ETH
/**
 * struct eth_pdata - Platform data for Ethernet MAC controllers
 *
 * @iobase: The base address of the hardware registers
 * @enetaddr: The Ethernet MAC address that is loaded from EEPROM or env
 * @phy_interface: PHY interface to use - see PHY_INTERFACE_MODE_...
 * @max_speed: Maximum speed of Ethernet connection supported by MAC
 */
struct eth_pdata {
	phys_addr_t iobase;
	unsigned char enetaddr[6];
	int phy_interface;
	int max_speed;
};

enum eth_recv_flags {
	/*
	 * Check hardware device for new packets (otherwise only return those
	 * which are already in the memory buffer ready to process)
	 */
	ETH_RECV_CHECK_DEVICE		= 1 << 0,
};

/**
 * struct eth_ops - functions of Ethernet MAC controllers
 *
 * start: Prepare the hardware to send and receive packets
 * send: Send the bytes passed in "packet" as a packet on the wire
 * recv: Check if the hardware received a packet. If so, set the pointer to the
 *	 packet buffer in the packetp parameter. If not, return an error or 0 to
 *	 indicate that the hardware receive FIFO is empty. If 0 is returned, the
 *	 network stack will not process the empty packet, but free_pkt() will be
 *	 called if supplied
 * free_pkt: Give the driver an opportunity to manage its packet buffer memory
 *	     when the network stack is finished processing it. This will only be
 *	     called when no error was returned from recv - optional
 * stop: Stop the hardware from looking for packets - may be called even if
 *	 state == PASSIVE
 * mcast: Join or leave a multicast group (for TFTP) - optional
 * write_hwaddr: Write a MAC address to the hardware (used to pass it to Linux
 *		 on some platforms like ARM). This function expects the
 *		 eth_pdata::enetaddr field to be populated. The method can
 *		 return -ENOSYS to indicate that this is not implemented for
		 this hardware - optional.
 * read_rom_hwaddr: Some devices have a backup of the MAC address stored in a
 *		    ROM on the board. This is how the driver should expose it
 *		    to the network stack. This function should fill in the
 *		    eth_pdata::enetaddr field - optional
 */
struct eth_ops {
	int (*start)(struct udevice *dev);
	int (*send)(struct udevice *dev, void *packet, int length);
	int (*recv)(struct udevice *dev, int flags, uchar **packetp);
	int (*free_pkt)(struct udevice *dev, uchar *packet, int length);
	void (*stop)(struct udevice *dev);
#ifdef CONFIG_MCAST_TFTP
	int (*mcast)(struct udevice *dev, const u8 *enetaddr, int join);
#endif
	int (*write_hwaddr)(struct udevice *dev);
	int (*read_rom_hwaddr)(struct udevice *dev);
};

#define eth_get_ops(dev) ((struct eth_ops *)(dev)->driver->ops)

struct udevice *eth_get_dev(void); /* get the current device */
/*
 * The devname can be either an exact name given by the driver or device tree
 * or it can be an alias of the form "eth%d"
 */
struct udevice *eth_get_dev_by_name(const char *devname);
unsigned char *eth_get_ethaddr(void); /* get the current device MAC */

/* Used only when NetConsole is enabled */
int eth_is_active(struct udevice *dev); /* Test device for active state */
int eth_init_state_only(void); /* Set active state */
void eth_halt_state_only(void); /* Set passive state */
#endif

#ifndef CONFIG_DM_ETH
struct eth_device {
	char name[16];
	unsigned char enetaddr[6];
	phys_addr_t iobase;
	int state;

	int (*init)(struct eth_device *, bd_t *);
	int (*send)(struct eth_device *, void *packet, int length);
	int (*recv)(struct eth_device *);
	void (*halt)(struct eth_device *);
#ifdef CONFIG_MCAST_TFTP
	int (*mcast)(struct eth_device *, const u8 *enetaddr, u8 set);
#endif
	int (*write_hwaddr)(struct eth_device *);
	struct eth_device *next;
	int index;
	void *priv;
};

int eth_register(struct eth_device *dev);/* Register network device */
int eth_unregister(struct eth_device *dev);/* Remove network device */

extern struct eth_device *eth_current;

static __always_inline struct eth_device *eth_get_dev(void)
{
	return eth_current;
}
struct eth_device *eth_get_dev_by_name(const char *devname);
struct eth_device *eth_get_dev_by_index(int index); /* get dev @ index */

/* get the current device MAC */
static inline unsigned char *eth_get_ethaddr(void)
{
	if (eth_current)
		return eth_current->enetaddr;
	return NULL;
}

/* Used only when NetConsole is enabled */
int eth_is_active(struct eth_device *dev); /* Test device for active state */
/* Set active state */
static __always_inline int eth_init_state_only(void)
{
	eth_get_dev()->state = ETH_STATE_ACTIVE;

	return 0;
}
/* Set passive state */
static __always_inline void eth_halt_state_only(void)
{
	eth_get_dev()->state = ETH_STATE_PASSIVE;
}

/*
 * Set the hardware address for an ethernet interface based on 'eth%daddr'
 * environment variable (or just 'ethaddr' if eth_number is 0).
 * Args:
 *	base_name - base name for device (normally "eth")
 *	eth_number - value of %d (0 for first device of this type)
 * Returns:
 *	0 is success, non-zero is error status from driver.
 */
int eth_write_hwaddr(struct eth_device *dev, const char *base_name,
		     int eth_number);

int usb_eth_initialize(bd_t *bi);
#endif

int eth_initialize(void);		/* Initialize network subsystem */
void eth_try_another(int first_restart);	/* Change the device */
void eth_set_current(void);		/* set nterface to ethcur var */

int eth_get_dev_index(void);		/* get the device index */
void eth_parse_enetaddr(const char *addr, uchar *enetaddr);
int eth_getenv_enetaddr(const char *name, uchar *enetaddr);
int eth_setenv_enetaddr(const char *name, const uchar *enetaddr);

/**
 * eth_setenv_enetaddr_by_index() - set the MAC address environment variable
 *
 * This sets up an environment variable with the given MAC address (@enetaddr).
 * The environment variable to be set is defined by <@base_name><@index>addr.
 * If @index is 0 it is omitted. For common Ethernet this means ethaddr,
 * eth1addr, etc.
 *
 * @base_name:  Base name for variable, typically "eth"
 * @index:      Index of interface being updated (>=0)
 * @enetaddr:   Pointer to MAC address to put into the variable
 * @return 0 if OK, other value on error
 */
int eth_setenv_enetaddr_by_index(const char *base_name, int index,
				 uchar *enetaddr);


/*
 * Get the hardware address for an ethernet interface .
 * Args:
 *	base_name - base name for device (normally "eth")
 *	index - device index number (0 for first)
 *	enetaddr - returns 6 byte hardware address
 * Returns:
 *	Return true if the address is valid.
 */
int eth_getenv_enetaddr_by_index(const char *base_name, int index,
				 uchar *enetaddr);

int eth_init(void);			/* Initialize the device */
int eth_send(void *packet, int length);	   /* Send a packet */

#if defined(CONFIG_API) || defined(CONFIG_EFI_LOADER)
int eth_receive(void *packet, int length); /* Receive a packet*/
extern void (*push_packet)(void *packet, int length);
#endif
int eth_rx(void);			/* Check for received packets */
void eth_halt(void);			/* stop SCC */
const char *eth_get_name(void);		/* get name of current device */

#ifdef CONFIG_MCAST_TFTP
int eth_mcast_join(struct in_addr mcast_addr, int join);
u32 ether_crc(size_t len, unsigned char const *p);
#endif


/**********************************************************************/
/*
 *	Protocol headers.
 */

/*
 *	Ethernet header
 */

struct ethernet_hdr {
	u8		et_dest[6];	/* Destination node		*/
	u8		et_src[6];	/* Source node			*/
	u16		et_protlen;	/* Protocol or length		*/
};

/* Ethernet header size */
#define ETHER_HDR_SIZE	(sizeof(struct ethernet_hdr))

#define ETH_FCS_LEN	4		/* Octets in the FCS		*/

struct e802_hdr {
	u8		et_dest[6];	/* Destination node		*/
	u8		et_src[6];	/* Source node			*/
	u16		et_protlen;	/* Protocol or length		*/
	u8		et_dsap;	/* 802 DSAP			*/
	u8		et_ssap;	/* 802 SSAP			*/
	u8		et_ctl;		/* 802 control			*/
	u8		et_snap1;	/* SNAP				*/
	u8		et_snap2;
	u8		et_snap3;
	u16		et_prot;	/* 802 protocol			*/
}__attribute__ ((packed));

/* 802 + SNAP + ethernet header size */
#define E802_HDR_SIZE	(sizeof(struct e802_hdr))

/*
 *	Virtual LAN Ethernet header
 */
struct vlan_ethernet_hdr {
	u8		vet_dest[6];	/* Destination node		*/
	u8		vet_src[6];	/* Source node			*/
	u16		vet_vlan_type;	/* PROT_VLAN			*/
	u16		vet_tag;	/* TAG of VLAN			*/
	u16		vet_type;	/* protocol type		*/
}__attribute__ ((packed));


/* VLAN Ethernet header size */
#define VLAN_ETHER_HDR_SIZE	(sizeof(struct vlan_ethernet_hdr))

#define PROT_IP		0x0800		/* IP protocol			*/
#define PROT_ARP	0x0806		/* IP ARP protocol		*/
#define PROT_RARP	0x8035		/* IP ARP protocol		*/
#define PROT_VLAN	0x8100		/* IEEE 802.1q protocol		*/
#define PROT_IPV6	0x86dd		/* IPv6 over bluebook		*/
#define PROT_PPP_SES	0x8864		/* PPPoE session messages	*/

#define IPPROTO_ICMP	 1	/* Internet Control Message Protocol	*/
#define IPPROTO_TCP      6      /* Transmission Control Protocol	*/
#define IPPROTO_UDP	17	/* User Datagram Protocol		*/

/*
 *	Internet Protocol (IP) header.
 */
struct ip_hdr {
	u8		ip_hl_v;	/* header length and version	*/
	u8		ip_tos;		/* type of service		*/
	u16		ip_len;		/* total length			*/
	u16		ip_id;		/* identification		*/
	u16		ip_off;		/* fragment offset field	*/
	u8		ip_ttl;		/* time to live			*/
	u8		ip_p;		/* protocol			*/
	u16		ip_sum;		/* checksum			*/
	struct in_addr	ip_src;		/* Source IP address		*/
	struct in_addr	ip_dst;		/* Destination IP address	*/
}__attribute__ ((packed));

#define IP_OFFS		0x1fff /* ip offset *= 8 */
#define IP_FLAGS	0xe000 /* first 3 bits */
#define IP_FLAGS_RES	0x8000 /* reserved */
#define IP_FLAGS_DFRAG	0x4000 /* don't fragment */
#define IP_FLAGS_MFRAG	0x2000 /* more fragments */

#define IP_HDR_SIZE		(sizeof(struct ip_hdr))

/*
 *	Internet Protocol (IP) + UDP header.
 */
struct ip_udp_hdr {
	u8		ip_hl_v;	/* header length and version	*/
	u8		ip_tos;		/* type of service		*/
	u16		ip_len;		/* total length			*/
	u16		ip_id;		/* identification		*/
					/* Flags first 3 bits of ip_off */
	u16		ip_off;		/* fragment offset field	*/
	u8		ip_ttl;		/* time to live			*/
	u8		ip_p;		/* protocol			*/
	u16		ip_sum;		/* checksum			*/
	struct in_addr	ip_src;		/* Source IP address		*/
	struct in_addr	ip_dst;		/* Destination IP address	*/
	u16		udp_src;	/* UDP source port		*/
	u16		udp_dst;	/* UDP destination port		*/
	u16		udp_len;	/* Length of UDP packet		*/
	u16		udp_xsum;	/* Checksum			*/
}__attribute__ ((packed));

#define IP_UDP_HDR_SIZE		(sizeof(struct ip_udp_hdr))
#define UDP_HDR_SIZE		(IP_UDP_HDR_SIZE - IP_HDR_SIZE)

/*
 *      Internet Protocol (IP) + TCP header.
*/
struct ip_tcp_hdr {
        u8              ip_hl_v;        /* header length and version    */
        u8              ip_tos;         /* type of service              */
        u16             ip_len;         /* total length                 */
        u16             ip_id;          /* identification               */
        u16             ip_off;         /* fragment offset field        */
        u8              ip_ttl;         /* time to live                 */
        u8              ip_p;           /* protocol                     */
        u16             ip_sum;         /* checksum                     */
        struct in_addr  ip_src;         /* Source IP address            */
        struct in_addr  ip_dst;         /* Destination IP address       */
        u16             tcp_src;	/* TCP source port              */
        u16             tcp_dst;	/* TCP destination port         */
        u32             tcp_seq;        /* TCP sequence number          */
        u32             tcp_ack;        /* TCP Acknowledgement number   */
        u8 		tcp_hlen;	/* 4 bits TCP header Length/4	*/
                       			/* 4 bits Reserved              */
					/* 2 more bits reserver		*/
	u8		tcp_flags;	/* see defines			*/
        u16             tcp_win;        /* TCP windows size             */
        u16             tcp_xsum;       /* Checksum                     */
        u16             tcp_ugr;        /* Pointer to urgent data       */
}__attribute__ ((packed));

#define IP_TCP_HDR_SIZE		(sizeof(struct ip_tcp_hdr))
#define TCP_HDR_SIZE		(IP_TCP_HDR_SIZE  - IP_HDR_SIZE)

#define TCP_DATA     0x00	/* Data Packet - internal use only	*/
#define TCP_FIN      0x01	/* Finish flag				*/
#define TCP_SYN      0x02	/* start flag				*/
#define TCP_RST      0x04	/* reset flag				*/
#define TCP_PUSH     0x08	/* Push - Notify app			*/
#define TCP_ACK      0x10	/* Acknowledgement of data received	*/
#define TCP_URG      0x20	/* Urgent				*/
#define TCP_ECE      0x40	/* Unknown				*/
#define TCP_CWR      0x80	/* Unknown				*/

/*
 * TCP header options, Seq, MSS, and SACK
*/

#define TCP_O_END  0x00			/* End of option list		*/
#define TCP_1_NOP  0x01			/* Single padding NOP		*/
#define TCP_O_NOP  0x01010101		/* NOPs pad to 32 bit boundary	*/
#define	TCP_O_MSS  0x02			/* MSS Size option		*/
#define TCP_O_SCL  0x03			/* Window Scale option		*/
#define TCP_P_SACK 0x04			/* SACK permitted		*/
#define TCP_V_SACK 0x05			/* SACK values			*/
#define TCP_O_TS   0x08			/* Timestanp option		*/
#define TCP_OPT_LEN_2 0x02
#define TCP_OPT_LEN_3 0x03
#define TCP_OPT_LEN_4 0x04
#define TCP_OPT_LEN_6 0x06
#define TCP_OPT_LEN_8 0x08
#define TCP_OPT_LEN_A 0x0a		/* TimestampLength              */



#define TCP_MSS   1460			/* Max segment size - 1460 	*/
#define TCP_SCALE 0x01			/* Scale 1			*/

struct tcp_mss{				/* TCP Mex Segment size		*/
	u8	kind	;		/* 0x02				*/
	u8	len;			/* 0x04				*/
	u16	mss;			/* 1460 - Max segment size	*/
}__attribute__ ((packed));

struct tcp_scale {			/* TCP Windows Scale		*/
	u8 	kind;			/* 0x03				*/
	u8	len;			/* 0x03				*/
	u8	scale;			/* win shift fat fast networks	*/
}__attribute__ ((packed));

struct tcp_sack_p {			/* SACK permitted		*/
	u8	kind;			/* 0x04				*/
	u8	len;			/* Length			*/
}__attribute__ ((packed));

struct sack_edges {
	u32	l;
	u32	r;
}__attribute__ ((packed));

#define TCP_STREAM_HOLES		16

#define TCP_SAK_HOLES			3

#define TCP_HOLE_AGE 			4

struct tcp_sack_v {
	u8	kind;			/* 0x05				*/
	u8      len;			/* Length			*/
	struct sack_edges hole[TCP_SAK_HOLES];	/* L & R widow edges	*/
}__attribute__ ((packed));

struct tcp_TSopt{			/* TCP time stamps option	*/
        u8      kind;                   /* 0x08                         */
	u8	len;			/* 0x0a				*/
 	u32	TSsnd;			/* Sender timestamp		*/
	u32	TSrcv;			/* Receiver timestamp		*/
}__attribute__ ((packed));

#define TCP_TSOPT_SIZE		(sizeof(struct tcp_TSopt))

/*
 * ip tcp  structure with options
*/

struct ip_tcp_hdr_o {
        struct	ip_tcp_hdr 	hdr;
	struct	tcp_mss		mss;
	struct	tcp_scale	scale;
	struct	tcp_sack_p	sack_p;
	struct	tcp_TSopt	TSopt;
	u8	end;
}__attribute__ ((packed));

#define IP_TCP_O_SIZE		(sizeof(struct ip_tcp_hdr_o))

struct ip_tcp_hdr_s {
	struct  ip_tcp_hdr	hdr;
	struct  tcp_TSopt       TSopt;
        struct  tcp_sack_v	sack_v;
        u8	end;
}__attribute__ ((packed));

#define IP_TCP_SACK_SIZE	(sizeof(struct ip_tcp_hdr_s))

/*
 * TCP psuedo header definitions
*/

struct psuedo_hdr{
        u8 padding[8];			/* psuedo header size = ip_tcp hdr size	*/
        struct in_addr p_src;
        struct in_addr p_dst;
        u8      rsvd;
        u8      p;
        u16     len;
}__attribute__ ((packed));

#define PSUEDO_PAD_SIZE         8
#define PSUEDO_HDR_SIZE         12

/*
 * union for building IP/TCP packet.
 * build Psuedo header in packed bufferfirst, calculate TCP checksum
 * then build IP header in packe buffer.
*/

union tcp_build_pkt{
        struct psuedo_hdr ph;
        struct ip_tcp_hdr_o ip;
	struct ip_tcp_hdr_s sack;
	uchar  raw[1600];
}__attribute__ ((packed));

/*
* TCP STATE MACHINE STATES FOR SOCKET
*/

enum TCP_STATE
{
	TCP_CLOSED,		/* Need to send SYN  to connect				*/
	TCP_SYN_SENT,		/* Trying to connect, waiting for SYN ACK		*/
	TCP_ESTABLISHED,	/* both server and client represents an open connection	*/
	TCP_CLOSE_WAIT,		/* Received FIN, passed to app for FIN, ACK response	*/
	TCP_CLOSING,		/* Received FIN, sent FIN, ACK waiting for ACK		*/
	TCP_FIN_WAIT_1,		/* Sendt FIN waiting for response			*/
	TCP_FIN_WAIT_2		/* Received ACK from FIN sent, waitng for FIN		*/
};

enum TCP_STATE net_get_tcp_state( void );

/*
 *	TCP incoming packet handler
*/

typedef void rxhand_tcp(uchar *pkt, unsigned dport,
			struct in_addr sip, unsigned sport,
				unsigned len, enum TCP_STATE tcp_state);
/*
 *	Address Resolution Protocol (ARP) header.
 */
struct arp_hdr {
	u16		ar_hrd;		/* Format of hardware address	*/
#   define ARP_ETHER	    1		/* Ethernet  hardware address	*/
	u16		ar_pro;		/* Format of protocol address	*/
	u8		ar_hln;		/* Length of hardware address	*/
#   define ARP_HLEN	6
	u8		ar_pln;		/* Length of protocol address	*/
#   define ARP_PLEN	4
	u16		ar_op;		/* Operation			*/
#   define ARPOP_REQUEST    1		/* Request  to resolve  address	*/
#   define ARPOP_REPLY	    2		/* Response to previous request	*/

#   define RARPOP_REQUEST   3		/* Request  to resolve  address	*/
#   define RARPOP_REPLY	    4		/* Response to previous request */

	/*
	 * The remaining fields are variable in size, according to
	 * the sizes above, and are defined as appropriate for
	 * specific hardware/protocol combinations.
	 */
	u8		ar_data[0];
#define ar_sha		ar_data[0]
#define ar_spa		ar_data[ARP_HLEN]
#define ar_tha		ar_data[ARP_HLEN + ARP_PLEN]
#define ar_tpa		ar_data[ARP_HLEN + ARP_PLEN + ARP_HLEN]
#if 0
	u8		ar_sha[];	/* Sender hardware address	*/
	u8		ar_spa[];	/* Sender protocol address	*/
	u8		ar_tha[];	/* Target hardware address	*/
	u8		ar_tpa[];	/* Target protocol address	*/
#endif /* 0 */
};

#define ARP_HDR_SIZE	(8+20)		/* Size assuming ethernet	*/

/*
 * ICMP stuff (just enough to handle (host) redirect messages)
 */
#define ICMP_ECHO_REPLY		0	/* Echo reply			*/
#define ICMP_NOT_REACH		3	/* Detination unreachable	*/
#define ICMP_REDIRECT		5	/* Redirect (change route)	*/
#define ICMP_ECHO_REQUEST	8	/* Echo request			*/

/* Codes for REDIRECT. */
#define ICMP_REDIR_NET		0	/* Redirect Net			*/
#define ICMP_REDIR_HOST		1	/* Redirect Host		*/

/* Codes for NOT_REACH */
#define ICMP_NOT_REACH_PORT	3	/* Port unreachable		*/

struct icmp_hdr {
	u8		type;
	u8		code;
	u16		checksum;
	union {
		struct {
			u16	id;
			u16	sequence;
		} echo;
		u32	gateway;
		struct {
			u16	unused;
			u16	mtu;
		} frag;
		u8 data[0];
	} un;
};

#define ICMP_HDR_SIZE		(sizeof(struct icmp_hdr))
#define IP_ICMP_HDR_SIZE	(IP_HDR_SIZE + ICMP_HDR_SIZE)

/*
 * Maximum packet size; used to allocate packet storage. Use
 * the maxium Ethernet frame size as specified by the Ethernet
 * standard including the 802.1Q tag (VLAN tagging).
 * maximum packet size =  1522
 * maximum packet size and multiple of 32 bytes =  1536
 */
#define PKTSIZE			1522
#define PKTSIZE_ALIGN		1536

/*
 * Maximum receive ring size; that is, the number of packets
 * we can buffer before overflow happens. Basically, this just
 * needs to be enough to prevent a packet being discarded while
 * we are processing the previous one.
 */
#define RINGSZ		4
#define RINGSZ_LOG2	2

/**********************************************************************/
/*
 *	Globals.
 *
 * Note:
 *
 * All variables of type struct in_addr are stored in NETWORK byte order
 * (big endian).
 */

/* net.c */
/** BOOTP EXTENTIONS **/
extern struct in_addr net_gateway;	/* Our gateway IP address */
extern struct in_addr net_netmask;	/* Our subnet mask (0 = unknown) */
/* Our Domain Name Server (0 = unknown) */
extern struct in_addr net_dns_server;
#if defined(CONFIG_BOOTP_DNS2)
/* Our 2nd Domain Name Server (0 = unknown) */
extern struct in_addr net_dns_server2;
#endif
extern char	net_nis_domain[32];	/* Our IS domain */
extern char	net_hostname[32];	/* Our hostname */
extern char	net_root_path[64];	/* Our root path */
/** END OF BOOTP EXTENTIONS **/
extern u8		net_ethaddr[6];		/* Our ethernet address */
extern u8		net_server_ethaddr[6];	/* Boot server enet address */
extern struct in_addr	net_ip;		/* Our    IP addr (0 = unknown) */
extern struct in_addr	net_server_ip;	/* Server IP addr (0 = unknown) */
extern uchar		*net_tx_packet;		/* THE transmit packet */
extern uchar		*net_rx_packets[PKTBUFSRX]; /* Receive packets */
extern uchar		*net_rx_packet;		/* Current receive packet */
extern int		net_rx_packet_len;	/* Current rx packet length */
extern const u8		net_bcast_ethaddr[6];	/* Ethernet broadcast address */
extern const u8		net_null_ethaddr[6];

#define VLAN_NONE	4095			/* untagged */
#define VLAN_IDMASK	0x0fff			/* mask of valid vlan id */
extern ushort		net_our_vlan;		/* Our VLAN */
extern ushort		net_native_vlan;	/* Our Native VLAN */

extern int		net_restart_wrap;	/* Tried all network devices */

enum proto_t {
	BOOTP, RARP, ARP, TFTPGET, DHCP, PING, DNS, NFS, CDP, NETCONS, SNTP,
	TFTPSRV, TFTPPUT, LINKLOCAL, WGET
};

extern char	net_boot_file_name[1024];/* Boot File name */
/* The actual transferred size of the bootfile (in bytes) */
extern u32	net_boot_file_size;
/* Boot file size in blocks as reported by the DHCP server */
extern u32	net_boot_file_expected_size_in_blocks;

#if defined(CONFIG_CMD_DNS)
extern char *net_dns_resolve;		/* The host to resolve  */
extern char *net_dns_env_var;		/* the env var to put the ip into */
#endif

#if defined(CONFIG_CMD_PING)
extern struct in_addr net_ping_ip;	/* the ip address to ping */
#endif

#if defined(CONFIG_CMD_CDP)
/* when CDP completes these hold the return values */
extern ushort cdp_native_vlan;		/* CDP returned native VLAN */
extern ushort cdp_appliance_vlan;	/* CDP returned appliance VLAN */

/*
 * Check for a CDP packet by examining the received MAC address field
 */
static inline int is_cdp_packet(const uchar *ethaddr)
{
	extern const u8 net_cdp_ethaddr[6];

	return memcmp(ethaddr, net_cdp_ethaddr, 6) == 0;
}
#endif

#if defined(CONFIG_CMD_SNTP)
extern struct in_addr	net_ntp_server;		/* the ip address to NTP */
extern int net_ntp_time_offset;			/* offset time from UTC */
#endif

#if defined(CONFIG_MCAST_TFTP)
extern struct in_addr net_mcast_addr;
#endif

/* Initialize the network adapter */
void net_init(void);
int net_loop(enum proto_t);

/* Load failed.	 Start again. */
int net_start_again(void);

/* Get size of the ethernet header when we send */
int net_eth_hdr_size(void);

/* Set ethernet header; returns the size of the header */
int net_set_ether(uchar *xet, const uchar *dest_ethaddr, uint prot);
int net_update_ether(struct ethernet_hdr *et, uchar *addr, uint prot);

/* Set IP header */
void net_set_ip_header(uchar *pkt, struct in_addr dest, struct in_addr source,
				u16  pkt_len, u8 prot );
int net_set_tcp_header(uchar *pkt, int len, u8 action, u32 tcp_seq_num, u32 tcp_ack_seq_num );
void net_set_udp_header(uchar *pkt, struct in_addr dest, int dport,
				int sport, int len);
/* Set ports  */
void net_set_ports( int server_port, int our_port );
/* Sequence number and SAK management */
void net_tcp_hole_contract( u32 tcp_seq_num, u32 tcp_end_num );
void net_tcp_hole_expand( u32 tcp_seq_num, u32 tcp_end_num );

/* Print packet or messsage */
void net_print_buffer( uchar raw[], int pkt_len, int payload_len,
				int hdr_len, bool hide );
/* Find string in buffer */
int net_find_in_buffer( uchar raw[], int payload_len, uchar field[], int field_len  );
/**
 * compute_ip_checksum() - Compute IP checksum
 *
 * @addr:	Address to check (must be 16-bit aligned)
 * @nbytes:	Number of bytes to check (normally a multiple of 2)
 * @return 16-bit IP checksum
 */
unsigned compute_ip_checksum(const void *addr, unsigned nbytes);

/**
 * add_ip_checksums() - add two IP checksums
 *
 * @offset:	Offset of first sum (if odd we do a byte-swap)
 * @sum:	First checksum
 * @new_sum:	New checksum to add
 * @return updated 16-bit IP checksum
 */
unsigned add_ip_checksums(unsigned offset, unsigned sum, unsigned new_sum);

/**
 * ip_checksum_ok() - check if a checksum is correct
 *
 * This works by making sure the checksum sums to 0
 *
 * @addr:	Address to check (must be 16-bit aligned)
 * @nbytes:	Number of bytes to check (normally a multiple of 2)
 * @return true if the checksum matches, false if not
 */
int ip_checksum_ok(const void *addr, unsigned nbytes);

/* Callbacks */
rxhand_f *net_get_tcp_handler(void);	/* Get TCP RX packet handler */
void net_set_tcp_handler(rxhand_f *);	/* Set TCP RX packet handler */
rxhand_f *net_get_udp_handler(void);	/* Get UDP RX packet handler */
void net_set_udp_handler(rxhand_f *);	/* Set UDP RX packet handler */
rxhand_f *net_get_arp_handler(void);	/* Get ARP RX packet handler */
void net_set_arp_handler(rxhand_f *);	/* Set ARP RX packet handler */
void net_set_icmp_handler(rxhand_icmp_f *f); 	/* Set ICMP RX handlr*/
void net_set_timeout_handler(ulong, thand_f *);	/* Set timeout handlr*/

/* Network loop state */
enum net_loop_state {
	NETLOOP_CONTINUE,
	NETLOOP_RESTART,
	NETLOOP_SUCCESS,
	NETLOOP_FAIL
};
extern enum net_loop_state net_state;

static inline void net_set_state(enum net_loop_state state)
{
	debug_cond(DEBUG_INT_STATE, "--- NetState set to %d\n", state);
	net_state = state;
}

/* Transmit a packet */
static inline void net_send_packet(uchar *pkt, int len)
{
	/* Currently no way to return errors from eth_send() */
	(void) eth_send(pkt, len);
}

/*
 * Transmit "net_tx_packet" as UDP packet, performing ARP request if needed
 *  (ether will be populated)
 *
 * @param ether Raw packet buffer
 * @param dest IP address to send the datagram to
 * @param dport Destination UDP port
 * @param sport Source UDP port
 * @param payload_len Length of data after the UDP header
 * Added TCP support and protocol parameter for sending ip packets, and
 * shim to support existing udp interface.
 */

int net_send_ip_packet( int payload_len, int proto, u8 action, u32 tcp_seq_num, u32 tcp_ack_num );

int net_send_udp_packet(uchar *ether, struct in_addr dest, int dport,
			int sport, int payload_len);

/* Processes a received packet */
void net_process_received_packet(uchar *in_packet, int len);

#ifdef CONFIG_NETCONSOLE
void nc_start(void);
int nc_input_packet(uchar *pkt, struct in_addr src_ip, unsigned dest_port,
	unsigned src_port, unsigned len);
#endif

static __always_inline int eth_is_on_demand_init(void)
{
#ifdef CONFIG_NETCONSOLE
	extern enum proto_t net_loop_last_protocol;

	return net_loop_last_protocol != NETCONS;
#else
	return 1;
#endif
}

static inline void eth_set_last_protocol(int protocol)
{
#ifdef CONFIG_NETCONSOLE
	extern enum proto_t net_loop_last_protocol;

	net_loop_last_protocol = protocol;
#endif
}

/*
 * Check if autoload is enabled. If so, use either NFS or TFTP to download
 * the boot file.
 */
void net_auto_load(void);

/*
 * The following functions are a bit ugly, but necessary to deal with
 * alignment restrictions on ARM.
 *
 * We're using inline functions, which had the smallest memory
 * footprint in our tests.
 */
/* return IP *in network byteorder* */
static inline struct in_addr net_read_ip(void *from)
{
	struct in_addr ip;

	memcpy((void *)&ip, (void *)from, sizeof(ip));
	return ip;
}

/* return ulong *in network byteorder* */
static inline u32 net_read_u32(u32 *from)
{
	u32 l;

	memcpy((void *)&l, (void *)from, sizeof(l));
	return l;
}

/* write IP *in network byteorder* */
static inline void net_write_ip(void *to, struct in_addr ip)
{
	memcpy(to, (void *)&ip, sizeof(ip));
}

/* copy IP */
static inline void net_copy_ip(void *to, void *from)
{
	memcpy((void *)to, from, sizeof(struct in_addr));
}

/* copy ulong */
static inline void net_copy_u32(u32 *to, u32 *from)
{
	memcpy((void *)to, (void *)from, sizeof(u32));
}

/**
 * is_zero_ethaddr - Determine if give Ethernet address is all zeros.
 * @addr: Pointer to a six-byte array containing the Ethernet address
 *
 * Return true if the address is all zeroes.
 */
static inline int is_zero_ethaddr(const u8 *addr)
{
	return !(addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5]);
}

/**
 * is_multicast_ethaddr - Determine if the Ethernet address is a multicast.
 * @addr: Pointer to a six-byte array containing the Ethernet address
 *
 * Return true if the address is a multicast address.
 * By definition the broadcast address is also a multicast address.
 */
static inline int is_multicast_ethaddr(const u8 *addr)
{
	return 0x01 & addr[0];
}

/*
 * is_broadcast_ethaddr - Determine if the Ethernet address is broadcast
 * @addr: Pointer to a six-byte array containing the Ethernet address
 *
 * Return true if the address is the broadcast address.
 */
static inline int is_broadcast_ethaddr(const u8 *addr)
{
	return (addr[0] & addr[1] & addr[2] & addr[3] & addr[4] & addr[5]) ==
		0xff;
}

/*
 * is_valid_ethaddr - Determine if the given Ethernet address is valid
 * @addr: Pointer to a six-byte array containing the Ethernet address
 *
 * Check that the Ethernet address (MAC) is not 00:00:00:00:00:00, is not
 * a multicast address, and is not FF:FF:FF:FF:FF:FF.
 *
 * Return true if the address is valid.
 */
static inline int is_valid_ethaddr(const u8 *addr)
{
	/* FF:FF:FF:FF:FF:FF is a multicast address so we don't need to
	 * explicitly check for it here. */
	return !is_multicast_ethaddr(addr) && !is_zero_ethaddr(addr);
}

/**
 * net_random_ethaddr - Generate software assigned random Ethernet address
 * @addr: Pointer to a six-byte array containing the Ethernet address
 *
 * Generate a random Ethernet address (MAC) that is not multicast
 * and has the local assigned bit set.
 */
static inline void net_random_ethaddr(uchar *addr)
{
	int i;
	unsigned int seed = get_timer(0);

	for (i = 0; i < 6; i++)
		addr[i] = rand_r(&seed);

	addr[0] &= 0xfe;	/* clear multicast bit */
	addr[0] |= 0x02;	/* set local assignment bit (IEEE802) */
}

/* Convert an IP address to a string */
void ip_to_string(struct in_addr x, char *s);

/* Convert a string to ip address */
struct in_addr string_to_ip(const char *s);

/* Convert a VLAN id to a string */
void vlan_to_string(ushort x, char *s);

/* Convert a string to a vlan id */
ushort string_to_vlan(const char *s);

/* read a VLAN id from an environment variable */
ushort getenv_vlan(char *);

/* copy a filename (allow for "..." notation, limit length) */
void copy_filename(char *dst, const char *src, int size);

/* get a random source port */
unsigned int random_port(void);

/**
 * update_tftp - Update firmware over TFTP (via DFU)
 *
 * This function updates board's firmware via TFTP
 *
 * @param addr - memory address where data is stored
 * @param interface - the DFU medium name - e.g. "mmc"
 * @param devstring - the DFU medium number - e.g. "1"
 *
 * @return - 0 on success, other value on failure
 */
int update_tftp(ulong addr, char *interface, char *devstring);

/**********************************************************************/

#endif /* __NET_H__ */

net/wget.c



	
	
	
	


/*
 * FILE  support driver - based on etherboot and U-BOOT's tftp.c
 *
 * Duncan Hare <dh@synoia.com> 2017
 *
 */


#include <common.h>
#include <command.h>
#include <fat.h>	//FAT
#include <net.h>
#include <mapmem.h>

#ifdef TCP

#define		FILE_TEST 0	/* Set to 1 for debug messges */

#define 	FILE_RETRY_COUNT 30
#ifndef 	CONFIG_FILE_TIMEOUT
# define 	FILE_TIMEOUT 	2000UL
#define 	SERVER_PORT 	8081
#else
# define 	FILE_TIMEOUT CONFIG_FILE_TIMEOUT
#endif

char bootfile[50] 	= "GET /r.32.test/boot/cmdline.txt HTTP/1.1\r\n\r\n";
char bootfile1[10]	= "GET ";
char bootfile2[30]	= "/r.32.test/boot/cmdline.txt";
char bootfile3[20]	= " HTTP/1.1\r\n\r\n";

uchar content[6] 	= "<html>";
uchar error404[3]	= "404";

static unsigned int file_timeout = FILE_TIMEOUT;

static struct 	in_addr file_server_ip;
static int 	file_timeout_count;
int packets = 0;

static u32 initial_data_seq_num;

enum FILE_STATE {
	FILE_CLOSED,
	FILE_CONNECTING,
	FILE_CONNECTED,
	FILE_TRANSFERRING,
	FILE_TRANSFERRED};

static enum  FILE_STATE file_state;

static char *file_filename;
static char *file_path;
static char file_path_buff[2048];
int i  = 0;

static void file_timeout_handler(void);
void file_fail( char * error_message, unsigned tcp_seq_num, unsigned tcp_ack_num, u8 action );

static inline int store_block(uchar *src, unsigned offset, unsigned len)
{
	ulong newsize = offset + len;
#ifdef CONFIG_SYS_DIRECT_FLASH_WGET
	int i, rc = 0;

	for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
		/* start address in flash? */
		if (load_addr + offset >= flash_info[i].start[0]) {
			rc = 1;
			break;
		}
	}

	if (rc) { /* Flash is destination for this packet */
		rc = flash_write((uchar *)src, (ulong)(load_addr+offset), len);
		if (rc) {
			flash_perror(rc);
			return -1;
		}
	} else
#endif /* CONFIG_SYS_DIRECT_FLASH_NFS */
	{
		uchar *ptr = map_sysmem(load_addr + offset, len);
		memcpy(ptr, src, len);
		if ( i < 3 )
		{
			printf( "Offset %d, Length %d\n", offset, len );
			net_print_buffer( src, 24, 24, 24, 0 );
			i++;
			net_print_buffer( src + len -24, (int)src + len -24, 24, 24, 0);
		}
		unmap_sysmem(ptr);
	}

	if (net_boot_file_size < (offset + len))
		net_boot_file_size = newsize;
	return 0;
}

/*
 * File request dispatcher
 * WARNING, This, and only this, is the place where
 * SEQUENCE NUMBERS are swapped betweeen incoming (RX)  and outgoing (TX)
 * What is in procedure file_handler() is correct for RX traffic.
*/
static void file_send( u8 action, unsigned tcp_ack_num, unsigned tcp_seq_num, int len )
{
	uchar *ptr;
	uchar *offset;

	tcp_ack_num = tcp_ack_num + len;

	switch (file_state)
	{
		case FILE_CLOSED:
			debug_cond( FILE_TEST, "File send: send SYN\n");
			file_state = FILE_CONNECTING;
			net_send_ip_packet( 0, IPPROTO_TCP, action, tcp_seq_num, tcp_ack_num );
		break;
		case FILE_CONNECTING:
			debug_cond( FILE_TEST, "File send: send HTTP GET\n");

			ptr = net_tx_packet + net_eth_hdr_size() + IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2;
			offset = ptr;

			memcpy( offset, &bootfile1, strlen(bootfile1) );
			offset = offset + strlen(bootfile1);

//			memcpy( offset, &bootfile2, strlen(bootfile2) );
//			offset = offset + strlen(bootfile2);

			memcpy( offset, file_path, strlen(file_path) );
			offset = offset + strlen(file_path);

			memcpy( offset, &bootfile3, strlen(bootfile3) );
			offset = offset + strlen(bootfile3);
			net_send_ip_packet(( offset - ptr), IPPROTO_TCP, TCP_DATA, tcp_seq_num, tcp_ack_num );
			file_state = FILE_CONNECTED;
		break;
		case FILE_CONNECTED:
			debug_cond( FILE_TEST, "File send: Header\n" );
			file_state = FILE_TRANSFERRING;
		break;
	        case FILE_TRANSFERRING:
			debug_cond( FILE_TEST, "File send:Transferring \n");
			net_send_ip_packet( 0, IPPROTO_TCP, action, tcp_seq_num, tcp_ack_num);
		break;
	        case FILE_TRANSFERRED:

			debug_cond( FILE_TEST, "File send:Transferred \n");
			net_send_ip_packet( 0, IPPROTO_TCP, action, tcp_seq_num, tcp_ack_num);
		break;
	}
}

void file_success( u8 action, unsigned tcp_seq_num, unsigned tcp_ack_num, int len, int packets )
{
	file_state = FILE_TRANSFERRED;
	net_set_state( NETLOOP_SUCCESS );
	net_set_timeout_handler( 0, NULL);
	debug_cond( 1, "Packets received %d, File send:SUCCESS!!!\n", packets );
	file_send( action, tcp_seq_num, tcp_ack_num, len );
}


void file_fail( char * error_message, unsigned tcp_seq_num, unsigned tcp_ack_num, u8 action )
{
	debug_cond( FILE_TEST, error_message );
	debug_cond( FILE_TEST, "File Fail\n" );
	file_state = FILE_TRANSFERRED;
	net_set_timeout_handler( 0, NULL);
	net_set_state( NETLOOP_FAIL );
	file_send( action | TCP_FIN, tcp_seq_num, tcp_ack_num, 0 );
}

/*
 * Interfaces of U-BOOT
*/
static void file_timeout_handler(void)
{
	if (++file_timeout_count > FILE_RETRY_COUNT) {
		puts("\nRetry count exceeded; starting again\n");
		net_start_again();
	} else {
		puts("T ");
		net_set_timeout_handler(file_timeout +
					FILE_TIMEOUT * file_timeout_count,
					file_timeout_handler);
		file_send( TCP_DATA, 0, 0, 0 );
	}
}

	/*
	 * In the "application push" invocation the TCP header with all its information is $
	 * packet pointer, and the other variable "repurposed" (or misused) to carry sequen$
	 * and  TCP state.
	 *
	 * Parms: ((uchar *) b, tcp_ack_num, action_and_state, tcp_seq_num, payload_len );
	 *
        */


static void file_handler(uchar *pkt, unsigned tcp_seq_num, struct in_addr action_and_state,
			unsigned tcp_ack_num, unsigned len )
{
	enum TCP_STATE file_tcp_state = net_get_tcp_state();
	u8 action = action_and_state.s_addr;
	int offset;

	net_set_timeout_handler(file_timeout, file_timeout_handler);
	packets ++;

        switch (file_state)
        {
                case FILE_CLOSED:
			debug_cond( FILE_TEST, "File Handler: Error!, State wrong\n");
                break;
		case FILE_CONNECTING:
			debug_cond(FILE_TEST, "File Connecting In len=%d, Seq=%d, Ack=%d\n", 
					len, tcp_seq_num, tcp_ack_num );
			if ( len == 0 )
			{
			 	if ( file_tcp_state == TCP_ESTABLISHED )
				{
					debug_cond( FILE_TEST, "File Handler Connecting, send, len=%d\n", len );
					file_send( action, tcp_seq_num, tcp_ack_num, len );
				}
				else file_fail( "File Handler Connected Fail\n",
					tcp_seq_num, tcp_ack_num, action );
			}
		break;
                case FILE_CONNECTED:
			debug_cond(FILE_TEST, "File Connected In len=%d, seq=%d, ack=%d\n"
					, len, tcp_seq_num, tcp_ack_num );
			if ( len == 0 )
			{
				file_fail("File not found, no data returned/n" ,
					 tcp_seq_num, tcp_ack_num, action );
			}

			else if ( net_find_in_buffer( pkt, len, error404, 3)  > 0 )
			{
				offset = net_find_in_buffer( pkt, len, content, 6 );
				net_print_buffer( pkt, offset-4, offset-4, -1, 0 );
				pkt[0] = 0x00;
				file_fail( (char *)pkt, tcp_seq_num, tcp_ack_num, action );
			}
			else						/* Got HTTP Header	*/
			{
				net_print_buffer( pkt, len, len, -1, 0 );
				file_state = FILE_TRANSFERRING;
				initial_data_seq_num = tcp_seq_num + len;
				file_send( action, tcp_seq_num, tcp_ack_num, len );
			}
                break;
                case FILE_TRANSFERRING:
			debug_cond(FILE_TEST, "File Transferring, seq=%d, ack=%d,len=%d\n"
					, tcp_seq_num, tcp_ack_num, len );

			if ( len > 0 )
			{
				offset = tcp_seq_num - initial_data_seq_num;
				if ( store_block( pkt, offset, len) != 0 )
					file_fail( "File store error\n",
							tcp_seq_num, tcp_ack_num, action  );
				else switch (file_tcp_state)
				{
					case TCP_SYN_SENT:
					case TCP_CLOSING:
					case TCP_FIN_WAIT_1:
					case TCP_FIN_WAIT_2:
					case TCP_CLOSED:
						net_set_state(NETLOOP_FAIL);
					break;
					case TCP_ESTABLISHED:
						file_send( TCP_ACK, tcp_seq_num, tcp_ack_num, len );
					break;
					case TCP_CLOSE_WAIT:		/* End of file 	*/
						file_success( action | TCP_ACK,
							tcp_seq_num, tcp_ack_num, len, packets );
					break;
				}
			}
			else file_send( action , tcp_seq_num, tcp_ack_num, len );
                break;
                case FILE_TRANSFERRED:
			file_send( TCP_ACK, tcp_seq_num, tcp_ack_num, len );
                break;
        }
}

void wget_start(void)
{
	debug("%s\n", __func__);

	file_server_ip = net_server_ip;
	file_path = (char *)file_path_buff;

	if (file_path == NULL) {
		net_set_state(NETLOOP_FAIL);
		debug("*** ERROR: Fail allocate memory\n");
		return;
	}

	if (net_boot_file_name[0] == '\0') {
		sprintf(file_path, "/fileroot/%02X%02X%02X%02X.img",
			net_ip.s_addr & 0xFF,
			(net_ip.s_addr >>  8) & 0xFF,
			(net_ip.s_addr >> 16) & 0xFF,
			(net_ip.s_addr >> 24) & 0xFF);

		debug("*** Warning: no boot file name; using '%s'\n",
		      file_path);
	} else {
		char *p = net_boot_file_name;

		p = strchr(p, ':');

		if (p != NULL) {
			file_server_ip = string_to_ip(net_boot_file_name);
			++p;
			strcpy(file_path, p);
		} else {
			strcpy(file_path, net_boot_file_name);
		}
	}

	debug_cond( FILE_TEST, "Using %s device\n", eth_get_name());

	debug_cond( FILE_TEST, "File transfer via HTTP from server %pI4; our IP address is %pI4\n",
	      &file_server_ip, &net_ip);

	/* Check if we need to send across this subnet */
	if (net_gateway.s_addr && net_netmask.s_addr) {
		struct in_addr our_net;
		struct in_addr server_net;

		our_net.s_addr = net_ip.s_addr & net_netmask.s_addr;
		server_net.s_addr = net_server_ip.s_addr & net_netmask.s_addr;
		if (our_net.s_addr != server_net.s_addr)
			debug("; sending through gateway %pI4",
			      &net_gateway);
	}
	debug_cond(FILE_TEST, "Filename '%s, %s'.\n", file_path, file_filename);

	if (net_boot_file_expected_size_in_blocks) {
		debug(" Size is 0x%x Bytes = ",
		      net_boot_file_expected_size_in_blocks << 9);
		print_size(net_boot_file_expected_size_in_blocks << 9, "");
	}
	debug("\nLoad address: 0x%lx\nLoading: *\b", load_addr);

	net_set_timeout_handler(file_timeout, file_timeout_handler);
	net_set_tcp_handler(file_handler);

	file_timeout_count = 0;
	file_state = FILE_CLOSED;

	net_set_ports( SERVER_PORT, 4096 + (get_ticks() % 3072));

	/* zero out server ether in case the server ip has changed */
	memset(net_server_ethaddr, 0, 6);

	file_send( TCP_SYN, 0, 0, 0 );
}
#endif
wget.h
/*
 * Duncan Hare Copyright 2017
*/

void wget_start(void);   /* Begin NFS */
net/ping.c changes
        struct icmp_hdr *icmp = (struct icmp_hdr *)(pkt + IP_HDR_SIZE);

        net_set_ip_header(pkt, dest, net_ip, IP_ICMP_HDR_SIZE, IPPROTO_ICMP);

        icmp->type = ICMP_ECHO_REQUEST;
        icmp->code = 0;
        icmp->checksum = 0;
        icmp->un.echo.id = 0;
        icmp->un.echo.sequence = htons(ping_seq_number++);
        icmp->checksum = compute_ip_checksum(icmp, ICMP_HDR_SIZE);
n Hare

714 931 7952

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [U-Boot] Request For Comments: wget and TCP listener
  2017-09-21 20:32 ` [U-Boot] Request For Comments: wget and TCP listener Duncan Hare
@ 2017-09-25  2:14   ` Simon Glass
  0 siblings, 0 replies; 4+ messages in thread
From: Simon Glass @ 2017-09-25  2:14 UTC (permalink / raw)
  To: u-boot

Hi Duncan,

On 21 September 2017 at 14:32, Duncan Hare <dh@synoia.com> wrote:
> Mods to:
> cmd/net.cnet/Makefilenet/net.cinclude/net.hnet/wget.cnet/wget.hnet/ping.c
> I do not know how to do patches, I'm a noobat this:

There is some documentation here.

https://www.denx.de/wiki/U-Boot/Patches

If you read all of that you will probably be on the right track.

Regards,
Simon

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [U-Boot] Request For Comments: wget and TCP listener
@ 2017-10-01 21:17 Duncan Hare
  0 siblings, 0 replies; 4+ messages in thread
From: Duncan Hare @ 2017-10-01 21:17 UTC (permalink / raw)
  To: u-boot

This code is written. and has changes to the following files:

I sent an "atomic patch", and it got stuck in moderation because it is 
350k+, that is too big.

My rules to myself were:

All interfaces, function calls, to be preserved. However, This results 
in an ugly
"re-purposing" of variables in the packet-in calls to wget.

Advice and suggestions welcome for the best implementation.

Following is a phased approach to get from here to there.

1. tcp headers, constants and procedure calls. This could be split into 
a net.h and a new tcp.h, or be added to net.h

module: include net.h

There are  many lines of code for TCP header structures, and readily 
inspected

2. cmd.net,  cmd/Kcongif, net/Kconfig and net/Makefile (I don't know if 
net/Makefile will be built automagically at some point.)

Small changes in a number of places.

3. net.c interfaces. and ping.c, and procedure definitions in include/net.

Body of code. tcp  could be a separate module, tcp.c Or integrated into 
the net.c source code.

As an aside at some point I'd suggest deleting the ip fragmentation 
reassembly code and
second checksum calculating routine in net.c.
3a) IP fragment reassembly does not provide any mechanism to retrieve 
"lost packets",
and should never be use where a complete data stream is necessary.
Kernels require a complete data stream, movies do not.

3b) 2 pieces of code to perform the same task, checksum, seems redundant.

4. tpc.c either as a new module, tcp.c, or included in net/net.c, 
400-500 lines of code.

5. wget.c and wget.h as a new command, 300+ lines of code.

 From 1 to 3 would be "it's not broken" regression testing, and 
relatively low risk.

4 & 5 would be the new function testing, and would have to include a 
significant amount of stress testing,
  because I'm anal retentive and it's too much new code not to have bugs.

Here's the cover letter, for background:

wget and enough TCP stack for fast netboot

September 29, 2017

Boot with udp is relatively slow on a local net, and very slow over a WAN.
TCP is a very effective protocol for fast file, or stream, transfers.
http (used by wget) is a very efficient protocol, there is one message to
retrieve the file, and acks from u-boot to server TCP thereafter.

The code was built & tested on a raspberry pi, LAN and WAN with
the Jan 2017 version of u-boot. This code is an update for the current
master, September 2017 version of u-boot, and cleaned up to pass
clearpatch standards, but not tested.


This is a limited TCP implementation for the wget command only.
And all the code newly written, without viewing any copyrighted source.

Complied binary size: When using wget, tftp and nfs can be omitted
from the build. This has not been measured. When not using wget, the TCP
code is eliminated from the build.

All interfaces, function calls, in use are preserved. This results in an 
ugly
"re-purposing" of variables in the packet-in calls to wget. This requires
advice on the best implementation.

Kconfig is used in ./cmd and ./net to control wget and tcp respectively.
Makefile in ./net appears to have to be configured by hand.

Duncan Hare

.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [U-Boot] Request For Comments: wget and TCP listener
@ 2017-10-01 21:01 Duncan Hare
  0 siblings, 0 replies; 4+ messages in thread
From: Duncan Hare @ 2017-10-01 21:01 UTC (permalink / raw)
  To: u-boot

This code is written. and has changes to the following files:

I sent an "atomic patch", and it got stuck in moderation because it is 
350k+, that is too big.

My rules to myself were:

All interfaces, function calls, to be preserved. However, This results 
in an ugly "re-purposing" of variables in the packet-in calls to wget, 
because wget needs sequence numbers and state.

Advice and suggestions welcome for the best implementation.

Following is a phased approach to get from here to there, one patch per 
numbered step:

1. tcp headers, and constants. This could be split into a net.h and a 
new tcp.h, or be added to net.h

module: include net.h

There are  many lines of code for TCP header structures, and readily 
inspected

2. cmd.net,  cmd/Kcongif, net/Kconfig and net/Makefile (I don't know if 
net/Makefile will be built automagically at some point.)

Small changes in a number of places.

3. net.c interfaces, include/net.h procedure call definitions, and ping.c.

Body of code. tcp  could be a separate module, tcp.c. Or in the net.c 
source code file.

As an aside at some point I'd suggest deleting the ip fragmentation 
reassembly code and
second checksum calculating routine in net.c.

3a) IP fragment reassembly does not provide any mechanism to retrieve 
"lost packets",
and should never be use where a complete data stream is necessary.
Kernels require a complete data stream, movies do not.

3b) 2 pieces of code to perform the same task, checksum, seems redundant.

4. tpc.c either as a new module, tcp.c, or included in net/net.c, 
400-500 lines of code.

5. wget.c and wget.h as a new command, 300+ lines of code.

 From 1 to 3 would be "it's not broken" regression testing, and 
relatively low risk.

4 & 5 would be the new function testing, and would have to include a 
significant amount of stress testing,
  because I'm anal retentive and it's too much new code not to have bugs.

Here's the cover letter, for background:

wget and enough TCP stack for fast netboot

September 29, 2017

Boot with udp is relatively slow on a local net, and very slow over a WAN.
TCP is a very effective protocol for fast file, or stream, transfers.
http (used by wget) is a very efficient protocol, there is one message to
retrieve the file, and acks from u-boot to server TCP thereafter.

The code was built & tested on a raspberry pi, LAN and WAN with
the Jan 2017 version of u-boot. This code is an update for the current
master, September 2017 version of u-boot, and cleaned up to pass
clearpatch standards, but not tested.


This is a limited TCP implementation for the wget command only.
And all the code newly written, without viewing any copyrighted source.

Complied binary size: When using wget, tftp and nfs can be omitted
from the build. This has not been measured. When not using wget, the TCP
code is eliminated from the build.

All interfaces, function calls, in use are preserved. This results in an 
ugly
"re-purposing" of variables in the packet-in calls to wget. This requires
advice on the best implementation.

Kconfig is used in ./cmd and ./net to control wget and tcp respectively.
Makefile in ./net appears to have to be configured by hand.

Duncan


.

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2017-10-01 21:17 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <1166665933.7178985.1506025978766.ref@mail.yahoo.com>
2017-09-21 20:32 ` [U-Boot] Request For Comments: wget and TCP listener Duncan Hare
2017-09-25  2:14   ` Simon Glass
2017-10-01 21:01 Duncan Hare
2017-10-01 21:17 Duncan Hare

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.