[U-Boot] Request For Comments: wget and TCP listener

Duncan Hare dh at synoia.com
Thu Sep 21 20:32:58 UTC 2017


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 at 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 at 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


More information about the U-Boot mailing list