diff options
Diffstat (limited to 'roms/u-boot/net')
| -rw-r--r-- | roms/u-boot/net/Makefile | 21 | ||||
| -rw-r--r-- | roms/u-boot/net/arp.c | 236 | ||||
| -rw-r--r-- | roms/u-boot/net/arp.h | 30 | ||||
| -rw-r--r-- | roms/u-boot/net/bootp.c | 963 | ||||
| -rw-r--r-- | roms/u-boot/net/bootp.h | 95 | ||||
| -rw-r--r-- | roms/u-boot/net/cdp.c | 366 | ||||
| -rw-r--r-- | roms/u-boot/net/cdp.h | 21 | ||||
| -rw-r--r-- | roms/u-boot/net/dns.c | 206 | ||||
| -rw-r--r-- | roms/u-boot/net/dns.h | 36 | ||||
| -rw-r--r-- | roms/u-boot/net/eth.c | 531 | ||||
| -rw-r--r-- | roms/u-boot/net/link_local.c | 345 | ||||
| -rw-r--r-- | roms/u-boot/net/link_local.h | 24 | ||||
| -rw-r--r-- | roms/u-boot/net/net.c | 1487 | ||||
| -rw-r--r-- | roms/u-boot/net/net_rand.h | 43 | ||||
| -rw-r--r-- | roms/u-boot/net/nfs.c | 787 | ||||
| -rw-r--r-- | roms/u-boot/net/nfs.h | 77 | ||||
| -rw-r--r-- | roms/u-boot/net/ping.c | 115 | ||||
| -rw-r--r-- | roms/u-boot/net/ping.h | 31 | ||||
| -rw-r--r-- | roms/u-boot/net/rarp.c | 100 | ||||
| -rw-r--r-- | roms/u-boot/net/rarp.h | 29 | ||||
| -rw-r--r-- | roms/u-boot/net/sntp.c | 92 | ||||
| -rw-r--r-- | roms/u-boot/net/sntp.h | 58 | ||||
| -rw-r--r-- | roms/u-boot/net/tftp.c | 949 | ||||
| -rw-r--r-- | roms/u-boot/net/tftp.h | 30 | 
24 files changed, 6672 insertions, 0 deletions
diff --git a/roms/u-boot/net/Makefile b/roms/u-boot/net/Makefile new file mode 100644 index 00000000..94259502 --- /dev/null +++ b/roms/u-boot/net/Makefile @@ -0,0 +1,21 @@ +# +# (C) Copyright 2000-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier:	GPL-2.0+ +# + +#ccflags-y += -DDEBUG + +obj-$(CONFIG_CMD_NET)  += arp.o +obj-$(CONFIG_CMD_NET)  += bootp.o +obj-$(CONFIG_CMD_CDP)  += cdp.o +obj-$(CONFIG_CMD_DNS)  += dns.o +obj-$(CONFIG_CMD_NET)  += eth.o +obj-$(CONFIG_CMD_LINK_LOCAL) += link_local.o +obj-$(CONFIG_CMD_NET)  += net.o +obj-$(CONFIG_CMD_NFS)  += nfs.o +obj-$(CONFIG_CMD_PING) += ping.o +obj-$(CONFIG_CMD_RARP) += rarp.o +obj-$(CONFIG_CMD_SNTP) += sntp.o +obj-$(CONFIG_CMD_NET)  += tftp.o diff --git a/roms/u-boot/net/arp.c b/roms/u-boot/net/arp.c new file mode 100644 index 00000000..20c6b2d4 --- /dev/null +++ b/roms/u-boot/net/arp.c @@ -0,0 +1,236 @@ +/* + *	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@denx.de + */ + +#include <common.h> + +#include "arp.h" + +#ifndef	CONFIG_ARP_TIMEOUT +/* Milliseconds before trying ARP again */ +# define ARP_TIMEOUT		5000UL +#else +# define ARP_TIMEOUT		CONFIG_ARP_TIMEOUT +#endif + + +#ifndef	CONFIG_NET_RETRY_COUNT +# define ARP_TIMEOUT_COUNT	5	/* # of timeouts before giving up  */ +#else +# define ARP_TIMEOUT_COUNT	CONFIG_NET_RETRY_COUNT +#endif + +IPaddr_t	NetArpWaitPacketIP; +static IPaddr_t	NetArpWaitReplyIP; +/* MAC address of waiting packet's destination */ +uchar	       *NetArpWaitPacketMAC; +int		NetArpWaitTxPacketSize; +ulong		NetArpWaitTimerStart; +int		NetArpWaitTry; + +static uchar   *NetArpTxPacket;	/* THE ARP transmit packet */ +static uchar	NetArpPacketBuf[PKTSIZE_ALIGN + PKTALIGN]; + +void ArpInit(void) +{ +	/* XXX problem with bss workaround */ +	NetArpWaitPacketMAC = NULL; +	NetArpWaitPacketIP = 0; +	NetArpWaitReplyIP = 0; +	NetArpWaitTxPacketSize = 0; +	NetArpTxPacket = &NetArpPacketBuf[0] + (PKTALIGN - 1); +	NetArpTxPacket -= (ulong)NetArpTxPacket % PKTALIGN; +} + +void arp_raw_request(IPaddr_t sourceIP, const uchar *targetEther, +	IPaddr_t targetIP) +{ +	uchar *pkt; +	struct arp_hdr *arp; +	int eth_hdr_size; + +	debug_cond(DEBUG_DEV_PKT, "ARP broadcast %d\n", NetArpWaitTry); + +	pkt = NetArpTxPacket; + +	eth_hdr_size = NetSetEther(pkt, NetBcastAddr, PROT_ARP); +	pkt += eth_hdr_size; + +	arp = (struct arp_hdr *) pkt; + +	arp->ar_hrd = htons(ARP_ETHER); +	arp->ar_pro = htons(PROT_IP); +	arp->ar_hln = ARP_HLEN; +	arp->ar_pln = ARP_PLEN; +	arp->ar_op = htons(ARPOP_REQUEST); + +	memcpy(&arp->ar_sha, NetOurEther, ARP_HLEN);	/* source ET addr */ +	NetWriteIP(&arp->ar_spa, sourceIP);		/* source IP addr */ +	memcpy(&arp->ar_tha, targetEther, ARP_HLEN);	/* target ET addr */ +	NetWriteIP(&arp->ar_tpa, targetIP);		/* target IP addr */ + +	NetSendPacket(NetArpTxPacket, eth_hdr_size + ARP_HDR_SIZE); +} + +void ArpRequest(void) +{ +	if ((NetArpWaitPacketIP & NetOurSubnetMask) != +	    (NetOurIP & NetOurSubnetMask)) { +		if (NetOurGatewayIP == 0) { +			puts("## Warning: gatewayip needed but not set\n"); +			NetArpWaitReplyIP = NetArpWaitPacketIP; +		} else { +			NetArpWaitReplyIP = NetOurGatewayIP; +		} +	} else { +		NetArpWaitReplyIP = NetArpWaitPacketIP; +	} + +	arp_raw_request(NetOurIP, NetEtherNullAddr, NetArpWaitReplyIP); +} + +void ArpTimeoutCheck(void) +{ +	ulong t; + +	if (!NetArpWaitPacketIP) +		return; + +	t = get_timer(0); + +	/* check for arp timeout */ +	if ((t - NetArpWaitTimerStart) > ARP_TIMEOUT) { +		NetArpWaitTry++; + +		if (NetArpWaitTry >= ARP_TIMEOUT_COUNT) { +			puts("\nARP Retry count exceeded; starting again\n"); +			NetArpWaitTry = 0; +			NetStartAgain(); +		} else { +			NetArpWaitTimerStart = t; +			ArpRequest(); +		} +	} +} + +void ArpReceive(struct ethernet_hdr *et, struct ip_udp_hdr *ip, int len) +{ +	struct arp_hdr *arp; +	IPaddr_t reply_ip_addr; +	uchar *pkt; +	int eth_hdr_size; + +	/* +	 * We have to deal with two types of ARP packets: +	 * - REQUEST packets will be answered by sending  our +	 *   IP address - if we know it. +	 * - REPLY packates are expected only after we asked +	 *   for the TFTP server's or the gateway's ethernet +	 *   address; so if we receive such a packet, we set +	 *   the server ethernet address +	 */ +	debug_cond(DEBUG_NET_PKT, "Got ARP\n"); + +	arp = (struct arp_hdr *)ip; +	if (len < ARP_HDR_SIZE) { +		printf("bad length %d < %d\n", len, ARP_HDR_SIZE); +		return; +	} +	if (ntohs(arp->ar_hrd) != ARP_ETHER) +		return; +	if (ntohs(arp->ar_pro) != PROT_IP) +		return; +	if (arp->ar_hln != ARP_HLEN) +		return; +	if (arp->ar_pln != ARP_PLEN) +		return; + +	if (NetOurIP == 0) +		return; + +	if (NetReadIP(&arp->ar_tpa) != NetOurIP) +		return; + +	switch (ntohs(arp->ar_op)) { +	case ARPOP_REQUEST: +		/* reply with our IP address */ +		debug_cond(DEBUG_DEV_PKT, "Got ARP REQUEST, return our IP\n"); +		pkt = (uchar *)et; +		eth_hdr_size = net_update_ether(et, et->et_src, PROT_ARP); +		pkt += eth_hdr_size; +		arp->ar_op = htons(ARPOP_REPLY); +		memcpy(&arp->ar_tha, &arp->ar_sha, ARP_HLEN); +		NetCopyIP(&arp->ar_tpa, &arp->ar_spa); +		memcpy(&arp->ar_sha, NetOurEther, ARP_HLEN); +		NetCopyIP(&arp->ar_spa, &NetOurIP); + +#ifdef CONFIG_CMD_LINK_LOCAL +		/* +		 * Work-around for brain-damaged Cisco equipment with +		 *   arp-proxy enabled. +		 * +		 *   If the requesting IP is not on our subnet, wait 5ms to +		 *   reply to ARP request so that our reply will overwrite +		 *   the arp-proxy's instead of the other way around. +		 */ +		if ((NetReadIP(&arp->ar_tpa) & NetOurSubnetMask) != +		    (NetReadIP(&arp->ar_spa) & NetOurSubnetMask)) +			udelay(5000); +#endif +		NetSendPacket((uchar *)et, eth_hdr_size + ARP_HDR_SIZE); +		return; + +	case ARPOP_REPLY:		/* arp reply */ +		/* are we waiting for a reply */ +		if (!NetArpWaitPacketIP) +			break; + +#ifdef CONFIG_KEEP_SERVERADDR +		if (NetServerIP == NetArpWaitPacketIP) { +			char buf[20]; +			sprintf(buf, "%pM", &arp->ar_sha); +			setenv("serveraddr", buf); +		} +#endif + +		reply_ip_addr = NetReadIP(&arp->ar_spa); + +		/* matched waiting packet's address */ +		if (reply_ip_addr == NetArpWaitReplyIP) { +			debug_cond(DEBUG_DEV_PKT, +				"Got ARP REPLY, set eth addr (%pM)\n", +				arp->ar_data); + +			/* save address for later use */ +			if (NetArpWaitPacketMAC != NULL) +				memcpy(NetArpWaitPacketMAC, +				       &arp->ar_sha, ARP_HLEN); + +			net_get_arp_handler()((uchar *)arp, 0, reply_ip_addr, +				0, len); + +			/* set the mac address in the waiting packet's header +			   and transmit it */ +			memcpy(((struct ethernet_hdr *)NetTxPacket)->et_dest, +				&arp->ar_sha, ARP_HLEN); +			NetSendPacket(NetTxPacket, NetArpWaitTxPacketSize); + +			/* no arp request pending now */ +			NetArpWaitPacketIP = 0; +			NetArpWaitTxPacketSize = 0; +			NetArpWaitPacketMAC = NULL; + +		} +		return; +	default: +		debug("Unexpected ARP opcode 0x%x\n", +		      ntohs(arp->ar_op)); +		return; +	} +} diff --git a/roms/u-boot/net/arp.h b/roms/u-boot/net/arp.h new file mode 100644 index 00000000..bfd57e01 --- /dev/null +++ b/roms/u-boot/net/arp.h @@ -0,0 +1,30 @@ +/* + *	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@denx.de + */ + +#ifndef __ARP_H__ +#define __ARP_H__ + +#include <common.h> + +extern IPaddr_t	NetArpWaitPacketIP; +/* MAC address of waiting packet's destination */ +extern uchar *NetArpWaitPacketMAC; +extern int NetArpWaitTxPacketSize; +extern ulong NetArpWaitTimerStart; +extern int NetArpWaitTry; + +void ArpInit(void); +void ArpRequest(void); +void arp_raw_request(IPaddr_t sourceIP, const uchar *targetEther, +	IPaddr_t targetIP); +void ArpTimeoutCheck(void); +void ArpReceive(struct ethernet_hdr *et, struct ip_udp_hdr *ip, int len); + +#endif /* __ARP_H__ */ diff --git a/roms/u-boot/net/bootp.c b/roms/u-boot/net/bootp.c new file mode 100644 index 00000000..189a0038 --- /dev/null +++ b/roms/u-boot/net/bootp.c @@ -0,0 +1,963 @@ +/* + *	Based on LiMon - BOOTP. + * + *	Copyright 1994, 1995, 2000 Neil Russell. + *	(See License) + *	Copyright 2000 Roland Borde + *	Copyright 2000 Paolo Scaffardi + *	Copyright 2000-2004 Wolfgang Denk, wd@denx.de + */ + +#include <common.h> +#include <command.h> +#include <net.h> +#include "bootp.h" +#include "tftp.h" +#include "nfs.h" +#ifdef CONFIG_STATUS_LED +#include <status_led.h> +#endif +#ifdef CONFIG_BOOTP_RANDOM_DELAY +#include "net_rand.h" +#endif + +#define BOOTP_VENDOR_MAGIC	0x63825363	/* RFC1048 Magic Cookie */ + +#define TIMEOUT		5000UL	/* Milliseconds before trying BOOTP again */ +#ifndef CONFIG_NET_RETRY_COUNT +# define TIMEOUT_COUNT	5		/* # of timeouts before giving up */ +#else +# define TIMEOUT_COUNT	(CONFIG_NET_RETRY_COUNT) +#endif + +#define PORT_BOOTPS	67		/* BOOTP server UDP port */ +#define PORT_BOOTPC	68		/* BOOTP client UDP port */ + +#ifndef CONFIG_DHCP_MIN_EXT_LEN		/* minimal length of extension list */ +#define CONFIG_DHCP_MIN_EXT_LEN 64 +#endif + +ulong		BootpID; +int		BootpTry; + +#if defined(CONFIG_CMD_DHCP) +static dhcp_state_t dhcp_state = INIT; +static unsigned long dhcp_leasetime; +static IPaddr_t NetDHCPServerIP; +static void DhcpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src, +			unsigned len); + +/* For Debug */ +#if 0 +static char *dhcpmsg2str(int type) +{ +	switch (type) { +	case 1:	 return "DHCPDISCOVER"; break; +	case 2:	 return "DHCPOFFER";	break; +	case 3:	 return "DHCPREQUEST";	break; +	case 4:	 return "DHCPDECLINE";	break; +	case 5:	 return "DHCPACK";	break; +	case 6:	 return "DHCPNACK";	break; +	case 7:	 return "DHCPRELEASE";	break; +	default: return "UNKNOWN/INVALID MSG TYPE"; break; +	} +} +#endif +#endif + +static int BootpCheckPkt(uchar *pkt, unsigned dest, unsigned src, unsigned len) +{ +	struct Bootp_t *bp = (struct Bootp_t *) pkt; +	int retval = 0; + +	if (dest != PORT_BOOTPC || src != PORT_BOOTPS) +		retval = -1; +	else if (len < sizeof(struct Bootp_t) - OPT_FIELD_SIZE) +		retval = -2; +	else if (bp->bp_op != OP_BOOTREQUEST && +			bp->bp_op != OP_BOOTREPLY && +			bp->bp_op != DHCP_OFFER && +			bp->bp_op != DHCP_ACK && +			bp->bp_op != DHCP_NAK) +		retval = -3; +	else if (bp->bp_htype != HWT_ETHER) +		retval = -4; +	else if (bp->bp_hlen != HWL_ETHER) +		retval = -5; +	else if (NetReadLong((ulong *)&bp->bp_id) != BootpID) +		retval = -6; + +	debug("Filtering pkt = %d\n", retval); + +	return retval; +} + +/* + * Copy parameters of interest from BOOTP_REPLY/DHCP_OFFER packet + */ +static void BootpCopyNetParams(struct Bootp_t *bp) +{ +#if !defined(CONFIG_BOOTP_SERVERIP) +	IPaddr_t tmp_ip; + +	NetCopyIP(&tmp_ip, &bp->bp_siaddr); +	if (tmp_ip != 0) +		NetCopyIP(&NetServerIP, &bp->bp_siaddr); +	memcpy(NetServerEther, ((struct ethernet_hdr *)NetRxPacket)->et_src, 6); +#endif +	NetCopyIP(&NetOurIP, &bp->bp_yiaddr); +	if (strlen(bp->bp_file) > 0) +		copy_filename(BootFile, bp->bp_file, sizeof(BootFile)); + +	debug("Bootfile: %s\n", BootFile); + +	/* Propagate to environment: +	 * don't delete exising entry when BOOTP / DHCP reply does +	 * not contain a new value +	 */ +	if (*BootFile) +		setenv("bootfile", BootFile); +} + +static int truncate_sz(const char *name, int maxlen, int curlen) +{ +	if (curlen >= maxlen) { +		printf("*** WARNING: %s is too long (%d - max: %d)" +			" - truncated\n", name, curlen, maxlen); +		curlen = maxlen - 1; +	} +	return curlen; +} + +#if !defined(CONFIG_CMD_DHCP) + +static void BootpVendorFieldProcess(u8 *ext) +{ +	int size = *(ext + 1); + +	debug("[BOOTP] Processing extension %d... (%d bytes)\n", *ext, +		*(ext + 1)); + +	NetBootFileSize = 0; + +	switch (*ext) { +		/* Fixed length fields */ +	case 1:			/* Subnet mask */ +		if (NetOurSubnetMask == 0) +			NetCopyIP(&NetOurSubnetMask, (IPaddr_t *) (ext + 2)); +		break; +	case 2:			/* Time offset - Not yet supported */ +		break; +		/* Variable length fields */ +	case 3:			/* Gateways list */ +		if (NetOurGatewayIP == 0) +			NetCopyIP(&NetOurGatewayIP, (IPaddr_t *) (ext + 2)); +		break; +	case 4:			/* Time server - Not yet supported */ +		break; +	case 5:			/* IEN-116 name server - Not yet supported */ +		break; +	case 6: +		if (NetOurDNSIP == 0) +			NetCopyIP(&NetOurDNSIP, (IPaddr_t *) (ext + 2)); +#if defined(CONFIG_BOOTP_DNS2) +		if ((NetOurDNS2IP == 0) && (size > 4)) +			NetCopyIP(&NetOurDNS2IP, (IPaddr_t *) (ext + 2 + 4)); +#endif +		break; +	case 7:			/* Log server - Not yet supported */ +		break; +	case 8:			/* Cookie/Quote server - Not yet supported */ +		break; +	case 9:			/* LPR server - Not yet supported */ +		break; +	case 10:		/* Impress server - Not yet supported */ +		break; +	case 11:		/* RPL server - Not yet supported */ +		break; +	case 12:		/* Host name */ +		if (NetOurHostName[0] == 0) { +			size = truncate_sz("Host Name", +				sizeof(NetOurHostName), size); +			memcpy(&NetOurHostName, ext + 2, size); +			NetOurHostName[size] = 0; +		} +		break; +	case 13:		/* Boot file size */ +		if (size == 2) +			NetBootFileSize = ntohs(*(ushort *) (ext + 2)); +		else if (size == 4) +			NetBootFileSize = ntohl(*(ulong *) (ext + 2)); +		break; +	case 14:		/* Merit dump file - Not yet supported */ +		break; +	case 15:		/* Domain name - Not yet supported */ +		break; +	case 16:		/* Swap server - Not yet supported */ +		break; +	case 17:		/* Root path */ +		if (NetOurRootPath[0] == 0) { +			size = truncate_sz("Root Path", +				sizeof(NetOurRootPath), size); +			memcpy(&NetOurRootPath, ext + 2, size); +			NetOurRootPath[size] = 0; +		} +		break; +	case 18:		/* Extension path - Not yet supported */ +		/* +		 * This can be used to send the information of the +		 * vendor area in another file that the client can +		 * access via TFTP. +		 */ +		break; +		/* IP host layer fields */ +	case 40:		/* NIS Domain name */ +		if (NetOurNISDomain[0] == 0) { +			size = truncate_sz("NIS Domain Name", +				sizeof(NetOurNISDomain), size); +			memcpy(&NetOurNISDomain, ext + 2, size); +			NetOurNISDomain[size] = 0; +		} +		break; +#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER) +	case 42:	/* NTP server IP */ +		NetCopyIP(&NetNtpServerIP, (IPaddr_t *) (ext + 2)); +		break; +#endif +		/* Application layer fields */ +	case 43:		/* Vendor specific info - Not yet supported */ +		/* +		 * Binary information to exchange specific +		 * product information. +		 */ +		break; +		/* Reserved (custom) fields (128..254) */ +	} +} + +static void BootpVendorProcess(u8 *ext, int size) +{ +	u8 *end = ext + size; + +	debug("[BOOTP] Checking extension (%d bytes)...\n", size); + +	while ((ext < end) && (*ext != 0xff)) { +		if (*ext == 0) { +			ext++; +		} else { +			u8 *opt = ext; + +			ext += ext[1] + 2; +			if (ext <= end) +				BootpVendorFieldProcess(opt); +		} +	} + +	debug("[BOOTP] Received fields:\n"); +	if (NetOurSubnetMask) +		debug("NetOurSubnetMask : %pI4\n", &NetOurSubnetMask); + +	if (NetOurGatewayIP) +		debug("NetOurGatewayIP	: %pI4", &NetOurGatewayIP); + +	if (NetBootFileSize) +		debug("NetBootFileSize : %d\n", NetBootFileSize); + +	if (NetOurHostName[0]) +		debug("NetOurHostName  : %s\n", NetOurHostName); + +	if (NetOurRootPath[0]) +		debug("NetOurRootPath  : %s\n", NetOurRootPath); + +	if (NetOurNISDomain[0]) +		debug("NetOurNISDomain : %s\n", NetOurNISDomain); + +	if (NetBootFileSize) +		debug("NetBootFileSize: %d\n", NetBootFileSize); + +#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER) +	if (NetNtpServerIP) +		debug("NetNtpServerIP : %pI4\n", &NetNtpServerIP); +#endif +} + +/* + *	Handle a BOOTP received packet. + */ +static void +BootpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src, +	     unsigned len) +{ +	struct Bootp_t *bp; + +	debug("got BOOTP packet (src=%d, dst=%d, len=%d want_len=%zu)\n", +		src, dest, len, sizeof(struct Bootp_t)); + +	bp = (struct Bootp_t *)pkt; + +	/* Filter out pkts we don't want */ +	if (BootpCheckPkt(pkt, dest, src, len)) +		return; + +	/* +	 *	Got a good BOOTP reply.	 Copy the data into our variables. +	 */ +#ifdef CONFIG_STATUS_LED +	status_led_set(STATUS_LED_BOOT, STATUS_LED_OFF); +#endif + +	BootpCopyNetParams(bp);		/* Store net parameters from reply */ + +	/* Retrieve extended information (we must parse the vendor area) */ +	if (NetReadLong((ulong *)&bp->bp_vend[0]) == htonl(BOOTP_VENDOR_MAGIC)) +		BootpVendorProcess((uchar *)&bp->bp_vend[4], len); + +	NetSetTimeout(0, (thand_f *)0); +	bootstage_mark_name(BOOTSTAGE_ID_BOOTP_STOP, "bootp_stop"); + +	debug("Got good BOOTP\n"); + +	net_auto_load(); +} +#endif + +/* + *	Timeout on BOOTP/DHCP request. + */ +static void +BootpTimeout(void) +{ +	if (BootpTry >= TIMEOUT_COUNT) { +#ifdef CONFIG_BOOTP_MAY_FAIL +		puts("\nRetry count exceeded\n"); +		net_set_state(NETLOOP_FAIL); +#else +		puts("\nRetry count exceeded; starting again\n"); +		NetStartAgain(); +#endif +	} else { +		NetSetTimeout(TIMEOUT, BootpTimeout); +		BootpRequest(); +	} +} + +#define put_vci(e, str)						\ +	do {							\ +		size_t vci_strlen = strlen(str);		\ +		*e++ = 60;	/* Vendor Class Identifier */	\ +		*e++ = vci_strlen;				\ +		memcpy(e, str, vci_strlen);			\ +		e += vci_strlen;				\ +	} while (0) + +/* + *	Initialize BOOTP extension fields in the request. + */ +#if defined(CONFIG_CMD_DHCP) +static int DhcpExtended(u8 *e, int message_type, IPaddr_t ServerID, +			IPaddr_t RequestedIP) +{ +	u8 *start = e; +	u8 *cnt; +#if defined(CONFIG_BOOTP_PXE) +	char *uuid; +	u16 clientarch; +#endif + +#if defined(CONFIG_BOOTP_VENDOREX) +	u8 *x; +#endif +#if defined(CONFIG_BOOTP_SEND_HOSTNAME) +	char *hostname; +#endif + +	*e++ = 99;		/* RFC1048 Magic Cookie */ +	*e++ = 130; +	*e++ = 83; +	*e++ = 99; + +	*e++ = 53;		/* DHCP Message Type */ +	*e++ = 1; +	*e++ = message_type; + +	*e++ = 57;		/* Maximum DHCP Message Size */ +	*e++ = 2; +	*e++ = (576 - 312 + OPT_FIELD_SIZE) >> 8; +	*e++ = (576 - 312 + OPT_FIELD_SIZE) & 0xff; + +	if (ServerID) { +		int tmp = ntohl(ServerID); + +		*e++ = 54;	/* ServerID */ +		*e++ = 4; +		*e++ = tmp >> 24; +		*e++ = tmp >> 16; +		*e++ = tmp >> 8; +		*e++ = tmp & 0xff; +	} + +	if (RequestedIP) { +		int tmp = ntohl(RequestedIP); + +		*e++ = 50;	/* Requested IP */ +		*e++ = 4; +		*e++ = tmp >> 24; +		*e++ = tmp >> 16; +		*e++ = tmp >> 8; +		*e++ = tmp & 0xff; +	} +#if defined(CONFIG_BOOTP_SEND_HOSTNAME) +	hostname = getenv("hostname"); +	if (hostname) { +		int hostnamelen = strlen(hostname); + +		*e++ = 12;	/* Hostname */ +		*e++ = hostnamelen; +		memcpy(e, hostname, hostnamelen); +		e += hostnamelen; +	} +#endif + +#if defined(CONFIG_BOOTP_PXE) +	clientarch = CONFIG_BOOTP_PXE_CLIENTARCH; +	*e++ = 93;	/* Client System Architecture */ +	*e++ = 2; +	*e++ = (clientarch >> 8) & 0xff; +	*e++ = clientarch & 0xff; + +	*e++ = 94;	/* Client Network Interface Identifier */ +	*e++ = 3; +	*e++ = 1;	/* type field for UNDI */ +	*e++ = 0;	/* major revision */ +	*e++ = 0;	/* minor revision */ + +	uuid = getenv("pxeuuid"); + +	if (uuid) { +		if (uuid_str_valid(uuid)) { +			*e++ = 97;	/* Client Machine Identifier */ +			*e++ = 17; +			*e++ = 0;	/* type 0 - UUID */ + +			uuid_str_to_bin(uuid, e, UUID_STR_FORMAT_STD); +			e += 16; +		} else { +			printf("Invalid pxeuuid: %s\n", uuid); +		} +	} +#endif + +#ifdef CONFIG_BOOTP_VCI_STRING +	put_vci(e, CONFIG_BOOTP_VCI_STRING); +#endif + +#if defined(CONFIG_BOOTP_VENDOREX) +	x = dhcp_vendorex_prep(e); +	if (x) +		return x - start; +#endif + +	*e++ = 55;		/* Parameter Request List */ +	 cnt = e++;		/* Pointer to count of requested items */ +	*cnt = 0; +#if defined(CONFIG_BOOTP_SUBNETMASK) +	*e++  = 1;		/* Subnet Mask */ +	*cnt += 1; +#endif +#if defined(CONFIG_BOOTP_TIMEOFFSET) +	*e++  = 2; +	*cnt += 1; +#endif +#if defined(CONFIG_BOOTP_GATEWAY) +	*e++  = 3;		/* Router Option */ +	*cnt += 1; +#endif +#if defined(CONFIG_BOOTP_DNS) +	*e++  = 6;		/* DNS Server(s) */ +	*cnt += 1; +#endif +#if defined(CONFIG_BOOTP_HOSTNAME) +	*e++  = 12;		/* Hostname */ +	*cnt += 1; +#endif +#if defined(CONFIG_BOOTP_BOOTFILESIZE) +	*e++  = 13;		/* Boot File Size */ +	*cnt += 1; +#endif +#if defined(CONFIG_BOOTP_BOOTPATH) +	*e++  = 17;		/* Boot path */ +	*cnt += 1; +#endif +#if defined(CONFIG_BOOTP_NISDOMAIN) +	*e++  = 40;		/* NIS Domain name request */ +	*cnt += 1; +#endif +#if defined(CONFIG_BOOTP_NTPSERVER) +	*e++  = 42; +	*cnt += 1; +#endif +	/* no options, so back up to avoid sending an empty request list */ +	if (*cnt == 0) +		e -= 2; + +	*e++  = 255;		/* End of the list */ + +	/* Pad to minimal length */ +#ifdef	CONFIG_DHCP_MIN_EXT_LEN +	while ((e - start) < CONFIG_DHCP_MIN_EXT_LEN) +		*e++ = 0; +#endif + +	return e - start; +} + +#else +/* + * Warning: no field size check - change CONFIG_BOOTP_* at your own risk! + */ +static int BootpExtended(u8 *e) +{ +	u8 *start = e; + +	*e++ = 99;		/* RFC1048 Magic Cookie */ +	*e++ = 130; +	*e++ = 83; +	*e++ = 99; + +#if defined(CONFIG_CMD_DHCP) +	*e++ = 53;		/* DHCP Message Type */ +	*e++ = 1; +	*e++ = DHCP_DISCOVER; + +	*e++ = 57;		/* Maximum DHCP Message Size */ +	*e++ = 2; +	*e++ = (576 - 312 + OPT_FIELD_SIZE) >> 16; +	*e++ = (576 - 312 + OPT_FIELD_SIZE) & 0xff; +#endif + +#if defined(CONFIG_BOOTP_VCI_STRING) || \ +	(defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_NET_VCI_STRING)) +#ifdef CONFIG_SPL_BUILD +	put_vci(e, CONFIG_SPL_NET_VCI_STRING); +#else +	put_vci(e, CONFIG_BOOTP_VCI_STRING); +#endif +#endif + +#if defined(CONFIG_BOOTP_SUBNETMASK) +	*e++ = 1;		/* Subnet mask request */ +	*e++ = 4; +	e   += 4; +#endif + +#if defined(CONFIG_BOOTP_GATEWAY) +	*e++ = 3;		/* Default gateway request */ +	*e++ = 4; +	e   += 4; +#endif + +#if defined(CONFIG_BOOTP_DNS) +	*e++ = 6;		/* Domain Name Server */ +	*e++ = 4; +	e   += 4; +#endif + +#if defined(CONFIG_BOOTP_HOSTNAME) +	*e++ = 12;		/* Host name request */ +	*e++ = 32; +	e   += 32; +#endif + +#if defined(CONFIG_BOOTP_BOOTFILESIZE) +	*e++ = 13;		/* Boot file size */ +	*e++ = 2; +	e   += 2; +#endif + +#if defined(CONFIG_BOOTP_BOOTPATH) +	*e++ = 17;		/* Boot path */ +	*e++ = 32; +	e   += 32; +#endif + +#if defined(CONFIG_BOOTP_NISDOMAIN) +	*e++ = 40;		/* NIS Domain name request */ +	*e++ = 32; +	e   += 32; +#endif +#if defined(CONFIG_BOOTP_NTPSERVER) +	*e++ = 42; +	*e++ = 4; +	e   += 4; +#endif + +	*e++ = 255;		/* End of the list */ + +	return e - start; +} +#endif + +void +BootpRequest(void) +{ +	uchar *pkt, *iphdr; +	struct Bootp_t *bp; +	int extlen, pktlen, iplen; +	int eth_hdr_size; +#ifdef CONFIG_BOOTP_RANDOM_DELAY +	ulong i, rand_ms; +#endif + +	bootstage_mark_name(BOOTSTAGE_ID_BOOTP_START, "bootp_start"); +#if defined(CONFIG_CMD_DHCP) +	dhcp_state = INIT; +#endif + +#ifdef CONFIG_BOOTP_RANDOM_DELAY		/* Random BOOTP delay */ +	if (BootpTry == 0) +		srand_mac(); + +	if (BootpTry <= 2)	/* Start with max 1024 * 1ms */ +		rand_ms = rand() >> (22 - BootpTry); +	else		/* After 3rd BOOTP request max 8192 * 1ms */ +		rand_ms = rand() >> 19; + +	printf("Random delay: %ld ms...\n", rand_ms); +	for (i = 0; i < rand_ms; i++) +		udelay(1000); /*Wait 1ms*/ + +#endif	/* CONFIG_BOOTP_RANDOM_DELAY */ + +	printf("BOOTP broadcast %d\n", ++BootpTry); +	pkt = NetTxPacket; +	memset((void *)pkt, 0, PKTSIZE); + +	eth_hdr_size = NetSetEther(pkt, NetBcastAddr, PROT_IP); +	pkt += eth_hdr_size; + +	/* +	 * Next line results in incorrect packet size being transmitted, +	 * resulting in errors in some DHCP servers, reporting missing bytes. +	 * Size must be set in packet header after extension length has been +	 * determined. +	 * C. Hallinan, DS4.COM, Inc. +	 */ +	/* net_set_udp_header(pkt, 0xFFFFFFFFL, PORT_BOOTPS, PORT_BOOTPC, +		sizeof (struct Bootp_t)); */ +	iphdr = pkt;	/* We need this later for net_set_udp_header() */ +	pkt += IP_UDP_HDR_SIZE; + +	bp = (struct Bootp_t *)pkt; +	bp->bp_op = OP_BOOTREQUEST; +	bp->bp_htype = HWT_ETHER; +	bp->bp_hlen = HWL_ETHER; +	bp->bp_hops = 0; +	bp->bp_secs = htons(get_timer(0) / 1000); +	NetWriteIP(&bp->bp_ciaddr, 0); +	NetWriteIP(&bp->bp_yiaddr, 0); +	NetWriteIP(&bp->bp_siaddr, 0); +	NetWriteIP(&bp->bp_giaddr, 0); +	memcpy(bp->bp_chaddr, NetOurEther, 6); +	copy_filename(bp->bp_file, BootFile, sizeof(bp->bp_file)); + +	/* Request additional information from the BOOTP/DHCP server */ +#if defined(CONFIG_CMD_DHCP) +	extlen = DhcpExtended((u8 *)bp->bp_vend, DHCP_DISCOVER, 0, 0); +#else +	extlen = BootpExtended((u8 *)bp->bp_vend); +#endif + +	/* +	 *	Bootp ID is the lower 4 bytes of our ethernet address +	 *	plus the current time in ms. +	 */ +	BootpID = ((ulong)NetOurEther[2] << 24) +		| ((ulong)NetOurEther[3] << 16) +		| ((ulong)NetOurEther[4] << 8) +		| (ulong)NetOurEther[5]; +	BootpID += get_timer(0); +	BootpID	 = htonl(BootpID); +	NetCopyLong(&bp->bp_id, &BootpID); + +	/* +	 * Calculate proper packet lengths taking into account the +	 * variable size of the options field +	 */ +	iplen = BOOTP_HDR_SIZE - OPT_FIELD_SIZE + extlen; +	pktlen = eth_hdr_size + IP_UDP_HDR_SIZE + iplen; +	net_set_udp_header(iphdr, 0xFFFFFFFFL, PORT_BOOTPS, PORT_BOOTPC, iplen); +	NetSetTimeout(SELECT_TIMEOUT, BootpTimeout); + +#if defined(CONFIG_CMD_DHCP) +	dhcp_state = SELECTING; +	net_set_udp_handler(DhcpHandler); +#else +	net_set_udp_handler(BootpHandler); +#endif +	NetSendPacket(NetTxPacket, pktlen); +} + +#if defined(CONFIG_CMD_DHCP) +static void DhcpOptionsProcess(uchar *popt, struct Bootp_t *bp) +{ +	uchar *end = popt + BOOTP_HDR_SIZE; +	int oplen, size; +#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET) +	int *to_ptr; +#endif + +	while (popt < end && *popt != 0xff) { +		oplen = *(popt + 1); +		switch (*popt) { +		case 1: +			NetCopyIP(&NetOurSubnetMask, (popt + 2)); +			break; +#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET) +		case 2:		/* Time offset	*/ +			to_ptr = &NetTimeOffset; +			NetCopyLong((ulong *)to_ptr, (ulong *)(popt + 2)); +			NetTimeOffset = ntohl(NetTimeOffset); +			break; +#endif +		case 3: +			NetCopyIP(&NetOurGatewayIP, (popt + 2)); +			break; +		case 6: +			NetCopyIP(&NetOurDNSIP, (popt + 2)); +#if defined(CONFIG_BOOTP_DNS2) +			if (*(popt + 1) > 4) +				NetCopyIP(&NetOurDNS2IP, (popt + 2 + 4)); +#endif +			break; +		case 12: +			size = truncate_sz("Host Name", +				sizeof(NetOurHostName), oplen); +			memcpy(&NetOurHostName, popt + 2, size); +			NetOurHostName[size] = 0; +			break; +		case 15:	/* Ignore Domain Name Option */ +			break; +		case 17: +			size = truncate_sz("Root Path", +				sizeof(NetOurRootPath), oplen); +			memcpy(&NetOurRootPath, popt + 2, size); +			NetOurRootPath[size] = 0; +			break; +		case 28:	/* Ignore Broadcast Address Option */ +			break; +#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER) +		case 42:	/* NTP server IP */ +			NetCopyIP(&NetNtpServerIP, (popt + 2)); +			break; +#endif +		case 51: +			NetCopyLong(&dhcp_leasetime, (ulong *) (popt + 2)); +			break; +		case 53:	/* Ignore Message Type Option */ +			break; +		case 54: +			NetCopyIP(&NetDHCPServerIP, (popt + 2)); +			break; +		case 58:	/* Ignore Renewal Time Option */ +			break; +		case 59:	/* Ignore Rebinding Time Option */ +			break; +		case 66:	/* Ignore TFTP server name */ +			break; +		case 67:	/* vendor opt bootfile */ +			/* +			 * I can't use dhcp_vendorex_proc here because I need +			 * to write into the bootp packet - even then I had to +			 * pass the bootp packet pointer into here as the +			 * second arg +			 */ +			size = truncate_sz("Opt Boot File", +					    sizeof(bp->bp_file), +					    oplen); +			if (bp->bp_file[0] == '\0' && size > 0) { +				/* +				 * only use vendor boot file if we didn't +				 * receive a boot file in the main non-vendor +				 * part of the packet - god only knows why +				 * some vendors chose not to use this perfectly +				 * good spot to store the boot file (join on +				 * Tru64 Unix) it seems mind bogglingly crazy +				 * to me +				 */ +				printf("*** WARNING: using vendor " +					"optional boot file\n"); +				memcpy(bp->bp_file, popt + 2, size); +				bp->bp_file[size] = '\0'; +			} +			break; +		default: +#if defined(CONFIG_BOOTP_VENDOREX) +			if (dhcp_vendorex_proc(popt)) +				break; +#endif +			printf("*** Unhandled DHCP Option in OFFER/ACK:" +				" %d\n", *popt); +			break; +		} +		popt += oplen + 2;	/* Process next option */ +	} +} + +static int DhcpMessageType(unsigned char *popt) +{ +	if (NetReadLong((ulong *)popt) != htonl(BOOTP_VENDOR_MAGIC)) +		return -1; + +	popt += 4; +	while (*popt != 0xff) { +		if (*popt == 53)	/* DHCP Message Type */ +			return *(popt + 2); +		popt += *(popt + 1) + 2;	/* Scan through all options */ +	} +	return -1; +} + +static void DhcpSendRequestPkt(struct Bootp_t *bp_offer) +{ +	uchar *pkt, *iphdr; +	struct Bootp_t *bp; +	int pktlen, iplen, extlen; +	int eth_hdr_size; +	IPaddr_t OfferedIP; + +	debug("DhcpSendRequestPkt: Sending DHCPREQUEST\n"); +	pkt = NetTxPacket; +	memset((void *)pkt, 0, PKTSIZE); + +	eth_hdr_size = NetSetEther(pkt, NetBcastAddr, PROT_IP); +	pkt += eth_hdr_size; + +	iphdr = pkt;	/* We'll need this later to set proper pkt size */ +	pkt += IP_UDP_HDR_SIZE; + +	bp = (struct Bootp_t *)pkt; +	bp->bp_op = OP_BOOTREQUEST; +	bp->bp_htype = HWT_ETHER; +	bp->bp_hlen = HWL_ETHER; +	bp->bp_hops = 0; +	bp->bp_secs = htons(get_timer(0) / 1000); +	/* Do not set the client IP, your IP, or server IP yet, since it +	 * hasn't been ACK'ed by the server yet */ + +	/* +	 * RFC3046 requires Relay Agents to discard packets with +	 * nonzero and offered giaddr +	 */ +	NetWriteIP(&bp->bp_giaddr, 0); + +	memcpy(bp->bp_chaddr, NetOurEther, 6); + +	/* +	 * ID is the id of the OFFER packet +	 */ + +	NetCopyLong(&bp->bp_id, &bp_offer->bp_id); + +	/* +	 * Copy options from OFFER packet if present +	 */ + +	/* Copy offered IP into the parameters request list */ +	NetCopyIP(&OfferedIP, &bp_offer->bp_yiaddr); +	extlen = DhcpExtended((u8 *)bp->bp_vend, DHCP_REQUEST, +		NetDHCPServerIP, OfferedIP); + +	iplen = BOOTP_HDR_SIZE - OPT_FIELD_SIZE + extlen; +	pktlen = eth_hdr_size + IP_UDP_HDR_SIZE + iplen; +	net_set_udp_header(iphdr, 0xFFFFFFFFL, PORT_BOOTPS, PORT_BOOTPC, iplen); + +#ifdef CONFIG_BOOTP_DHCP_REQUEST_DELAY +	udelay(CONFIG_BOOTP_DHCP_REQUEST_DELAY); +#endif	/* CONFIG_BOOTP_DHCP_REQUEST_DELAY */ +	debug("Transmitting DHCPREQUEST packet: len = %d\n", pktlen); +	NetSendPacket(NetTxPacket, pktlen); +} + +/* + *	Handle DHCP received packets. + */ +static void +DhcpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src, +	    unsigned len) +{ +	struct Bootp_t *bp = (struct Bootp_t *)pkt; + +	debug("DHCPHandler: got packet: (src=%d, dst=%d, len=%d) state: %d\n", +		src, dest, len, dhcp_state); + +	/* Filter out pkts we don't want */ +	if (BootpCheckPkt(pkt, dest, src, len)) +		return; + +	debug("DHCPHandler: got DHCP packet: (src=%d, dst=%d, len=%d) state:" +		" %d\n", src, dest, len, dhcp_state); + +	switch (dhcp_state) { +	case SELECTING: +		/* +		 * Wait an appropriate time for any potential DHCPOFFER packets +		 * to arrive.  Then select one, and generate DHCPREQUEST +		 * response.  If filename is in format we recognize, assume it +		 * is a valid OFFER from a server we want. +		 */ +		debug("DHCP: state=SELECTING bp_file: \"%s\"\n", bp->bp_file); +#ifdef CONFIG_SYS_BOOTFILE_PREFIX +		if (strncmp(bp->bp_file, +			    CONFIG_SYS_BOOTFILE_PREFIX, +			    strlen(CONFIG_SYS_BOOTFILE_PREFIX)) == 0) { +#endif	/* CONFIG_SYS_BOOTFILE_PREFIX */ + +			debug("TRANSITIONING TO REQUESTING STATE\n"); +			dhcp_state = REQUESTING; + +			if (NetReadLong((ulong *)&bp->bp_vend[0]) == +						htonl(BOOTP_VENDOR_MAGIC)) +				DhcpOptionsProcess((u8 *)&bp->bp_vend[4], bp); + +			NetSetTimeout(TIMEOUT, BootpTimeout); +			DhcpSendRequestPkt(bp); +#ifdef CONFIG_SYS_BOOTFILE_PREFIX +		} +#endif	/* CONFIG_SYS_BOOTFILE_PREFIX */ + +		return; +		break; +	case REQUESTING: +		debug("DHCP State: REQUESTING\n"); + +		if (DhcpMessageType((u8 *)bp->bp_vend) == DHCP_ACK) { +			if (NetReadLong((ulong *)&bp->bp_vend[0]) == +						htonl(BOOTP_VENDOR_MAGIC)) +				DhcpOptionsProcess((u8 *)&bp->bp_vend[4], bp); +			/* Store net params from reply */ +			BootpCopyNetParams(bp); +			dhcp_state = BOUND; +			printf("DHCP client bound to address %pI4\n", +				&NetOurIP); +			bootstage_mark_name(BOOTSTAGE_ID_BOOTP_STOP, +				"bootp_stop"); + +			net_auto_load(); +			return; +		} +		break; +	case BOUND: +		/* DHCP client bound to address */ +		break; +	default: +		puts("DHCP: INVALID STATE\n"); +		break; +	} + +} + +void DhcpRequest(void) +{ +	BootpRequest(); +} +#endif	/* CONFIG_CMD_DHCP */ diff --git a/roms/u-boot/net/bootp.h b/roms/u-boot/net/bootp.h new file mode 100644 index 00000000..ecbcc4d5 --- /dev/null +++ b/roms/u-boot/net/bootp.h @@ -0,0 +1,95 @@ +/* + *	Copied from LiMon - BOOTP. + * + *	Copyright 1994, 1995, 2000 Neil Russell. + *	(See License) + *	Copyright 2000 Paolo Scaffardi + */ + +#ifndef __BOOTP_H__ +#define __BOOTP_H__ + +#ifndef __NET_H__ +#include <net.h> +#endif /* __NET_H__ */ + +/**********************************************************************/ + +/* + *	BOOTP header. + */ +#if defined(CONFIG_CMD_DHCP) +/* Minimum DHCP Options size per RFC2131 - results in 576 byte pkt */ +#define OPT_FIELD_SIZE 312 +#if defined(CONFIG_BOOTP_VENDOREX) +extern u8 *dhcp_vendorex_prep(u8 *e); /*rtn new e after add own opts. */ +extern u8 *dhcp_vendorex_proc(u8 *e); /*rtn next e if mine,else NULL  */ +#endif +#else +#define OPT_FIELD_SIZE 64 +#endif + +struct Bootp_t { +	uchar		bp_op;		/* Operation			*/ +# define OP_BOOTREQUEST	1 +# define OP_BOOTREPLY	2 +	uchar		bp_htype;	/* Hardware type		*/ +# define HWT_ETHER	1 +	uchar		bp_hlen;	/* Hardware address length	*/ +# define HWL_ETHER	6 +	uchar		bp_hops;	/* Hop count (gateway thing)	*/ +	ulong		bp_id;		/* Transaction ID		*/ +	ushort		bp_secs;	/* Seconds since boot		*/ +	ushort		bp_spare1;	/* Alignment			*/ +	IPaddr_t	bp_ciaddr;	/* Client IP address		*/ +	IPaddr_t	bp_yiaddr;	/* Your (client) IP address	*/ +	IPaddr_t	bp_siaddr;	/* Server IP address		*/ +	IPaddr_t	bp_giaddr;	/* Gateway IP address		*/ +	uchar		bp_chaddr[16];	/* Client hardware address	*/ +	char		bp_sname[64];	/* Server host name		*/ +	char		bp_file[128];	/* Boot file name		*/ +	char		bp_vend[OPT_FIELD_SIZE]; /* Vendor information	*/ +}; + +#define BOOTP_HDR_SIZE	sizeof(struct Bootp_t) + +/**********************************************************************/ +/* + *	Global functions and variables. + */ + +/* bootp.c */ +extern ulong	BootpID;		/* ID of cur BOOTP request	*/ +extern char	BootFile[128];		/* Boot file name		*/ +extern int	BootpTry; + + +/* Send a BOOTP request */ +extern void BootpRequest(void); + +/****************** DHCP Support *********************/ +extern void DhcpRequest(void); + +/* DHCP States */ +typedef enum { INIT, +	       INIT_REBOOT, +	       REBOOTING, +	       SELECTING, +	       REQUESTING, +	       REBINDING, +	       BOUND, +	       RENEWING } dhcp_state_t; + +#define DHCP_DISCOVER 1 +#define DHCP_OFFER    2 +#define DHCP_REQUEST  3 +#define DHCP_DECLINE  4 +#define DHCP_ACK      5 +#define DHCP_NAK      6 +#define DHCP_RELEASE  7 + +#define SELECT_TIMEOUT 3000UL	/* Milliseconds to wait for offers */ + +/**********************************************************************/ + +#endif /* __BOOTP_H__ */ diff --git a/roms/u-boot/net/cdp.c b/roms/u-boot/net/cdp.c new file mode 100644 index 00000000..3d9559eb --- /dev/null +++ b/roms/u-boot/net/cdp.c @@ -0,0 +1,366 @@ +/* + *	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@denx.de + */ + +#include <common.h> +#include <net.h> +#if defined(CONFIG_CDP_VERSION) +#include <timestamp.h> +#endif + +#include "cdp.h" + +/* Ethernet bcast address */ +const uchar NetCDPAddr[6] = { 0x01, 0x00, 0x0c, 0xcc, 0xcc, 0xcc }; + +#define CDP_DEVICE_ID_TLV		0x0001 +#define CDP_ADDRESS_TLV			0x0002 +#define CDP_PORT_ID_TLV			0x0003 +#define CDP_CAPABILITIES_TLV		0x0004 +#define CDP_VERSION_TLV			0x0005 +#define CDP_PLATFORM_TLV		0x0006 +#define CDP_NATIVE_VLAN_TLV		0x000a +#define CDP_APPLIANCE_VLAN_TLV		0x000e +#define CDP_TRIGGER_TLV			0x000f +#define CDP_POWER_CONSUMPTION_TLV	0x0010 +#define CDP_SYSNAME_TLV			0x0014 +#define CDP_SYSOBJECT_TLV		0x0015 +#define CDP_MANAGEMENT_ADDRESS_TLV	0x0016 + +#define CDP_TIMEOUT			250UL	/* one packet every 250ms */ + +static int CDPSeq; +static int CDPOK; + +ushort CDPNativeVLAN; +ushort CDPApplianceVLAN; + +static const uchar CDP_SNAP_hdr[8] = { +	0xAA, 0xAA, 0x03, 0x00, 0x00, 0x0C, 0x20, 0x00 }; + +static ushort +CDP_compute_csum(const uchar *buff, ushort len) +{ +	ushort csum; +	int     odd; +	ulong   result = 0; +	ushort  leftover; +	ushort *p; + +	if (len > 0) { +		odd = 1 & (ulong)buff; +		if (odd) { +			result = *buff << 8; +			len--; +			buff++; +		} +		while (len > 1) { +			p = (ushort *)buff; +			result += *p++; +			buff = (uchar *)p; +			if (result & 0x80000000) +				result = (result & 0xFFFF) + (result >> 16); +			len -= 2; +		} +		if (len) { +			leftover = (signed short)(*(const signed char *)buff); +			/* +			 * CISCO SUCKS big time! (and blows too): +			 * CDP uses the IP checksum algorithm with a twist; +			 * for the last byte it *sign* extends and sums. +			 */ +			result = (result & 0xffff0000) | +				 ((result + leftover) & 0x0000ffff); +		} +		while (result >> 16) +			result = (result & 0xFFFF) + (result >> 16); + +		if (odd) +			result = ((result >> 8) & 0xff) | +				 ((result & 0xff) << 8); +	} + +	/* add up 16-bit and 17-bit words for 17+c bits */ +	result = (result & 0xffff) + (result >> 16); +	/* add up 16-bit and 2-bit for 16+c bit */ +	result = (result & 0xffff) + (result >> 16); +	/* add up carry.. */ +	result = (result & 0xffff) + (result >> 16); + +	/* negate */ +	csum = ~(ushort)result; + +	/* run time endian detection */ +	if (csum != htons(csum))	/* little endian */ +		csum = htons(csum); + +	return csum; +} + +static int +CDPSendTrigger(void) +{ +	uchar *pkt; +	ushort *s; +	ushort *cp; +	struct ethernet_hdr *et; +	int len; +	ushort chksum; +#if	defined(CONFIG_CDP_DEVICE_ID) || defined(CONFIG_CDP_PORT_ID)   || \ +	defined(CONFIG_CDP_VERSION)   || defined(CONFIG_CDP_PLATFORM) +	char buf[32]; +#endif + +	pkt = NetTxPacket; +	et = (struct ethernet_hdr *)pkt; + +	/* NOTE: trigger sent not on any VLAN */ + +	/* form ethernet header */ +	memcpy(et->et_dest, NetCDPAddr, 6); +	memcpy(et->et_src, NetOurEther, 6); + +	pkt += ETHER_HDR_SIZE; + +	/* SNAP header */ +	memcpy((uchar *)pkt, CDP_SNAP_hdr, sizeof(CDP_SNAP_hdr)); +	pkt += sizeof(CDP_SNAP_hdr); + +	/* CDP header */ +	*pkt++ = 0x02;				/* CDP version 2 */ +	*pkt++ = 180;				/* TTL */ +	s = (ushort *)pkt; +	cp = s; +	/* checksum (0 for later calculation) */ +	*s++ = htons(0); + +	/* CDP fields */ +#ifdef CONFIG_CDP_DEVICE_ID +	*s++ = htons(CDP_DEVICE_ID_TLV); +	*s++ = htons(CONFIG_CDP_DEVICE_ID); +	sprintf(buf, CONFIG_CDP_DEVICE_ID_PREFIX "%pm", NetOurEther); +	memcpy((uchar *)s, buf, 16); +	s += 16 / 2; +#endif + +#ifdef CONFIG_CDP_PORT_ID +	*s++ = htons(CDP_PORT_ID_TLV); +	memset(buf, 0, sizeof(buf)); +	sprintf(buf, CONFIG_CDP_PORT_ID, eth_get_dev_index()); +	len = strlen(buf); +	if (len & 1)	/* make it even */ +		len++; +	*s++ = htons(len + 4); +	memcpy((uchar *)s, buf, len); +	s += len / 2; +#endif + +#ifdef CONFIG_CDP_CAPABILITIES +	*s++ = htons(CDP_CAPABILITIES_TLV); +	*s++ = htons(8); +	*(ulong *)s = htonl(CONFIG_CDP_CAPABILITIES); +	s += 2; +#endif + +#ifdef CONFIG_CDP_VERSION +	*s++ = htons(CDP_VERSION_TLV); +	memset(buf, 0, sizeof(buf)); +	strcpy(buf, CONFIG_CDP_VERSION); +	len = strlen(buf); +	if (len & 1)	/* make it even */ +		len++; +	*s++ = htons(len + 4); +	memcpy((uchar *)s, buf, len); +	s += len / 2; +#endif + +#ifdef CONFIG_CDP_PLATFORM +	*s++ = htons(CDP_PLATFORM_TLV); +	memset(buf, 0, sizeof(buf)); +	strcpy(buf, CONFIG_CDP_PLATFORM); +	len = strlen(buf); +	if (len & 1)	/* make it even */ +		len++; +	*s++ = htons(len + 4); +	memcpy((uchar *)s, buf, len); +	s += len / 2; +#endif + +#ifdef CONFIG_CDP_TRIGGER +	*s++ = htons(CDP_TRIGGER_TLV); +	*s++ = htons(8); +	*(ulong *)s = htonl(CONFIG_CDP_TRIGGER); +	s += 2; +#endif + +#ifdef CONFIG_CDP_POWER_CONSUMPTION +	*s++ = htons(CDP_POWER_CONSUMPTION_TLV); +	*s++ = htons(6); +	*s++ = htons(CONFIG_CDP_POWER_CONSUMPTION); +#endif + +	/* length of ethernet packet */ +	len = (uchar *)s - ((uchar *)NetTxPacket + ETHER_HDR_SIZE); +	et->et_protlen = htons(len); + +	len = ETHER_HDR_SIZE + sizeof(CDP_SNAP_hdr); +	chksum = CDP_compute_csum((uchar *)NetTxPacket + len, +				  (uchar *)s - (NetTxPacket + len)); +	if (chksum == 0) +		chksum = 0xFFFF; +	*cp = htons(chksum); + +	NetSendPacket(NetTxPacket, (uchar *)s - NetTxPacket); +	return 0; +} + +static void +CDPTimeout(void) +{ +	CDPSeq++; + +	if (CDPSeq < 3) { +		NetSetTimeout(CDP_TIMEOUT, CDPTimeout); +		CDPSendTrigger(); +		return; +	} + +	/* if not OK try again */ +	if (!CDPOK) +		NetStartAgain(); +	else +		net_set_state(NETLOOP_SUCCESS); +} + +void cdp_receive(const uchar *pkt, unsigned len) +{ +	const uchar *t; +	const ushort *ss; +	ushort type, tlen; +	ushort vlan, nvlan; + +	/* minimum size? */ +	if (len < sizeof(CDP_SNAP_hdr) + 4) +		goto pkt_short; + +	/* check for valid CDP SNAP header */ +	if (memcmp(pkt, CDP_SNAP_hdr, sizeof(CDP_SNAP_hdr)) != 0) +		return; + +	pkt += sizeof(CDP_SNAP_hdr); +	len -= sizeof(CDP_SNAP_hdr); + +	/* Version of CDP protocol must be >= 2 and TTL != 0 */ +	if (pkt[0] < 0x02 || pkt[1] == 0) +		return; + +	/* +	 * if version is greater than 0x02 maybe we'll have a problem; +	 * output a warning +	 */ +	if (pkt[0] != 0x02) +		printf("**WARNING: CDP packet received with a protocol version " +				"%d > 2\n", pkt[0] & 0xff); + +	if (CDP_compute_csum(pkt, len) != 0) +		return; + +	pkt += 4; +	len -= 4; + +	vlan = htons(-1); +	nvlan = htons(-1); +	while (len > 0) { +		if (len < 4) +			goto pkt_short; + +		ss = (const ushort *)pkt; +		type = ntohs(ss[0]); +		tlen = ntohs(ss[1]); +		if (tlen > len) +			goto pkt_short; + +		pkt += tlen; +		len -= tlen; + +		ss += 2;	/* point ss to the data of the TLV */ +		tlen -= 4; + +		switch (type) { +		case CDP_DEVICE_ID_TLV: +			break; +		case CDP_ADDRESS_TLV: +			break; +		case CDP_PORT_ID_TLV: +			break; +		case CDP_CAPABILITIES_TLV: +			break; +		case CDP_VERSION_TLV: +			break; +		case CDP_PLATFORM_TLV: +			break; +		case CDP_NATIVE_VLAN_TLV: +			nvlan = *ss; +			break; +		case CDP_APPLIANCE_VLAN_TLV: +			t = (const uchar *)ss; +			while (tlen > 0) { +				if (tlen < 3) +					goto pkt_short; + +				ss = (const ushort *)(t + 1); + +#ifdef CONFIG_CDP_APPLIANCE_VLAN_TYPE +				if (t[0] == CONFIG_CDP_APPLIANCE_VLAN_TYPE) +					vlan = *ss; +#else +				/* XXX will this work; dunno */ +				vlan = ntohs(*ss); +#endif +				t += 3; tlen -= 3; +			} +			break; +		case CDP_TRIGGER_TLV: +			break; +		case CDP_POWER_CONSUMPTION_TLV: +			break; +		case CDP_SYSNAME_TLV: +			break; +		case CDP_SYSOBJECT_TLV: +			break; +		case CDP_MANAGEMENT_ADDRESS_TLV: +			break; +		} +	} + +	CDPApplianceVLAN = vlan; +	CDPNativeVLAN = nvlan; + +	CDPOK = 1; +	return; + + pkt_short: +	printf("** CDP packet is too short\n"); +	return; +} + +void +CDPStart(void) +{ +	printf("Using %s device\n", eth_get_name()); +	CDPSeq = 0; +	CDPOK = 0; + +	CDPNativeVLAN = htons(-1); +	CDPApplianceVLAN = htons(-1); + +	NetSetTimeout(CDP_TIMEOUT, CDPTimeout); + +	CDPSendTrigger(); +} diff --git a/roms/u-boot/net/cdp.h b/roms/u-boot/net/cdp.h new file mode 100644 index 00000000..ec7315af --- /dev/null +++ b/roms/u-boot/net/cdp.h @@ -0,0 +1,21 @@ +/* + *	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@denx.de + */ + +#if defined(CONFIG_CMD_CDP) + +#ifndef __CDP_H__ +#define __CDP_H__ + +void CDPStart(void); +/* Process a received CDP packet */ +void cdp_receive(const uchar *pkt, unsigned len); + +#endif /* __CDP_H__ */ +#endif diff --git a/roms/u-boot/net/dns.c b/roms/u-boot/net/dns.c new file mode 100644 index 00000000..ff9ddffc --- /dev/null +++ b/roms/u-boot/net/dns.c @@ -0,0 +1,206 @@ +/* + * DNS support driver + * + * Copyright (c) 2008 Pieter Voorthuijsen <pieter.voorthuijsen@prodrive.nl> + * Copyright (c) 2009 Robin Getz <rgetz@blackfin.uclinux.org> + * + * This is a simple DNS implementation for U-Boot. It will use the first IP + * in the DNS response as NetServerIP. This can then be used for any other + * network related activities. + * + * The packet handling is partly based on TADNS, original copyrights + * follow below. + * + */ + +/* + * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com> + * + * "THE BEER-WARE LICENSE" (Revision 42): + * Sergey Lyubka wrote this file.  As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. + */ + +#include <common.h> +#include <command.h> +#include <net.h> +#include <asm/unaligned.h> + +#include "dns.h" + +char *NetDNSResolve;	/* The host to resolve  */ +char *NetDNSenvvar;	/* The envvar to store the answer in */ + +static int DnsOurPort; + +static void +DnsSend(void) +{ +	struct header *header; +	int n, name_len; +	uchar *p, *pkt; +	const char *s; +	const char *name; +	enum dns_query_type qtype = DNS_A_RECORD; + +	name = NetDNSResolve; +	pkt = p = (uchar *)(NetTxPacket + NetEthHdrSize() + IP_UDP_HDR_SIZE); + +	/* Prepare DNS packet header */ +	header           = (struct header *) pkt; +	header->tid      = 1; +	header->flags    = htons(0x100);	/* standard query */ +	header->nqueries = htons(1);		/* Just one query */ +	header->nanswers = 0; +	header->nauth    = 0; +	header->nother   = 0; + +	/* Encode DNS name */ +	name_len = strlen(name); +	p = (uchar *) &header->data;	/* For encoding host name into packet */ + +	do { +		s = strchr(name, '.'); +		if (!s) +			s = name + name_len; + +		n = s - name;			/* Chunk length */ +		*p++ = n;			/* Copy length  */ +		memcpy(p, name, n);		/* Copy chunk   */ +		p += n; + +		if (*s == '.') +			n++; + +		name += n; +		name_len -= n; +	} while (*s != '\0'); + +	*p++ = 0;			/* Mark end of host name */ +	*p++ = 0;			/* Some servers require double null */ +	*p++ = (unsigned char) qtype;	/* Query Type */ + +	*p++ = 0; +	*p++ = 1;				/* Class: inet, 0x0001 */ + +	n = p - pkt;				/* Total packet length */ +	debug("Packet size %d\n", n); + +	DnsOurPort = random_port(); + +	NetSendUDPPacket(NetServerEther, NetOurDNSIP, DNS_SERVICE_PORT, +		DnsOurPort, n); +	debug("DNS packet sent\n"); +} + +static void +DnsTimeout(void) +{ +	puts("Timeout\n"); +	net_set_state(NETLOOP_FAIL); +} + +static void +DnsHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src, unsigned len) +{ +	struct header *header; +	const unsigned char *p, *e, *s; +	u16 type, i; +	int found, stop, dlen; +	char IPStr[22]; +	IPaddr_t IPAddress; + + +	debug("%s\n", __func__); +	if (dest != DnsOurPort) +		return; + +	for (i = 0; i < len; i += 4) +		debug("0x%p - 0x%.2x  0x%.2x  0x%.2x  0x%.2x\n", +			pkt+i, pkt[i], pkt[i+1], pkt[i+2], pkt[i+3]); + +	/* We sent one query. We want to have a single answer: */ +	header = (struct header *) pkt; +	if (ntohs(header->nqueries) != 1) +		return; + +	/* Received 0 answers */ +	if (header->nanswers == 0) { +		puts("DNS: host not found\n"); +		net_set_state(NETLOOP_SUCCESS); +		return; +	} + +	/* Skip host name */ +	s = &header->data[0]; +	e = pkt + len; +	for (p = s; p < e && *p != '\0'; p++) +		continue; + +	/* We sent query class 1, query type 1 */ +	if (&p[5] > e || get_unaligned_be16(p+1) != DNS_A_RECORD) { +		puts("DNS: response was not an A record\n"); +		net_set_state(NETLOOP_SUCCESS); +		return; +	} + +	/* Go to the first answer section */ +	p += 5; + +	/* Loop through the answers, we want A type answer */ +	for (found = stop = 0; !stop && &p[12] < e; ) { + +		/* Skip possible name in CNAME answer */ +		if (*p != 0xc0) { +			while (*p && &p[12] < e) +				p++; +			p--; +		} +		debug("Name (Offset in header): %d\n", p[1]); + +		type = get_unaligned_be16(p+2); +		debug("type = %d\n", type); +		if (type == DNS_CNAME_RECORD) { +			/* CNAME answer. shift to the next section */ +			debug("Found canonical name\n"); +			dlen = get_unaligned_be16(p+10); +			debug("dlen = %d\n", dlen); +			p += 12 + dlen; +		} else if (type == DNS_A_RECORD) { +			debug("Found A-record\n"); +			found = stop = 1; +		} else { +			debug("Unknown type\n"); +			stop = 1; +		} +	} + +	if (found && &p[12] < e) { + +		dlen = get_unaligned_be16(p+10); +		p += 12; +		memcpy(&IPAddress, p, 4); + +		if (p + dlen <= e) { +			ip_to_string(IPAddress, IPStr); +			printf("%s\n", IPStr); +			if (NetDNSenvvar) +				setenv(NetDNSenvvar, IPStr); +		} else +			puts("server responded with invalid IP number\n"); +	} + +	net_set_state(NETLOOP_SUCCESS); +} + +void +DnsStart(void) +{ +	debug("%s\n", __func__); + +	NetSetTimeout(DNS_TIMEOUT, DnsTimeout); +	net_set_udp_handler(DnsHandler); + +	DnsSend(); +} diff --git a/roms/u-boot/net/dns.h b/roms/u-boot/net/dns.h new file mode 100644 index 00000000..dbc3890d --- /dev/null +++ b/roms/u-boot/net/dns.h @@ -0,0 +1,36 @@ +/* + * (C) Masami Komiya <mkomiya@sonare.it> 2005 + *  Copyright 2009, Robin Getz <rgetz@blackfin.uclinux.org> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#ifndef __DNS_H__ +#define __DNS_H__ + +#define DNS_SERVICE_PORT 53 +#define DNS_TIMEOUT      10000UL + +/* http://en.wikipedia.org/wiki/List_of_DNS_record_types */ +enum dns_query_type { +	DNS_A_RECORD = 0x01, +	DNS_CNAME_RECORD = 0x05, +	DNS_MX_RECORD = 0x0f, +}; + +/* + * DNS network packet + */ +struct header { +	uint16_t	tid;		/* Transaction ID */ +	uint16_t	flags;		/* Flags */ +	uint16_t	nqueries;	/* Questions */ +	uint16_t	nanswers;	/* Answers */ +	uint16_t	nauth;		/* Authority PRs */ +	uint16_t	nother;		/* Other PRs */ +	unsigned char	data[1];	/* Data, variable length */ +}; + +extern void DnsStart(void);		/* Begin DNS */ + +#endif diff --git a/roms/u-boot/net/eth.c b/roms/u-boot/net/eth.c new file mode 100644 index 00000000..99386e3e --- /dev/null +++ b/roms/u-boot/net/eth.c @@ -0,0 +1,531 @@ +/* + * (C) Copyright 2001-2010 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <net.h> +#include <miiphy.h> +#include <phy.h> + +void eth_parse_enetaddr(const char *addr, uchar *enetaddr) +{ +	char *end; +	int i; + +	for (i = 0; i < 6; ++i) { +		enetaddr[i] = addr ? simple_strtoul(addr, &end, 16) : 0; +		if (addr) +			addr = (*end) ? end + 1 : end; +	} +} + +int eth_getenv_enetaddr(char *name, uchar *enetaddr) +{ +	eth_parse_enetaddr(getenv(name), enetaddr); +	return is_valid_ether_addr(enetaddr); +} + +int eth_setenv_enetaddr(char *name, const uchar *enetaddr) +{ +	char buf[20]; + +	sprintf(buf, "%pM", enetaddr); + +	return setenv(name, buf); +} + +int eth_getenv_enetaddr_by_index(const char *base_name, int index, +				 uchar *enetaddr) +{ +	char enetvar[32]; +	sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index); +	return eth_getenv_enetaddr(enetvar, enetaddr); +} + +static inline int eth_setenv_enetaddr_by_index(const char *base_name, int index, +				 uchar *enetaddr) +{ +	char enetvar[32]; +	sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index); +	return eth_setenv_enetaddr(enetvar, enetaddr); +} + + +static int eth_mac_skip(int index) +{ +	char enetvar[15]; +	char *skip_state; +	sprintf(enetvar, index ? "eth%dmacskip" : "ethmacskip", index); +	return ((skip_state = getenv(enetvar)) != NULL); +} + +/* + * CPU and board-specific Ethernet initializations.  Aliased function + * signals caller to move on + */ +static int __def_eth_init(bd_t *bis) +{ +	return -1; +} +int cpu_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init"))); +int board_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init"))); + +#ifdef CONFIG_API +static struct { +	uchar data[PKTSIZE]; +	int length; +} eth_rcv_bufs[PKTBUFSRX]; + +static unsigned int eth_rcv_current, eth_rcv_last; +#endif + +static struct eth_device *eth_devices; +struct eth_device *eth_current; + +struct eth_device *eth_get_dev_by_name(const char *devname) +{ +	struct eth_device *dev, *target_dev; + +	BUG_ON(devname == NULL); + +	if (!eth_devices) +		return NULL; + +	dev = eth_devices; +	target_dev = NULL; +	do { +		if (strcmp(devname, dev->name) == 0) { +			target_dev = dev; +			break; +		} +		dev = dev->next; +	} while (dev != eth_devices); + +	return target_dev; +} + +struct eth_device *eth_get_dev_by_index(int index) +{ +	struct eth_device *dev, *target_dev; + +	if (!eth_devices) +		return NULL; + +	dev = eth_devices; +	target_dev = NULL; +	do { +		if (dev->index == index) { +			target_dev = dev; +			break; +		} +		dev = dev->next; +	} while (dev != eth_devices); + +	return target_dev; +} + +int eth_get_dev_index(void) +{ +	if (!eth_current) +		return -1; + +	return eth_current->index; +} + +static void eth_current_changed(void) +{ +	char *act = getenv("ethact"); +	/* update current ethernet name */ +	if (eth_current) { +		if (act == NULL || strcmp(act, eth_current->name) != 0) +			setenv("ethact", eth_current->name); +	} +	/* +	 * remove the variable completely if there is no active +	 * interface +	 */ +	else if (act != NULL) +		setenv("ethact", NULL); +} + +int eth_write_hwaddr(struct eth_device *dev, const char *base_name, +		   int eth_number) +{ +	unsigned char env_enetaddr[6]; +	int ret = 0; + +	eth_getenv_enetaddr_by_index(base_name, eth_number, env_enetaddr); + +	if (memcmp(env_enetaddr, "\0\0\0\0\0\0", 6)) { +		if (memcmp(dev->enetaddr, "\0\0\0\0\0\0", 6) && +				memcmp(dev->enetaddr, env_enetaddr, 6)) { +			printf("\nWarning: %s MAC addresses don't match:\n", +				dev->name); +			printf("Address in SROM is         %pM\n", +				dev->enetaddr); +			printf("Address in environment is  %pM\n", +				env_enetaddr); +		} + +		memcpy(dev->enetaddr, env_enetaddr, 6); +	} else if (is_valid_ether_addr(dev->enetaddr)) { +		eth_setenv_enetaddr_by_index(base_name, eth_number, +					     dev->enetaddr); +		printf("\nWarning: %s using MAC address from net device\n", +			dev->name); +	} + +	if (dev->write_hwaddr && +			!eth_mac_skip(eth_number)) { +		if (!is_valid_ether_addr(dev->enetaddr)) +			return -1; + +		ret = dev->write_hwaddr(dev); +	} + +	return ret; +} + +int eth_register(struct eth_device *dev) +{ +	struct eth_device *d; +	static int index; + +	assert(strlen(dev->name) < sizeof(dev->name)); + +	if (!eth_devices) { +		eth_current = eth_devices = dev; +		eth_current_changed(); +	} else { +		for (d = eth_devices; d->next != eth_devices; d = d->next) +			; +		d->next = dev; +	} + +	dev->state = ETH_STATE_INIT; +	dev->next  = eth_devices; +	dev->index = index++; + +	return 0; +} + +int eth_unregister(struct eth_device *dev) +{ +	struct eth_device *cur; + +	/* No device */ +	if (!eth_devices) +		return -1; + +	for (cur = eth_devices; cur->next != eth_devices && cur->next != dev; +	     cur = cur->next) +		; + +	/* Device not found */ +	if (cur->next != dev) +		return -1; + +	cur->next = dev->next; + +	if (eth_devices == dev) +		eth_devices = dev->next == eth_devices ? NULL : dev->next; + +	if (eth_current == dev) { +		eth_current = eth_devices; +		eth_current_changed(); +	} + +	return 0; +} + +static void eth_env_init(bd_t *bis) +{ +	const char *s; + +	if ((s = getenv("bootfile")) != NULL) +		copy_filename(BootFile, s, sizeof(BootFile)); +} + +int eth_initialize(bd_t *bis) +{ +	int num_devices = 0; +	eth_devices = NULL; +	eth_current = NULL; + +	bootstage_mark(BOOTSTAGE_ID_NET_ETH_START); +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB) +	miiphy_init(); +#endif + +#ifdef CONFIG_PHYLIB +	phy_init(); +#endif + +	eth_env_init(bis); + +	/* +	 * If board-specific initialization exists, call it. +	 * If not, call a CPU-specific one +	 */ +	if (board_eth_init != __def_eth_init) { +		if (board_eth_init(bis) < 0) +			printf("Board Net Initialization Failed\n"); +	} else if (cpu_eth_init != __def_eth_init) { +		if (cpu_eth_init(bis) < 0) +			printf("CPU Net Initialization Failed\n"); +	} else +		printf("Net Initialization Skipped\n"); + +	if (!eth_devices) { +		puts("No ethernet found.\n"); +		bootstage_error(BOOTSTAGE_ID_NET_ETH_START); +	} else { +		struct eth_device *dev = eth_devices; +		char *ethprime = getenv("ethprime"); + +		bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT); +		do { +			if (dev->index) +				puts(", "); + +			printf("%s", dev->name); + +			if (ethprime && strcmp(dev->name, ethprime) == 0) { +				eth_current = dev; +				puts(" [PRIME]"); +			} + +			if (strchr(dev->name, ' ')) +				puts("\nWarning: eth device name has a space!" +					"\n"); + +			if (eth_write_hwaddr(dev, "eth", dev->index)) +				puts("\nWarning: failed to set MAC address\n"); + +			dev = dev->next; +			num_devices++; +		} while (dev != eth_devices); + +		eth_current_changed(); +		putc('\n'); +	} + +	return num_devices; +} + +#ifdef CONFIG_MCAST_TFTP +/* Multicast. + * mcast_addr: multicast ipaddr from which multicast Mac is made + * join: 1=join, 0=leave. + */ +int eth_mcast_join(IPaddr_t mcast_ip, u8 join) +{ +	u8 mcast_mac[6]; +	if (!eth_current || !eth_current->mcast) +		return -1; +	mcast_mac[5] = htonl(mcast_ip) & 0xff; +	mcast_mac[4] = (htonl(mcast_ip)>>8) & 0xff; +	mcast_mac[3] = (htonl(mcast_ip)>>16) & 0x7f; +	mcast_mac[2] = 0x5e; +	mcast_mac[1] = 0x0; +	mcast_mac[0] = 0x1; +	return eth_current->mcast(eth_current, mcast_mac, join); +} + +/* the 'way' for ethernet-CRC-32. Spliced in from Linux lib/crc32.c + * and this is the ethernet-crc method needed for TSEC -- and perhaps + * some other adapter -- hash tables + */ +#define CRCPOLY_LE 0xedb88320 +u32 ether_crc(size_t len, unsigned char const *p) +{ +	int i; +	u32 crc; +	crc = ~0; +	while (len--) { +		crc ^= *p++; +		for (i = 0; i < 8; i++) +			crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); +	} +	/* an reverse the bits, cuz of way they arrive -- last-first */ +	crc = (crc >> 16) | (crc << 16); +	crc = (crc >> 8 & 0x00ff00ff) | (crc << 8 & 0xff00ff00); +	crc = (crc >> 4 & 0x0f0f0f0f) | (crc << 4 & 0xf0f0f0f0); +	crc = (crc >> 2 & 0x33333333) | (crc << 2 & 0xcccccccc); +	crc = (crc >> 1 & 0x55555555) | (crc << 1 & 0xaaaaaaaa); +	return crc; +} + +#endif + + +int eth_init(bd_t *bis) +{ +	struct eth_device *old_current, *dev; + +	if (!eth_current) { +		puts("No ethernet found.\n"); +		return -1; +	} + +	/* Sync environment with network devices */ +	dev = eth_devices; +	do { +		uchar env_enetaddr[6]; + +		if (eth_getenv_enetaddr_by_index("eth", dev->index, +						 env_enetaddr)) +			memcpy(dev->enetaddr, env_enetaddr, 6); + +		dev = dev->next; +	} while (dev != eth_devices); + +	old_current = eth_current; +	do { +		debug("Trying %s\n", eth_current->name); + +		if (eth_current->init(eth_current, bis) >= 0) { +			eth_current->state = ETH_STATE_ACTIVE; + +			return 0; +		} +		debug("FAIL\n"); + +		eth_try_another(0); +	} while (old_current != eth_current); + +	return -1; +} + +void eth_halt(void) +{ +	if (!eth_current) +		return; + +	eth_current->halt(eth_current); + +	eth_current->state = ETH_STATE_PASSIVE; +} + +int eth_send(void *packet, int length) +{ +	if (!eth_current) +		return -1; + +	return eth_current->send(eth_current, packet, length); +} + +int eth_rx(void) +{ +	if (!eth_current) +		return -1; + +	return eth_current->recv(eth_current); +} + +#ifdef CONFIG_API +static void eth_save_packet(void *packet, int length) +{ +	char *p = packet; +	int i; + +	if ((eth_rcv_last+1) % PKTBUFSRX == eth_rcv_current) +		return; + +	if (PKTSIZE < length) +		return; + +	for (i = 0; i < length; i++) +		eth_rcv_bufs[eth_rcv_last].data[i] = p[i]; + +	eth_rcv_bufs[eth_rcv_last].length = length; +	eth_rcv_last = (eth_rcv_last + 1) % PKTBUFSRX; +} + +int eth_receive(void *packet, int length) +{ +	char *p = packet; +	void *pp = push_packet; +	int i; + +	if (eth_rcv_current == eth_rcv_last) { +		push_packet = eth_save_packet; +		eth_rx(); +		push_packet = pp; + +		if (eth_rcv_current == eth_rcv_last) +			return -1; +	} + +	length = min(eth_rcv_bufs[eth_rcv_current].length, length); + +	for (i = 0; i < length; i++) +		p[i] = eth_rcv_bufs[eth_rcv_current].data[i]; + +	eth_rcv_current = (eth_rcv_current + 1) % PKTBUFSRX; +	return length; +} +#endif /* CONFIG_API */ + +void eth_try_another(int first_restart) +{ +	static struct eth_device *first_failed; +	char *ethrotate; + +	/* +	 * Do not rotate between network interfaces when +	 * 'ethrotate' variable is set to 'no'. +	 */ +	ethrotate = getenv("ethrotate"); +	if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0)) +		return; + +	if (!eth_current) +		return; + +	if (first_restart) +		first_failed = eth_current; + +	eth_current = eth_current->next; + +	eth_current_changed(); + +	if (first_failed == eth_current) +		NetRestartWrap = 1; +} + +void eth_set_current(void) +{ +	static char *act; +	static int  env_changed_id; +	struct eth_device *old_current; +	int	env_id; + +	if (!eth_current)	/* XXX no current */ +		return; + +	env_id = get_env_id(); +	if ((act == NULL) || (env_changed_id != env_id)) { +		act = getenv("ethact"); +		env_changed_id = env_id; +	} +	if (act != NULL) { +		old_current = eth_current; +		do { +			if (strcmp(eth_current->name, act) == 0) +				return; +			eth_current = eth_current->next; +		} while (old_current != eth_current); +	} + +	eth_current_changed(); +} + +char *eth_get_name(void) +{ +	return eth_current ? eth_current->name : "unknown"; +} diff --git a/roms/u-boot/net/link_local.c b/roms/u-boot/net/link_local.c new file mode 100644 index 00000000..4152fae5 --- /dev/null +++ b/roms/u-boot/net/link_local.c @@ -0,0 +1,345 @@ +/* + * RFC3927 ZeroConf IPv4 Link-Local addressing + * (see <http://www.zeroconf.org/>) + * + * Copied from BusyBox - networking/zcip.c + * + * Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com) + * Copyright (C) 2004 by David Brownell + * Copyright (C) 2010 by Joe Hershberger + * + * Licensed under the GPL v2 or later + */ + +#include <common.h> +#include <net.h> +#include "arp.h" +#include "net_rand.h" + +/* We don't need more than 32 bits of the counter */ +#define MONOTONIC_MS() ((unsigned)get_timer(0) * (1000 / CONFIG_SYS_HZ)) + +enum { +/* 169.254.0.0 */ +	LINKLOCAL_ADDR = 0xa9fe0000, + +	IN_CLASSB_NET = 0xffff0000, +	IN_CLASSB_HOST = 0x0000ffff, + +/* protocol timeout parameters, specified in seconds */ +	PROBE_WAIT = 1, +	PROBE_MIN = 1, +	PROBE_MAX = 2, +	PROBE_NUM = 3, +	MAX_CONFLICTS = 10, +	RATE_LIMIT_INTERVAL = 60, +	ANNOUNCE_WAIT = 2, +	ANNOUNCE_NUM = 2, +	ANNOUNCE_INTERVAL = 2, +	DEFEND_INTERVAL = 10 +}; + +/* States during the configuration process. */ +static enum ll_state_t { +	PROBE = 0, +	RATE_LIMIT_PROBE, +	ANNOUNCE, +	MONITOR, +	DEFEND, +	DISABLED +} state = DISABLED; + +static IPaddr_t ip; +static int timeout_ms = -1; +static unsigned deadline_ms; +static unsigned conflicts; +static unsigned nprobes; +static unsigned nclaims; +static int ready; +static unsigned int seed; + +static void link_local_timeout(void); + +/** + * Pick a random link local IP address on 169.254/16, except that + * the first and last 256 addresses are reserved. + */ +static IPaddr_t pick(void) +{ +	unsigned tmp; + +	do { +		tmp = rand_r(&seed) & IN_CLASSB_HOST; +	} while (tmp > (IN_CLASSB_HOST - 0x0200)); +	return (IPaddr_t) htonl((LINKLOCAL_ADDR + 0x0100) + tmp); +} + +/** + * Return milliseconds of random delay, up to "secs" seconds. + */ +static inline unsigned random_delay_ms(unsigned secs) +{ +	return rand_r(&seed) % (secs * 1000); +} + +static void configure_wait(void) +{ +	if (timeout_ms == -1) +		return; + +	/* poll, being ready to adjust current timeout */ +	if (!timeout_ms) +		timeout_ms = random_delay_ms(PROBE_WAIT); + +	/* set deadline_ms to the point in time when we timeout */ +	deadline_ms = MONOTONIC_MS() + timeout_ms; + +	debug_cond(DEBUG_DEV_PKT, "...wait %d %s nprobes=%u, nclaims=%u\n", +			timeout_ms, eth_get_name(), nprobes, nclaims); + +	NetSetTimeout(timeout_ms, link_local_timeout); +} + +void link_local_start(void) +{ +	ip = getenv_IPaddr("llipaddr"); +	if (ip != 0 && (ntohl(ip) & IN_CLASSB_NET) != LINKLOCAL_ADDR) { +		puts("invalid link address"); +		net_set_state(NETLOOP_FAIL); +		return; +	} +	NetOurSubnetMask = IN_CLASSB_NET; + +	seed = seed_mac(); +	if (ip == 0) +		ip = pick(); + +	state = PROBE; +	timeout_ms = 0; +	conflicts = 0; +	nprobes = 0; +	nclaims = 0; +	ready = 0; + +	configure_wait(); +} + +static void link_local_timeout(void) +{ +	switch (state) { +	case PROBE: +		/* timeouts in the PROBE state mean no conflicting ARP packets +		   have been received, so we can progress through the states */ +		if (nprobes < PROBE_NUM) { +			nprobes++; +			debug_cond(DEBUG_LL_STATE, "probe/%u %s@%pI4\n", +					nprobes, eth_get_name(), &ip); +			arp_raw_request(0, NetEtherNullAddr, ip); +			timeout_ms = PROBE_MIN * 1000; +			timeout_ms += random_delay_ms(PROBE_MAX - PROBE_MIN); +		} else { +			/* Switch to announce state */ +			state = ANNOUNCE; +			nclaims = 0; +			debug_cond(DEBUG_LL_STATE, "announce/%u %s@%pI4\n", +					nclaims, eth_get_name(), &ip); +			arp_raw_request(ip, NetOurEther, ip); +			timeout_ms = ANNOUNCE_INTERVAL * 1000; +		} +		break; +	case RATE_LIMIT_PROBE: +		/* timeouts in the RATE_LIMIT_PROBE state mean no conflicting +		   ARP packets have been received, so we can move immediately +		   to the announce state */ +		state = ANNOUNCE; +		nclaims = 0; +		debug_cond(DEBUG_LL_STATE, "announce/%u %s@%pI4\n", +				nclaims, eth_get_name(), &ip); +		arp_raw_request(ip, NetOurEther, ip); +		timeout_ms = ANNOUNCE_INTERVAL * 1000; +		break; +	case ANNOUNCE: +		/* timeouts in the ANNOUNCE state mean no conflicting ARP +		   packets have been received, so we can progress through +		   the states */ +		if (nclaims < ANNOUNCE_NUM) { +			nclaims++; +			debug_cond(DEBUG_LL_STATE, "announce/%u %s@%pI4\n", +					nclaims, eth_get_name(), &ip); +			arp_raw_request(ip, NetOurEther, ip); +			timeout_ms = ANNOUNCE_INTERVAL * 1000; +		} else { +			/* Switch to monitor state */ +			state = MONITOR; +			printf("Successfully assigned %pI4\n", &ip); +			NetCopyIP(&NetOurIP, &ip); +			ready = 1; +			conflicts = 0; +			timeout_ms = -1; +			/* Never timeout in the monitor state */ +			NetSetTimeout(0, NULL); + +			/* NOTE: all other exit paths should deconfig ... */ +			net_set_state(NETLOOP_SUCCESS); +			return; +		} +		break; +	case DEFEND: +		/* We won!  No ARP replies, so just go back to monitor */ +		state = MONITOR; +		timeout_ms = -1; +		conflicts = 0; +		break; +	default: +		/* Invalid, should never happen.  Restart the whole protocol */ +		state = PROBE; +		ip = pick(); +		timeout_ms = 0; +		nprobes = 0; +		nclaims = 0; +		break; +	} +	configure_wait(); +} + +void link_local_receive_arp(struct arp_hdr *arp, int len) +{ +	int source_ip_conflict; +	int target_ip_conflict; +	IPaddr_t null_ip = 0; + +	if (state == DISABLED) +		return; + +	/* We need to adjust the timeout in case we didn't receive a +	   conflicting packet. */ +	if (timeout_ms > 0) { +		unsigned diff = deadline_ms - MONOTONIC_MS(); +		if ((int)(diff) < 0) { +			/* Current time is greater than the expected timeout +			   time. This should never happen */ +			debug_cond(DEBUG_LL_STATE, +				"missed an expected timeout\n"); +			timeout_ms = 0; +		} else { +			debug_cond(DEBUG_INT_STATE, "adjusting timeout\n"); +			timeout_ms = diff | 1; /* never 0 */ +		} +	} +#if 0 + /* XXX Don't bother with ethernet link just yet */ +	if ((fds[0].revents & POLLIN) == 0) { +		if (fds[0].revents & POLLERR) { +			/* +			 * FIXME: links routinely go down; +			 */ +			bb_error_msg("iface %s is down", eth_get_name()); +			if (ready) { +				run(argv, "deconfig", &ip); +			} +			return EXIT_FAILURE; +		} +		continue; +	} +#endif + +	debug_cond(DEBUG_INT_STATE, "%s recv arp type=%d, op=%d,\n", +		eth_get_name(), ntohs(arp->ar_pro), +		ntohs(arp->ar_op)); +	debug_cond(DEBUG_INT_STATE, "\tsource=%pM %pI4\n", +		&arp->ar_sha, +		&arp->ar_spa); +	debug_cond(DEBUG_INT_STATE, "\ttarget=%pM %pI4\n", +		&arp->ar_tha, +		&arp->ar_tpa); + +	if (arp->ar_op != htons(ARPOP_REQUEST) +	 && arp->ar_op != htons(ARPOP_REPLY) +	) { +		configure_wait(); +		return; +	} + +	source_ip_conflict = 0; +	target_ip_conflict = 0; + +	if (memcmp(&arp->ar_spa, &ip, ARP_PLEN) == 0 +	 && memcmp(&arp->ar_sha, NetOurEther, ARP_HLEN) != 0 +	) { +		source_ip_conflict = 1; +	} + +	/* +	 * According to RFC 3927, section 2.2.1: +	 * Check if packet is an ARP probe by checking for a null source IP +	 * then check that target IP is equal to ours and source hw addr +	 * is not equal to ours. This condition should cause a conflict only +	 * during probe. +	 */ +	if (arp->ar_op == htons(ARPOP_REQUEST) && +	    memcmp(&arp->ar_spa, &null_ip, ARP_PLEN) == 0 && +	    memcmp(&arp->ar_tpa, &ip, ARP_PLEN) == 0 && +	    memcmp(&arp->ar_sha, NetOurEther, ARP_HLEN) != 0) { +		target_ip_conflict = 1; +	} + +	debug_cond(DEBUG_NET_PKT, +		"state = %d, source ip conflict = %d, target ip conflict = " +		"%d\n", state, source_ip_conflict, target_ip_conflict); +	switch (state) { +	case PROBE: +	case ANNOUNCE: +		/* When probing or announcing, check for source IP conflicts +		   and other hosts doing ARP probes (target IP conflicts). */ +		if (source_ip_conflict || target_ip_conflict) { +			conflicts++; +			state = PROBE; +			if (conflicts >= MAX_CONFLICTS) { +				debug("%s ratelimit\n", eth_get_name()); +				timeout_ms = RATE_LIMIT_INTERVAL * 1000; +				state = RATE_LIMIT_PROBE; +			} + +			/* restart the whole protocol */ +			ip = pick(); +			timeout_ms = 0; +			nprobes = 0; +			nclaims = 0; +		} +		break; +	case MONITOR: +		/* If a conflict, we try to defend with a single ARP probe */ +		if (source_ip_conflict) { +			debug("monitor conflict -- defending\n"); +			state = DEFEND; +			timeout_ms = DEFEND_INTERVAL * 1000; +			arp_raw_request(ip, NetOurEther, ip); +		} +		break; +	case DEFEND: +		/* Well, we tried.  Start over (on conflict) */ +		if (source_ip_conflict) { +			state = PROBE; +			debug("defend conflict -- starting over\n"); +			ready = 0; +			NetOurIP = 0; + +			/* restart the whole protocol */ +			ip = pick(); +			timeout_ms = 0; +			nprobes = 0; +			nclaims = 0; +		} +		break; +	default: +		/* Invalid, should never happen.  Restart the whole protocol */ +		debug("invalid state -- starting over\n"); +		state = PROBE; +		ip = pick(); +		timeout_ms = 0; +		nprobes = 0; +		nclaims = 0; +		break; +	} +	configure_wait(); +} diff --git a/roms/u-boot/net/link_local.h b/roms/u-boot/net/link_local.h new file mode 100644 index 00000000..bb998164 --- /dev/null +++ b/roms/u-boot/net/link_local.h @@ -0,0 +1,24 @@ +/* + * RFC3927 ZeroConf IPv4 Link-Local addressing + * (see <http://www.zeroconf.org/>) + * + * Copied from BusyBox - networking/zcip.c + * + * Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com) + * Copyright (C) 2004 by David Brownell + * + * Licensed under the GPL v2 or later + */ + +#if defined(CONFIG_CMD_LINK_LOCAL) + +#ifndef __LINK_LOCAL_H__ +#define __LINK_LOCAL_H__ + +#include <common.h> + +void link_local_receive_arp(struct arp_hdr *arp, int len); +void link_local_start(void); + +#endif /* __LINK_LOCAL_H__ */ +#endif diff --git a/roms/u-boot/net/net.c b/roms/u-boot/net/net.c new file mode 100644 index 00000000..f7cc29f0 --- /dev/null +++ b/roms/u-boot/net/net.c @@ -0,0 +1,1487 @@ +/* + *	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@denx.de + */ + +/* + * 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 + */ + + +#include <common.h> +#include <command.h> +#include <environment.h> +#include <net.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 "ping.h" +#include "rarp.h" +#if defined(CONFIG_CMD_SNTP) +#include "sntp.h" +#endif +#include "tftp.h" + +DECLARE_GLOBAL_DATA_PTR; + +/** BOOTP EXTENTIONS **/ + +/* Our subnet mask (0=unknown) */ +IPaddr_t	NetOurSubnetMask; +/* Our gateways IP address */ +IPaddr_t	NetOurGatewayIP; +/* Our DNS IP address */ +IPaddr_t	NetOurDNSIP; +#if defined(CONFIG_BOOTP_DNS2) +/* Our 2nd DNS IP address */ +IPaddr_t	NetOurDNS2IP; +#endif +/* Our NIS domain */ +char		NetOurNISDomain[32] = {0,}; +/* Our hostname */ +char		NetOurHostName[32] = {0,}; +/* Our bootpath */ +char		NetOurRootPath[64] = {0,}; +/* Our bootfile size in blocks */ +ushort		NetBootFileSize; + +#ifdef CONFIG_MCAST_TFTP	/* Multicast TFTP */ +IPaddr_t Mcast_addr; +#endif + +/** END OF BOOTP EXTENTIONS **/ + +/* The actual transferred size of the bootfile (in bytes) */ +ulong		NetBootFileXferSize; +/* Our ethernet address */ +uchar		NetOurEther[6]; +/* Boot server enet address */ +uchar		NetServerEther[6]; +/* Our IP addr (0 = unknown) */ +IPaddr_t	NetOurIP; +/* Server IP addr (0 = unknown) */ +IPaddr_t	NetServerIP; +/* Current receive packet */ +uchar *NetRxPacket; +/* Current rx packet length */ +int		NetRxPacketLen; +/* IP packet ID */ +unsigned	NetIPID; +/* Ethernet bcast address */ +uchar		NetBcastAddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +uchar		NetEtherNullAddr[6]; +#ifdef CONFIG_API +void		(*push_packet)(void *, int len) = 0; +#endif +/* Network loop state */ +enum net_loop_state net_state; +/* Tried all network devices */ +int		NetRestartWrap; +/* Network loop restarted */ +static int	NetRestarted; +/* At least one device configured */ +static int	NetDevExists; + +/* XXX in both little & big endian machines 0xFFFF == ntohs(-1) */ +/* default is without VLAN */ +ushort		NetOurVLAN = 0xFFFF; +/* ditto */ +ushort		NetOurNativeVLAN = 0xFFFF; + +/* Boot File name */ +char		BootFile[128]; + +#if defined(CONFIG_CMD_SNTP) +/* NTP server IP address */ +IPaddr_t	NetNtpServerIP; +/* offset time from UTC */ +int		NetTimeOffset; +#endif + +static uchar PktBuf[(PKTBUFSRX+1) * PKTSIZE_ALIGN + PKTALIGN]; + +/* Receive packet */ +uchar *NetRxPackets[PKTBUFSRX]; + +/* 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 *timeHandler; +/* Time base value */ +static ulong	timeStart; +/* Current timeout value */ +static ulong	timeDelta; +/* THE transmit packet */ +uchar *NetTxPacket; + +static int net_check_prereq(enum proto_t protocol); + +static int NetTryCount; + +int __maybe_unused net_busy_flag; + +/**********************************************************************/ + +static int on_bootfile(const char *name, const char *value, enum env_op op, +	int flags) +{ +	switch (op) { +	case env_op_create: +	case env_op_overwrite: +		copy_filename(BootFile, value, sizeof(BootFile)); +		break; +	default: +		break; +	} + +	return 0; +} +U_BOOT_ENV_CALLBACK(bootfile, on_bootfile); + +/* + * 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. +		 */ +		NfsStart(); +		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; +	} +	TftpStart(TFTPGET); +} + +static void NetInitLoop(void) +{ +	static int env_changed_id; +	int env_id = get_env_id(); + +	/* update only when the environment has changed */ +	if (env_changed_id != env_id) { +		NetOurIP = getenv_IPaddr("ipaddr"); +		NetOurGatewayIP = getenv_IPaddr("gatewayip"); +		NetOurSubnetMask = getenv_IPaddr("netmask"); +		NetServerIP = getenv_IPaddr("serverip"); +		NetOurNativeVLAN = getenv_VLAN("nvlan"); +		NetOurVLAN = getenv_VLAN("vlan"); +#if defined(CONFIG_CMD_DNS) +		NetOurDNSIP = getenv_IPaddr("dnsip"); +#endif +		env_changed_id = env_id; +	} +	if (eth_get_dev()) +		memcpy(NetOurEther, eth_get_dev()->enetaddr, 6); + +	return; +} + +static void net_clear_handlers(void) +{ +	net_set_udp_handler(NULL); +	net_set_arp_handler(NULL); +	NetSetTimeout(0, NULL); +} + +static void net_cleanup_loop(void) +{ +	net_clear_handlers(); +} + +void net_init(void) +{ +	static int first_call = 1; + +	if (first_call) { +		/* +		 *	Setup packet buffers, aligned correctly. +		 */ +		int i; + +		NetTxPacket = &PktBuf[0] + (PKTALIGN - 1); +		NetTxPacket -= (ulong)NetTxPacket % PKTALIGN; +		for (i = 0; i < PKTBUFSRX; i++) +			NetRxPackets[i] = NetTxPacket + (i + 1) * PKTSIZE_ALIGN; + +		ArpInit(); +		net_clear_handlers(); + +		/* Only need to setup buffer pointers once. */ +		first_call = 0; +	} + +	NetInitLoop(); +} + +/**********************************************************************/ +/* + *	Main network processing loop. + */ + +int NetLoop(enum proto_t protocol) +{ +	bd_t *bd = gd->bd; +	int ret = -1; + +	NetRestarted = 0; +	NetDevExists = 0; +	NetTryCount = 1; +	debug_cond(DEBUG_INT_STATE, "--- NetLoop 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(); +		if (eth_init(bd) < 0) { +			eth_halt(); +			return -1; +		} +	} else +		eth_init_state_only(bd); + +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, "--- NetLoop Init\n"); +	NetInitLoop(); + +	switch (net_check_prereq(protocol)) { +	case 1: +		/* network not configured */ +		eth_halt(); +		return -1; + +	case 2: +		/* network device not configured */ +		break; + +	case 0: +		NetDevExists = 1; +		NetBootFileXferSize = 0; +		switch (protocol) { +		case TFTPGET: +#ifdef CONFIG_CMD_TFTPPUT +		case TFTPPUT: +#endif +			/* always use ARP to get server ethernet address */ +			TftpStart(protocol); +			break; +#ifdef CONFIG_CMD_TFTPSRV +		case TFTPSRV: +			TftpStartServer(); +			break; +#endif +#if defined(CONFIG_CMD_DHCP) +		case DHCP: +			BootpTry = 0; +			NetOurIP = 0; +			DhcpRequest();		/* Basically same as BOOTP */ +			break; +#endif + +		case BOOTP: +			BootpTry = 0; +			NetOurIP = 0; +			BootpRequest(); +			break; + +#if defined(CONFIG_CMD_RARP) +		case RARP: +			RarpTry = 0; +			NetOurIP = 0; +			RarpRequest(); +			break; +#endif +#if defined(CONFIG_CMD_PING) +		case PING: +			ping_start(); +			break; +#endif +#if defined(CONFIG_CMD_NFS) +		case NFS: +			NfsStart(); +			break; +#endif +#if defined(CONFIG_CMD_CDP) +		case CDP: +			CDPStart(); +			break; +#endif +#ifdef CONFIG_NETCONSOLE +		case NETCONS: +			NcStart(); +			break; +#endif +#if defined(CONFIG_CMD_SNTP) +		case SNTP: +			SntpStart(); +			break; +#endif +#if defined(CONFIG_CMD_DNS) +		case DNS: +			DnsStart(); +			break; +#endif +#if defined(CONFIG_CMD_LINK_LOCAL) +		case LINKLOCAL: +			link_local_start(); +			break; +#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 +		/* +		 *	Check the ethernet for a new packet.  The ethernet +		 *	receive routine will process it. +		 */ +		eth_rx(); + +		/* +		 *	Abort if ctrl-c was pressed. +		 */ +		if (ctrlc()) { +			/* cancel any ARP that may not have completed */ +			NetArpWaitPacketIP = 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, "--- NetLoop Abort!\n"); +			goto done; +		} + +		ArpTimeoutCheck(); + +		/* +		 *	Check for a timeout, and run the timeout handler +		 *	if we have one. +		 */ +		if (timeHandler && ((get_timer(0) - timeStart) > timeDelta)) { +			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, "--- NetLoop timeout\n"); +			x = timeHandler; +			timeHandler = (thand_f *)0; +			(*x)(); +		} + + +		switch (net_state) { + +		case NETLOOP_RESTART: +			NetRestarted = 1; +			goto restart; + +		case NETLOOP_SUCCESS: +			net_cleanup_loop(); +			if (NetBootFileXferSize > 0) { +				printf("Bytes transferred = %ld (%lx hex)\n", +					NetBootFileXferSize, +					NetBootFileXferSize); +				setenv_hex("filesize", NetBootFileXferSize); +				setenv_hex("fileaddr", load_addr); +			} +			if (protocol != NETCONS) +				eth_halt(); +			else +				eth_halt_state_only(); + +			eth_set_last_protocol(protocol); + +			ret = NetBootFileXferSize; +			debug_cond(DEBUG_INT_STATE, "--- NetLoop Success!\n"); +			goto done; + +		case NETLOOP_FAIL: +			net_cleanup_loop(); +			/* Invalidate the last protocol */ +			eth_set_last_protocol(BOOTP); +			debug_cond(DEBUG_INT_STATE, "--- NetLoop 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 +startAgainTimeout(void) +{ +	net_set_state(NETLOOP_RESTART); +} + +void NetStartAgain(void) +{ +	char *nretry; +	int retry_forever = 0; +	unsigned long retrycnt = 0; + +	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 +		retry_forever = 1; + +	if ((!retry_forever) && (NetTryCount >= retrycnt)) { +		eth_halt(); +		net_set_state(NETLOOP_FAIL); +		return; +	} + +	NetTryCount++; + +	eth_halt(); +#if !defined(CONFIG_NET_DO_NOT_TRY_ANOTHER) +	eth_try_another(!NetRestarted); +#endif +	eth_init(gd->bd); +	if (NetRestartWrap) { +		NetRestartWrap = 0; +		if (NetDevExists) { +			NetSetTimeout(10000UL, startAgainTimeout); +			net_set_udp_handler(NULL); +		} else { +			net_set_state(NETLOOP_FAIL); +		} +	} else { +		net_set_state(NETLOOP_RESTART); +	} +} + +/**********************************************************************/ +/* + *	Miscelaneous bits. + */ + +static void dummy_handler(uchar *pkt, unsigned dport, +			IPaddr_t sip, unsigned sport, +			unsigned len) +{ +} + +rxhand_f *net_get_udp_handler(void) +{ +	return udp_packet_handler; +} + +void net_set_udp_handler(rxhand_f *f) +{ +	debug_cond(DEBUG_INT_STATE, "--- NetLoop 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, "--- NetLoop 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 +NetSetTimeout(ulong iv, thand_f *f) +{ +	if (iv == 0) { +		debug_cond(DEBUG_INT_STATE, +			"--- NetLoop timeout handler cancelled\n"); +		timeHandler = (thand_f *)0; +	} else { +		debug_cond(DEBUG_INT_STATE, +			"--- NetLoop timeout handler set (%p)\n", f); +		timeHandler = f; +		timeStart = get_timer(0); +		timeDelta = iv * CONFIG_SYS_HZ / 1000; +	} +} + +int NetSendUDPPacket(uchar *ether, IPaddr_t dest, int dport, int sport, +		int payload_len) +{ +	uchar *pkt; +	int eth_hdr_size; +	int pkt_hdr_size; + +	/* make sure the NetTxPacket is initialized (NetInit() was called) */ +	assert(NetTxPacket != NULL); +	if (NetTxPacket == NULL) +		return -1; + +	/* convert to new style broadcast */ +	if (dest == 0) +		dest = 0xFFFFFFFF; + +	/* if broadcast, make the ether address a broadcast and don't do ARP */ +	if (dest == 0xFFFFFFFF) +		ether = NetBcastAddr; + +	pkt = (uchar *)NetTxPacket; + +	eth_hdr_size = NetSetEther(pkt, ether, PROT_IP); +	pkt += eth_hdr_size; +	net_set_udp_header(pkt, dest, dport, sport, payload_len); +	pkt_hdr_size = eth_hdr_size + IP_UDP_HDR_SIZE; + +	/* if MAC address was not discovered yet, do an ARP request */ +	if (memcmp(ether, NetEtherNullAddr, 6) == 0) { +		debug_cond(DEBUG_DEV_PKT, "sending ARP for %pI4\n", &dest); + +		/* save the ip and eth addr for the packet to send after arp */ +		NetArpWaitPacketIP = dest; +		NetArpWaitPacketMAC = ether; + +		/* size of the waiting packet */ +		NetArpWaitTxPacketSize = pkt_hdr_size + payload_len; + +		/* and do the ARP request */ +		NetArpWaitTry = 1; +		NetArpWaitTimerStart = get_timer(0); +		ArpRequest(); +		return 1;	/* waiting */ +	} else { +		debug_cond(DEBUG_DEV_PKT, "sending UDP to %pI4/%pM\n", +			&dest, ether); +		NetSendPacket(NetTxPacket, pkt_hdr_size + payload_len); +		return 0;	/* transmitted */ +	} +} + +#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 +/* + * MAXDEFRAG, above, is chosen in the config file and  is real data + * so we need to add the NFS overhead, which is more than TFTP. + * To use sizeof in the internal unnamed structures, we need a real + * instance (can't do "sizeof(struct rpc_t.u.reply))", unfortunately). + * The compiler doesn't complain nor allocates the actual structure + */ +static struct rpc_t rpc_specimen; +#define IP_PKTSIZE (CONFIG_NET_MAXDEFRAG + sizeof(rpc_specimen.u.reply)) + +#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 *__NetDefragment(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 *NetDefragment(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 __NetDefragment(ip, lenp); +} + +#else /* !CONFIG_IP_DEFRAG */ + +static inline struct ip_udp_hdr *NetDefragment(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, +			IPaddr_t 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 +NetReceive(uchar *inpkt, int len) +{ +	struct ethernet_hdr *et; +	struct ip_udp_hdr *ip; +	IPaddr_t dst_ip; +	IPaddr_t 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"); + +	NetRxPacket = inpkt; +	NetRxPacketLen = len; +	et = (struct ethernet_hdr *)inpkt; + +	/* too small packet? */ +	if (len < ETHER_HDR_SIZE) +		return; + +#ifdef CONFIG_API +	if (push_packet) { +		(*push_packet)(inpkt, len); +		return; +	} +#endif + +#if defined(CONFIG_CMD_CDP) +	/* keep track if packet is CDP */ +	iscdp = is_cdp_packet(et->et_dest); +#endif + +	myvlanid = ntohs(NetOurVLAN); +	if (myvlanid == (ushort)-1) +		myvlanid = VLAN_NONE; +	mynvlanid = ntohs(NetOurNativeVLAN); +	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 *)(inpkt + E802_HDR_SIZE); +		len -= E802_HDR_SIZE; + +	} else if (eth_proto != PROT_VLAN) {	/* normal packet */ +		ip = (struct ip_udp_hdr *)(inpkt + 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(NetOurVLAN) & 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 *)(inpkt + 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: +		ArpReceive(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 (!NetCksumOk((uchar *)ip, IP_HDR_SIZE / 2)) { +			debug("checksum bad\n"); +			return; +		} +		/* If it is not for us, ignore it */ +		dst_ip = NetReadIP(&ip->ip_dst); +		if (NetOurIP && dst_ip != NetOurIP && dst_ip != 0xFFFFFFFF) { +#ifdef CONFIG_MCAST_TFTP +			if (Mcast_addr != dst_ip) +#endif +				return; +		} +		/* Read source IP address for later use */ +		src_ip = NetReadIP(&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 = NetDefragment(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@denx.de), or - even better - send me the +		 * necessary fixes :-) +		 * +		 * Note: in all cases where I have seen this so far +		 * it was a problem with the router configuration, +		 * for instance when a router was configured in the +		 * BOOTP reply, but the TFTP server was on the same +		 * subnet. So this is probably a warning that your +		 * configuration might be wrong. But I'm not really +		 * sure if there aren't any other situations. +		 * +		 * Simon Glass <sjg@chromium.org>: We get an ICMP when +		 * we send a tftp packet to a dead connection, or when +		 * there is no server at the other end. +		 */ +		if (ip->ip_p == IPPROTO_ICMP) { +			receive_icmp(ip, len, src_ip, et); +			return; +		} else if (ip->ip_p != IPPROTO_UDP) {	/* Only UDP packets */ +			return; +		} + +		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) >> 16) & 0x0000ffff; +			xsum += (ntohl(ip->ip_src) >>  0) & 0x0000ffff; +			xsum += (ntohl(ip->ip_dst) >> 16) & 0x0000ffff; +			xsum += (ntohl(ip->ip_dst) >>  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 + + +#ifdef CONFIG_NETCONSOLE +		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. +		 */ +		(*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); +		break; +	} +} + + +/**********************************************************************/ + +static int net_check_prereq(enum proto_t protocol) +{ +	switch (protocol) { +		/* Fall through */ +#if defined(CONFIG_CMD_PING) +	case PING: +		if (NetPingIP == 0) { +			puts("*** ERROR: ping address not given\n"); +			return 1; +		} +		goto common; +#endif +#if defined(CONFIG_CMD_SNTP) +	case SNTP: +		if (NetNtpServerIP == 0) { +			puts("*** ERROR: NTP server address not given\n"); +			return 1; +		} +		goto common; +#endif +#if defined(CONFIG_CMD_DNS) +	case DNS: +		if (NetOurDNSIP == 0) { +			puts("*** ERROR: DNS server address not given\n"); +			return 1; +		} +		goto common; +#endif +#if defined(CONFIG_CMD_NFS) +	case NFS: +#endif +	case TFTPGET: +	case TFTPPUT: +		if (NetServerIP == 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 (NetOurIP == 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(NetOurEther, "\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; +			} + +			NetStartAgain(); +			return 2; +		} +		/* Fall through */ +	default: +		return 0; +	} +	return 0;		/* OK */ +} +/**********************************************************************/ + +int +NetCksumOk(uchar *ptr, int len) +{ +	return !((NetCksum(ptr, len) + 1) & 0xfffe); +} + + +unsigned +NetCksum(uchar *ptr, int len) +{ +	ulong	xsum; +	ushort *p = (ushort *)ptr; + +	xsum = 0; +	while (len-- > 0) +		xsum += *p++; +	xsum = (xsum & 0xffff) + (xsum >> 16); +	xsum = (xsum & 0xffff) + (xsum >> 16); +	return xsum & 0xffff; +} + +int +NetEthHdrSize(void) +{ +	ushort myvlanid; + +	myvlanid = ntohs(NetOurVLAN); +	if (myvlanid == (ushort)-1) +		myvlanid = VLAN_NONE; + +	return ((myvlanid & VLAN_IDMASK) == VLAN_NONE) ? ETHER_HDR_SIZE : +		VLAN_ETHER_HDR_SIZE; +} + +int +NetSetEther(uchar *xet, uchar * addr, uint prot) +{ +	struct ethernet_hdr *et = (struct ethernet_hdr *)xet; +	ushort myvlanid; + +	myvlanid = ntohs(NetOurVLAN); +	if (myvlanid == (ushort)-1) +		myvlanid = VLAN_NONE; + +	memcpy(et->et_dest, addr, 6); +	memcpy(et->et_src, NetOurEther, 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, NetOurEther, 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, IPaddr_t dest, IPaddr_t source) +{ +	struct ip_udp_hdr *ip = (struct ip_udp_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(IP_HDR_SIZE); +	ip->ip_id    = htons(NetIPID++); +	ip->ip_off   = htons(IP_FLAGS_DFRAG);	/* Don't fragment */ +	ip->ip_ttl   = 255; +	ip->ip_sum   = 0; +	/* already in network byte order */ +	NetCopyIP((void *)&ip->ip_src, &source); +	/* already in network byte order */ +	NetCopyIP((void *)&ip->ip_dst, &dest); +} + +void net_set_udp_header(uchar *pkt, IPaddr_t 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, NetOurIP); +	ip->ip_len   = htons(IP_UDP_HDR_SIZE + len); +	ip->ip_p     = IPPROTO_UDP; +	ip->ip_sum   = ~NetCksum((uchar *)ip, IP_HDR_SIZE >> 1); + +	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) +/* + * 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(IPaddr_t x, char *s) +{ +	x = ntohl(x); +	sprintf(s, "%d.%d.%d.%d", +		(int) ((x >> 24) & 0xff), +		(int) ((x >> 16) & 0xff), +		(int) ((x >> 8) & 0xff), (int) ((x >> 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)); +} diff --git a/roms/u-boot/net/net_rand.h b/roms/u-boot/net/net_rand.h new file mode 100644 index 00000000..ba9d0642 --- /dev/null +++ b/roms/u-boot/net/net_rand.h @@ -0,0 +1,43 @@ +/* + *	Copied from LiMon - BOOTP. + * + *	Copyright 1994, 1995, 2000 Neil Russell. + *	(See License) + *	Copyright 2000 Paolo Scaffardi + */ + +#ifndef __NET_RAND_H__ +#define __NET_RAND_H__ + +#include <common.h> + +/* + * Return a seed for the PRNG derived from the eth0 MAC address. + */ +static inline unsigned int seed_mac(void) +{ +	unsigned char enetaddr[6]; +	unsigned int seed; + +	/* get our mac */ +	eth_getenv_enetaddr("ethaddr", enetaddr); + +	seed = enetaddr[5]; +	seed ^= enetaddr[4] << 8; +	seed ^= enetaddr[3] << 16; +	seed ^= enetaddr[2] << 24; +	seed ^= enetaddr[1]; +	seed ^= enetaddr[0] << 8; + +	return seed; +} + +/* + * Seed the random number generator using the eth0 MAC address. + */ +static inline void srand_mac(void) +{ +	srand(seed_mac()); +} + +#endif /* __NET_RAND_H__ */ diff --git a/roms/u-boot/net/nfs.c b/roms/u-boot/net/nfs.c new file mode 100644 index 00000000..381b75f1 --- /dev/null +++ b/roms/u-boot/net/nfs.c @@ -0,0 +1,787 @@ +/* + * NFS support driver - based on etherboot and U-BOOT's tftp.c + * + * Masami Komiya <mkomiya@sonare.it> 2004 + * + */ + +/* NOTE: the NFS code is heavily inspired by the NetBSD netboot code (read: + * large portions are copied verbatim) as distributed in OSKit 0.97.  A few + * changes were necessary to adapt the code to Etherboot and to fix several + * inconsistencies.  Also the RPC message preparation is done "by hand" to + * avoid adding netsprintf() which I find hard to understand and use.  */ + +/* NOTE 2: Etherboot does not care about things beyond the kernel image, so + * it loads the kernel image off the boot server (ARP_SERVER) and does not + * access the client root disk (root-path in dhcpd.conf), which would use + * ARP_ROOTSERVER.  The root disk is something the operating system we are + * about to load needs to use.	This is different from the OSKit 0.97 logic.  */ + +/* NOTE 3: Symlink handling introduced by Anselm M Hoffmeister, 2003-July-14 + * If a symlink is encountered, it is followed as far as possible (recursion + * possible, maximum 16 steps). There is no clearing of ".."'s inside the + * path, so please DON'T DO THAT. thx. */ + +#include <common.h> +#include <command.h> +#include <net.h> +#include <malloc.h> +#include "nfs.h" +#include "bootp.h" + +#define HASHES_PER_LINE 65	/* Number of "loading" hashes per line	*/ +#define NFS_RETRY_COUNT 30 +#ifndef CONFIG_NFS_TIMEOUT +# define NFS_TIMEOUT 2000UL +#else +# define NFS_TIMEOUT CONFIG_NFS_TIMEOUT +#endif + +#define NFS_RPC_ERR	1 +#define NFS_RPC_DROP	124 + +static int fs_mounted; +static unsigned long rpc_id; +static int nfs_offset = -1; +static int nfs_len; +static ulong nfs_timeout = NFS_TIMEOUT; + +static char dirfh[NFS_FHSIZE];	/* file handle of directory */ +static char filefh[NFS_FHSIZE]; /* file handle of kernel image */ + +static enum net_loop_state nfs_download_state; +static IPaddr_t NfsServerIP; +static int	NfsSrvMountPort; +static int	NfsSrvNfsPort; +static int	NfsOurPort; +static int	NfsTimeoutCount; +static int	NfsState; +#define STATE_PRCLOOKUP_PROG_MOUNT_REQ	1 +#define STATE_PRCLOOKUP_PROG_NFS_REQ	2 +#define STATE_MOUNT_REQ			3 +#define STATE_UMOUNT_REQ		4 +#define STATE_LOOKUP_REQ		5 +#define STATE_READ_REQ			6 +#define STATE_READLINK_REQ		7 + +static char default_filename[64]; +static char *nfs_filename; +static char *nfs_path; +static char nfs_path_buff[2048]; + +static inline int +store_block(uchar *src, unsigned offset, unsigned len) +{ +	ulong newsize = offset + len; +#ifdef CONFIG_SYS_DIRECT_FLASH_NFS +	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 */ +	{ +		(void)memcpy((void *)(load_addr + offset), src, len); +	} + +	if (NetBootFileXferSize < (offset+len)) +		NetBootFileXferSize = newsize; +	return 0; +} + +static char* +basename(char *path) +{ +	char *fname; + +	fname = path + strlen(path) - 1; +	while (fname >= path) { +		if (*fname == '/') { +			fname++; +			break; +		} +		fname--; +	} +	return fname; +} + +static char* +dirname(char *path) +{ +	char *fname; + +	fname = basename(path); +	--fname; +	*fname = '\0'; +	return path; +} + +/************************************************************************** +RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries +**************************************************************************/ +static long *rpc_add_credentials(long *p) +{ +	int hl; +	int hostnamelen; +	char hostname[256]; + +	strcpy(hostname, ""); +	hostnamelen = strlen(hostname); + +	/* Here's the executive summary on authentication requirements of the +	 * various NFS server implementations:	Linux accepts both AUTH_NONE +	 * and AUTH_UNIX authentication (also accepts an empty hostname field +	 * in the AUTH_UNIX scheme).  *BSD refuses AUTH_NONE, but accepts +	 * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX +	 * scheme).  To be safe, use AUTH_UNIX and pass the hostname if we have +	 * it (if the BOOTP/DHCP reply didn't give one, just use an empty +	 * hostname).  */ + +	hl = (hostnamelen + 3) & ~3; + +	/* Provide an AUTH_UNIX credential.  */ +	*p++ = htonl(1);		/* AUTH_UNIX */ +	*p++ = htonl(hl+20);		/* auth length */ +	*p++ = htonl(0);		/* stamp */ +	*p++ = htonl(hostnamelen);	/* hostname string */ +	if (hostnamelen & 3) +		*(p + hostnamelen / 4) = 0; /* add zero padding */ +	memcpy(p, hostname, hostnamelen); +	p += hl / 4; +	*p++ = 0;			/* uid */ +	*p++ = 0;			/* gid */ +	*p++ = 0;			/* auxiliary gid list */ + +	/* Provide an AUTH_NONE verifier.  */ +	*p++ = 0;			/* AUTH_NONE */ +	*p++ = 0;			/* auth length */ + +	return p; +} + +/************************************************************************** +RPC_LOOKUP - Lookup RPC Port numbers +**************************************************************************/ +static void +rpc_req(int rpc_prog, int rpc_proc, uint32_t *data, int datalen) +{ +	struct rpc_t pkt; +	unsigned long id; +	uint32_t *p; +	int pktlen; +	int sport; + +	id = ++rpc_id; +	pkt.u.call.id = htonl(id); +	pkt.u.call.type = htonl(MSG_CALL); +	pkt.u.call.rpcvers = htonl(2);	/* use RPC version 2 */ +	pkt.u.call.prog = htonl(rpc_prog); +	pkt.u.call.vers = htonl(2);	/* portmapper is version 2 */ +	pkt.u.call.proc = htonl(rpc_proc); +	p = (uint32_t *)&(pkt.u.call.data); + +	if (datalen) +		memcpy((char *)p, (char *)data, datalen*sizeof(uint32_t)); + +	pktlen = (char *)p + datalen*sizeof(uint32_t) - (char *)&pkt; + +	memcpy((char *)NetTxPacket + NetEthHdrSize() + IP_UDP_HDR_SIZE, +		(char *)&pkt, pktlen); + +	if (rpc_prog == PROG_PORTMAP) +		sport = SUNRPC_PORT; +	else if (rpc_prog == PROG_MOUNT) +		sport = NfsSrvMountPort; +	else +		sport = NfsSrvNfsPort; + +	NetSendUDPPacket(NetServerEther, NfsServerIP, sport, NfsOurPort, +		pktlen); +} + +/************************************************************************** +RPC_LOOKUP - Lookup RPC Port numbers +**************************************************************************/ +static void +rpc_lookup_req(int prog, int ver) +{ +	uint32_t data[16]; + +	data[0] = 0; data[1] = 0;	/* auth credential */ +	data[2] = 0; data[3] = 0;	/* auth verifier */ +	data[4] = htonl(prog); +	data[5] = htonl(ver); +	data[6] = htonl(17);	/* IP_UDP */ +	data[7] = 0; + +	rpc_req(PROG_PORTMAP, PORTMAP_GETPORT, data, 8); +} + +/************************************************************************** +NFS_MOUNT - Mount an NFS Filesystem +**************************************************************************/ +static void +nfs_mount_req(char *path) +{ +	uint32_t data[1024]; +	uint32_t *p; +	int len; +	int pathlen; + +	pathlen = strlen(path); + +	p = &(data[0]); +	p = (uint32_t *)rpc_add_credentials((long *)p); + +	*p++ = htonl(pathlen); +	if (pathlen & 3) +		*(p + pathlen / 4) = 0; +	memcpy(p, path, pathlen); +	p += (pathlen + 3) / 4; + +	len = (uint32_t *)p - (uint32_t *)&(data[0]); + +	rpc_req(PROG_MOUNT, MOUNT_ADDENTRY, data, len); +} + +/************************************************************************** +NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server +**************************************************************************/ +static void +nfs_umountall_req(void) +{ +	uint32_t data[1024]; +	uint32_t *p; +	int len; + +	if ((NfsSrvMountPort == -1) || (!fs_mounted)) +		/* Nothing mounted, nothing to umount */ +		return; + +	p = &(data[0]); +	p = (uint32_t *)rpc_add_credentials((long *)p); + +	len = (uint32_t *)p - (uint32_t *)&(data[0]); + +	rpc_req(PROG_MOUNT, MOUNT_UMOUNTALL, data, len); +} + +/*************************************************************************** + * NFS_READLINK (AH 2003-07-14) + * This procedure is called when read of the first block fails - + * this probably happens when it's a directory or a symlink + * In case of successful readlink(), the dirname is manipulated, + * so that inside the nfs() function a recursion can be done. + **************************************************************************/ +static void +nfs_readlink_req(void) +{ +	uint32_t data[1024]; +	uint32_t *p; +	int len; + +	p = &(data[0]); +	p = (uint32_t *)rpc_add_credentials((long *)p); + +	memcpy(p, filefh, NFS_FHSIZE); +	p += (NFS_FHSIZE / 4); + +	len = (uint32_t *)p - (uint32_t *)&(data[0]); + +	rpc_req(PROG_NFS, NFS_READLINK, data, len); +} + +/************************************************************************** +NFS_LOOKUP - Lookup Pathname +**************************************************************************/ +static void +nfs_lookup_req(char *fname) +{ +	uint32_t data[1024]; +	uint32_t *p; +	int len; +	int fnamelen; + +	fnamelen = strlen(fname); + +	p = &(data[0]); +	p = (uint32_t *)rpc_add_credentials((long *)p); + +	memcpy(p, dirfh, NFS_FHSIZE); +	p += (NFS_FHSIZE / 4); +	*p++ = htonl(fnamelen); +	if (fnamelen & 3) +		*(p + fnamelen / 4) = 0; +	memcpy(p, fname, fnamelen); +	p += (fnamelen + 3) / 4; + +	len = (uint32_t *)p - (uint32_t *)&(data[0]); + +	rpc_req(PROG_NFS, NFS_LOOKUP, data, len); +} + +/************************************************************************** +NFS_READ - Read File on NFS Server +**************************************************************************/ +static void +nfs_read_req(int offset, int readlen) +{ +	uint32_t data[1024]; +	uint32_t *p; +	int len; + +	p = &(data[0]); +	p = (uint32_t *)rpc_add_credentials((long *)p); + +	memcpy(p, filefh, NFS_FHSIZE); +	p += (NFS_FHSIZE / 4); +	*p++ = htonl(offset); +	*p++ = htonl(readlen); +	*p++ = 0; + +	len = (uint32_t *)p - (uint32_t *)&(data[0]); + +	rpc_req(PROG_NFS, NFS_READ, data, len); +} + +/************************************************************************** +RPC request dispatcher +**************************************************************************/ + +static void +NfsSend(void) +{ +	debug("%s\n", __func__); + +	switch (NfsState) { +	case STATE_PRCLOOKUP_PROG_MOUNT_REQ: +		rpc_lookup_req(PROG_MOUNT, 1); +		break; +	case STATE_PRCLOOKUP_PROG_NFS_REQ: +		rpc_lookup_req(PROG_NFS, 2); +		break; +	case STATE_MOUNT_REQ: +		nfs_mount_req(nfs_path); +		break; +	case STATE_UMOUNT_REQ: +		nfs_umountall_req(); +		break; +	case STATE_LOOKUP_REQ: +		nfs_lookup_req(nfs_filename); +		break; +	case STATE_READ_REQ: +		nfs_read_req(nfs_offset, nfs_len); +		break; +	case STATE_READLINK_REQ: +		nfs_readlink_req(); +		break; +	} +} + +/************************************************************************** +Handlers for the reply from server +**************************************************************************/ + +static int +rpc_lookup_reply(int prog, uchar *pkt, unsigned len) +{ +	struct rpc_t rpc_pkt; + +	memcpy((unsigned char *)&rpc_pkt, pkt, len); + +	debug("%s\n", __func__); + +	if (ntohl(rpc_pkt.u.reply.id) > rpc_id) +		return -NFS_RPC_ERR; +	else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) +		return -NFS_RPC_DROP; + +	if (rpc_pkt.u.reply.rstatus  || +	    rpc_pkt.u.reply.verifier || +	    rpc_pkt.u.reply.astatus) +		return -1; + +	switch (prog) { +	case PROG_MOUNT: +		NfsSrvMountPort = ntohl(rpc_pkt.u.reply.data[0]); +		break; +	case PROG_NFS: +		NfsSrvNfsPort = ntohl(rpc_pkt.u.reply.data[0]); +		break; +	} + +	return 0; +} + +static int +nfs_mount_reply(uchar *pkt, unsigned len) +{ +	struct rpc_t rpc_pkt; + +	debug("%s\n", __func__); + +	memcpy((unsigned char *)&rpc_pkt, pkt, len); + +	if (ntohl(rpc_pkt.u.reply.id) > rpc_id) +		return -NFS_RPC_ERR; +	else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) +		return -NFS_RPC_DROP; + +	if (rpc_pkt.u.reply.rstatus  || +	    rpc_pkt.u.reply.verifier || +	    rpc_pkt.u.reply.astatus  || +	    rpc_pkt.u.reply.data[0]) +		return -1; + +	fs_mounted = 1; +	memcpy(dirfh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE); + +	return 0; +} + +static int +nfs_umountall_reply(uchar *pkt, unsigned len) +{ +	struct rpc_t rpc_pkt; + +	debug("%s\n", __func__); + +	memcpy((unsigned char *)&rpc_pkt, pkt, len); + +	if (ntohl(rpc_pkt.u.reply.id) > rpc_id) +		return -NFS_RPC_ERR; +	else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) +		return -NFS_RPC_DROP; + +	if (rpc_pkt.u.reply.rstatus  || +	    rpc_pkt.u.reply.verifier || +	    rpc_pkt.u.reply.astatus) +		return -1; + +	fs_mounted = 0; +	memset(dirfh, 0, sizeof(dirfh)); + +	return 0; +} + +static int +nfs_lookup_reply(uchar *pkt, unsigned len) +{ +	struct rpc_t rpc_pkt; + +	debug("%s\n", __func__); + +	memcpy((unsigned char *)&rpc_pkt, pkt, len); + +	if (ntohl(rpc_pkt.u.reply.id) > rpc_id) +		return -NFS_RPC_ERR; +	else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) +		return -NFS_RPC_DROP; + +	if (rpc_pkt.u.reply.rstatus  || +	    rpc_pkt.u.reply.verifier || +	    rpc_pkt.u.reply.astatus  || +	    rpc_pkt.u.reply.data[0]) +		return -1; + +	memcpy(filefh, rpc_pkt.u.reply.data + 1, NFS_FHSIZE); + +	return 0; +} + +static int +nfs_readlink_reply(uchar *pkt, unsigned len) +{ +	struct rpc_t rpc_pkt; +	int rlen; + +	debug("%s\n", __func__); + +	memcpy((unsigned char *)&rpc_pkt, pkt, len); + +	if (ntohl(rpc_pkt.u.reply.id) > rpc_id) +		return -NFS_RPC_ERR; +	else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) +		return -NFS_RPC_DROP; + +	if (rpc_pkt.u.reply.rstatus  || +	    rpc_pkt.u.reply.verifier || +	    rpc_pkt.u.reply.astatus  || +	    rpc_pkt.u.reply.data[0]) +		return -1; + +	rlen = ntohl(rpc_pkt.u.reply.data[1]); /* new path length */ + +	if (*((char *)&(rpc_pkt.u.reply.data[2])) != '/') { +		int pathlen; +		strcat(nfs_path, "/"); +		pathlen = strlen(nfs_path); +		memcpy(nfs_path + pathlen, (uchar *)&(rpc_pkt.u.reply.data[2]), +			rlen); +		nfs_path[pathlen + rlen] = 0; +	} else { +		memcpy(nfs_path, (uchar *)&(rpc_pkt.u.reply.data[2]), rlen); +		nfs_path[rlen] = 0; +	} +	return 0; +} + +static int +nfs_read_reply(uchar *pkt, unsigned len) +{ +	struct rpc_t rpc_pkt; +	int rlen; + +	debug("%s\n", __func__); + +	memcpy((uchar *)&rpc_pkt, pkt, sizeof(rpc_pkt.u.reply)); + +	if (ntohl(rpc_pkt.u.reply.id) > rpc_id) +		return -NFS_RPC_ERR; +	else if (ntohl(rpc_pkt.u.reply.id) < rpc_id) +		return -NFS_RPC_DROP; + +	if (rpc_pkt.u.reply.rstatus  || +	    rpc_pkt.u.reply.verifier || +	    rpc_pkt.u.reply.astatus  || +	    rpc_pkt.u.reply.data[0]) { +		if (rpc_pkt.u.reply.rstatus) +			return -9999; +		if (rpc_pkt.u.reply.astatus) +			return -9999; +		return -ntohl(rpc_pkt.u.reply.data[0]); +	} + +	if ((nfs_offset != 0) && !((nfs_offset) % +			(NFS_READ_SIZE / 2 * 10 * HASHES_PER_LINE))) +		puts("\n\t "); +	if (!(nfs_offset % ((NFS_READ_SIZE / 2) * 10))) +		putc('#'); + +	rlen = ntohl(rpc_pkt.u.reply.data[18]); +	if (store_block((uchar *)pkt + sizeof(rpc_pkt.u.reply), +			nfs_offset, rlen)) +		return -9999; + +	return rlen; +} + +/************************************************************************** +Interfaces of U-BOOT +**************************************************************************/ + +static void +NfsTimeout(void) +{ +	if (++NfsTimeoutCount > NFS_RETRY_COUNT) { +		puts("\nRetry count exceeded; starting again\n"); +		NetStartAgain(); +	} else { +		puts("T "); +		NetSetTimeout(nfs_timeout + NFS_TIMEOUT * NfsTimeoutCount, +			      NfsTimeout); +		NfsSend(); +	} +} + +static void +NfsHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src, unsigned len) +{ +	int rlen; +	int reply; + +	debug("%s\n", __func__); + +	if (dest != NfsOurPort) +		return; + +	switch (NfsState) { +	case STATE_PRCLOOKUP_PROG_MOUNT_REQ: +		if (rpc_lookup_reply(PROG_MOUNT, pkt, len) == -NFS_RPC_DROP) +			break; +		NfsState = STATE_PRCLOOKUP_PROG_NFS_REQ; +		NfsSend(); +		break; + +	case STATE_PRCLOOKUP_PROG_NFS_REQ: +		if (rpc_lookup_reply(PROG_NFS, pkt, len) == -NFS_RPC_DROP) +			break; +		NfsState = STATE_MOUNT_REQ; +		NfsSend(); +		break; + +	case STATE_MOUNT_REQ: +		reply = nfs_mount_reply(pkt, len); +		if (reply == -NFS_RPC_DROP) +			break; +		else if (reply == -NFS_RPC_ERR) { +			puts("*** ERROR: Cannot mount\n"); +			/* just to be sure... */ +			NfsState = STATE_UMOUNT_REQ; +			NfsSend(); +		} else { +			NfsState = STATE_LOOKUP_REQ; +			NfsSend(); +		} +		break; + +	case STATE_UMOUNT_REQ: +		reply = nfs_umountall_reply(pkt, len); +		if (reply == -NFS_RPC_DROP) +			break; +		else if (reply == -NFS_RPC_ERR) { +			puts("*** ERROR: Cannot umount\n"); +			net_set_state(NETLOOP_FAIL); +		} else { +			puts("\ndone\n"); +			net_set_state(nfs_download_state); +		} +		break; + +	case STATE_LOOKUP_REQ: +		reply = nfs_lookup_reply(pkt, len); +		if (reply == -NFS_RPC_DROP) +			break; +		else if (reply == -NFS_RPC_ERR) { +			puts("*** ERROR: File lookup fail\n"); +			NfsState = STATE_UMOUNT_REQ; +			NfsSend(); +		} else { +			NfsState = STATE_READ_REQ; +			nfs_offset = 0; +			nfs_len = NFS_READ_SIZE; +			NfsSend(); +		} +		break; + +	case STATE_READLINK_REQ: +		reply = nfs_readlink_reply(pkt, len); +		if (reply == -NFS_RPC_DROP) +			break; +		else if (reply == -NFS_RPC_ERR) { +			puts("*** ERROR: Symlink fail\n"); +			NfsState = STATE_UMOUNT_REQ; +			NfsSend(); +		} else { +			debug("Symlink --> %s\n", nfs_path); +			nfs_filename = basename(nfs_path); +			nfs_path     = dirname(nfs_path); + +			NfsState = STATE_MOUNT_REQ; +			NfsSend(); +		} +		break; + +	case STATE_READ_REQ: +		rlen = nfs_read_reply(pkt, len); +		NetSetTimeout(nfs_timeout, NfsTimeout); +		if (rlen > 0) { +			nfs_offset += rlen; +			NfsSend(); +		} else if ((rlen == -NFSERR_ISDIR) || (rlen == -NFSERR_INVAL)) { +			/* symbolic link */ +			NfsState = STATE_READLINK_REQ; +			NfsSend(); +		} else { +			if (!rlen) +				nfs_download_state = NETLOOP_SUCCESS; +			NfsState = STATE_UMOUNT_REQ; +			NfsSend(); +		} +		break; +	} +} + + +void +NfsStart(void) +{ +	debug("%s\n", __func__); +	nfs_download_state = NETLOOP_FAIL; + +	NfsServerIP = NetServerIP; +	nfs_path = (char *)nfs_path_buff; + +	if (nfs_path == NULL) { +		net_set_state(NETLOOP_FAIL); +		puts("*** ERROR: Fail allocate memory\n"); +		return; +	} + +	if (BootFile[0] == '\0') { +		sprintf(default_filename, "/nfsroot/%02X%02X%02X%02X.img", +			NetOurIP & 0xFF, +			(NetOurIP >>  8) & 0xFF, +			(NetOurIP >> 16) & 0xFF, +			(NetOurIP >> 24) & 0xFF); +		strcpy(nfs_path, default_filename); + +		printf("*** Warning: no boot file name; using '%s'\n", +			nfs_path); +	} else { +		char *p = BootFile; + +		p = strchr(p, ':'); + +		if (p != NULL) { +			NfsServerIP = string_to_ip(BootFile); +			++p; +			strcpy(nfs_path, p); +		} else { +			strcpy(nfs_path, BootFile); +		} +	} + +	nfs_filename = basename(nfs_path); +	nfs_path     = dirname(nfs_path); + +	printf("Using %s device\n", eth_get_name()); + +	printf("File transfer via NFS from server %pI4" +		"; our IP address is %pI4", &NfsServerIP, &NetOurIP); + +	/* Check if we need to send across this subnet */ +	if (NetOurGatewayIP && NetOurSubnetMask) { +		IPaddr_t OurNet	    = NetOurIP	  & NetOurSubnetMask; +		IPaddr_t ServerNet  = NetServerIP & NetOurSubnetMask; + +		if (OurNet != ServerNet) +			printf("; sending through gateway %pI4", +				&NetOurGatewayIP); +	} +	printf("\nFilename '%s/%s'.", nfs_path, nfs_filename); + +	if (NetBootFileSize) { +		printf(" Size is 0x%x Bytes = ", NetBootFileSize<<9); +		print_size(NetBootFileSize<<9, ""); +	} +	printf("\nLoad address: 0x%lx\n" +		"Loading: *\b", load_addr); + +	NetSetTimeout(nfs_timeout, NfsTimeout); +	net_set_udp_handler(NfsHandler); + +	NfsTimeoutCount = 0; +	NfsState = STATE_PRCLOOKUP_PROG_MOUNT_REQ; + +	/*NfsOurPort = 4096 + (get_ticks() % 3072);*/ +	/*FIX ME !!!*/ +	NfsOurPort = 1000; + +	/* zero out server ether in case the server ip has changed */ +	memset(NetServerEther, 0, 6); + +	NfsSend(); +} diff --git a/roms/u-boot/net/nfs.h b/roms/u-boot/net/nfs.h new file mode 100644 index 00000000..53451dbd --- /dev/null +++ b/roms/u-boot/net/nfs.h @@ -0,0 +1,77 @@ +/* + * (C) Masami Komiya <mkomiya@sonare.it> 2004 + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#ifndef __NFS_H__ +#define __NFS_H__ + +#define SUNRPC_PORT     111 + +#define PROG_PORTMAP    100000 +#define PROG_NFS        100003 +#define PROG_MOUNT      100005 + +#define MSG_CALL        0 +#define MSG_REPLY       1 + +#define PORTMAP_GETPORT 3 + +#define MOUNT_ADDENTRY  1 +#define MOUNT_UMOUNTALL 4 + +#define NFS_LOOKUP      4 +#define NFS_READLINK    5 +#define NFS_READ        6 + +#define NFS_FHSIZE      32 + +#define NFSERR_PERM     1 +#define NFSERR_NOENT    2 +#define NFSERR_ACCES    13 +#define NFSERR_ISDIR    21 +#define NFSERR_INVAL    22 + +/* Block size used for NFS read accesses.  A RPC reply packet (including  all + * headers) must fit within a single Ethernet frame to avoid fragmentation. + * However, if CONFIG_IP_DEFRAG is set, the config file may want to use a + * bigger value. In any case, most NFS servers are optimized for a power of 2. + */ +#ifdef CONFIG_NFS_READ_SIZE +#define NFS_READ_SIZE CONFIG_NFS_READ_SIZE +#else +#define NFS_READ_SIZE 1024 /* biggest power of two that fits Ether frame */ +#endif + +#define NFS_MAXLINKDEPTH 16 + +struct rpc_t { +	union { +		uint8_t data[2048]; +		struct { +			uint32_t id; +			uint32_t type; +			uint32_t rpcvers; +			uint32_t prog; +			uint32_t vers; +			uint32_t proc; +			uint32_t data[1]; +		} call; +		struct { +			uint32_t id; +			uint32_t type; +			uint32_t rstatus; +			uint32_t verifier; +			uint32_t v2; +			uint32_t astatus; +			uint32_t data[19]; +		} reply; +	} u; +}; +extern void NfsStart(void);	/* Begin NFS */ + + +/**********************************************************************/ + +#endif /* __NFS_H__ */ diff --git a/roms/u-boot/net/ping.c b/roms/u-boot/net/ping.c new file mode 100644 index 00000000..0710b922 --- /dev/null +++ b/roms/u-boot/net/ping.c @@ -0,0 +1,115 @@ +/* + *	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@denx.de + */ + +#include "ping.h" +#include "arp.h" + +static ushort PingSeqNo; + +/* The ip address to ping */ +IPaddr_t NetPingIP; + +static void set_icmp_header(uchar *pkt, IPaddr_t dest) +{ +	/* +	 *	Construct an IP and ICMP header. +	 */ +	struct ip_hdr *ip = (struct ip_hdr *)pkt; +	struct icmp_hdr *icmp = (struct icmp_hdr *)(pkt + IP_HDR_SIZE); + +	net_set_ip_header(pkt, dest, NetOurIP); + +	ip->ip_len   = htons(IP_ICMP_HDR_SIZE); +	ip->ip_p     = IPPROTO_ICMP; +	ip->ip_sum   = ~NetCksum((uchar *)ip, IP_HDR_SIZE >> 1); + +	icmp->type = ICMP_ECHO_REQUEST; +	icmp->code = 0; +	icmp->checksum = 0; +	icmp->un.echo.id = 0; +	icmp->un.echo.sequence = htons(PingSeqNo++); +	icmp->checksum = ~NetCksum((uchar *)icmp, ICMP_HDR_SIZE	>> 1); +} + +static int ping_send(void) +{ +	uchar *pkt; +	int eth_hdr_size; + +	/* XXX always send arp request */ + +	debug_cond(DEBUG_DEV_PKT, "sending ARP for %pI4\n", &NetPingIP); + +	NetArpWaitPacketIP = NetPingIP; + +	eth_hdr_size = NetSetEther(NetTxPacket, NetEtherNullAddr, PROT_IP); +	pkt = (uchar *)NetTxPacket + eth_hdr_size; + +	set_icmp_header(pkt, NetPingIP); + +	/* size of the waiting packet */ +	NetArpWaitTxPacketSize = eth_hdr_size + IP_ICMP_HDR_SIZE; + +	/* and do the ARP request */ +	NetArpWaitTry = 1; +	NetArpWaitTimerStart = get_timer(0); +	ArpRequest(); +	return 1;	/* waiting */ +} + +static void ping_timeout(void) +{ +	eth_halt(); +	net_set_state(NETLOOP_FAIL);	/* we did not get the reply */ +} + +void ping_start(void) +{ +	printf("Using %s device\n", eth_get_name()); +	NetSetTimeout(10000UL, ping_timeout); + +	ping_send(); +} + +void ping_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip, int len) +{ +	struct icmp_hdr *icmph = (struct icmp_hdr *)&ip->udp_src; +	IPaddr_t src_ip; +	int eth_hdr_size; + +	switch (icmph->type) { +	case ICMP_ECHO_REPLY: +		src_ip = NetReadIP((void *)&ip->ip_src); +		if (src_ip == NetPingIP) +			net_set_state(NETLOOP_SUCCESS); +		return; +	case ICMP_ECHO_REQUEST: +		eth_hdr_size = net_update_ether(et, et->et_src, PROT_IP); + +		debug_cond(DEBUG_DEV_PKT, "Got ICMP ECHO REQUEST, return " +			"%d bytes\n", eth_hdr_size + len); + +		ip->ip_sum = 0; +		ip->ip_off = 0; +		NetCopyIP((void *)&ip->ip_dst, &ip->ip_src); +		NetCopyIP((void *)&ip->ip_src, &NetOurIP); +		ip->ip_sum = ~NetCksum((uchar *)ip, +				       IP_HDR_SIZE >> 1); + +		icmph->type = ICMP_ECHO_REPLY; +		icmph->checksum = 0; +		icmph->checksum = ~NetCksum((uchar *)icmph, +			(len - IP_HDR_SIZE) >> 1); +		NetSendPacket((uchar *)et, eth_hdr_size + len); +		return; +/*	default: +		return;*/ +	} +} diff --git a/roms/u-boot/net/ping.h b/roms/u-boot/net/ping.h new file mode 100644 index 00000000..8c71be4f --- /dev/null +++ b/roms/u-boot/net/ping.h @@ -0,0 +1,31 @@ +/* + *	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@denx.de + */ + +#ifndef __PING_H__ +#define __PING_H__ + +#include <common.h> +#include <net.h> + +/* + * Initialize ping (beginning of netloop) + */ +void ping_start(void); + +/* + * Deal with the receipt of a ping packet + * + * @param et Ethernet header in packet + * @param ip IP header in the same packet + * @param len Packet length + */ +void ping_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip, int len); + +#endif /* __PING_H__ */ diff --git a/roms/u-boot/net/rarp.c b/roms/u-boot/net/rarp.c new file mode 100644 index 00000000..a8e08512 --- /dev/null +++ b/roms/u-boot/net/rarp.c @@ -0,0 +1,100 @@ +/* + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <net.h> +#include "nfs.h" +#include "bootp.h" +#include "rarp.h" +#include "tftp.h" + +#define TIMEOUT 5000UL /* Milliseconds before trying BOOTP again */ +#ifndef	CONFIG_NET_RETRY_COUNT +#define TIMEOUT_COUNT 5 /* # of timeouts before giving up  */ +#else +#define TIMEOUT_COUNT (CONFIG_NET_RETRY_COUNT) +#endif + +int RarpTry; + +/* + *	Handle a RARP received packet. + */ +void rarp_receive(struct ip_udp_hdr *ip, unsigned len) +{ +	struct arp_hdr *arp; + +	debug_cond(DEBUG_NET_PKT, "Got RARP\n"); +	arp = (struct arp_hdr *)ip; +	if (len < ARP_HDR_SIZE) { +		printf("bad length %d < %d\n", len, ARP_HDR_SIZE); +		return; +	} + +	if ((ntohs(arp->ar_op) != RARPOP_REPLY) || +		(ntohs(arp->ar_hrd) != ARP_ETHER)   || +		(ntohs(arp->ar_pro) != PROT_IP)     || +		(arp->ar_hln != 6) || (arp->ar_pln != 4)) { + +		puts("invalid RARP header\n"); +	} else { +		NetCopyIP(&NetOurIP, &arp->ar_data[16]); +		if (NetServerIP == 0) +			NetCopyIP(&NetServerIP, &arp->ar_data[6]); +		memcpy(NetServerEther, &arp->ar_data[0], 6); +		debug_cond(DEBUG_DEV_PKT, "Got good RARP\n"); +		net_auto_load(); +	} +} + + +/* + *	Timeout on BOOTP request. + */ +static void RarpTimeout(void) +{ +	if (RarpTry >= TIMEOUT_COUNT) { +		puts("\nRetry count exceeded; starting again\n"); +		NetStartAgain(); +	} else { +		NetSetTimeout(TIMEOUT, RarpTimeout); +		RarpRequest(); +	} +} + + +void RarpRequest(void) +{ +	uchar *pkt; +	struct arp_hdr *rarp; +	int eth_hdr_size; + +	printf("RARP broadcast %d\n", ++RarpTry); +	pkt = NetTxPacket; + +	eth_hdr_size = NetSetEther(pkt, NetBcastAddr, PROT_RARP); +	pkt += eth_hdr_size; + +	rarp = (struct arp_hdr *)pkt; + +	rarp->ar_hrd = htons(ARP_ETHER); +	rarp->ar_pro = htons(PROT_IP); +	rarp->ar_hln = 6; +	rarp->ar_pln = 4; +	rarp->ar_op  = htons(RARPOP_REQUEST); +	memcpy(&rarp->ar_data[0],  NetOurEther, 6);	/* source ET addr */ +	memcpy(&rarp->ar_data[6],  &NetOurIP,   4);	/* source IP addr */ +	/* dest ET addr = source ET addr ??*/ +	memcpy(&rarp->ar_data[10], NetOurEther, 6); +	/* dest IP addr set to broadcast */ +	memset(&rarp->ar_data[16], 0xff,        4); + +	NetSendPacket(NetTxPacket, eth_hdr_size + ARP_HDR_SIZE); + +	NetSetTimeout(TIMEOUT, RarpTimeout); +} diff --git a/roms/u-boot/net/rarp.h b/roms/u-boot/net/rarp.h new file mode 100644 index 00000000..93e18899 --- /dev/null +++ b/roms/u-boot/net/rarp.h @@ -0,0 +1,29 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#if defined(CONFIG_CMD_RARP) + +#ifndef __RARP_H__ +#define __RARP_H__ + +#include <net.h> + +/**********************************************************************/ +/* + *	Global functions and variables. + */ + +extern int RarpTry; + +/* Process the receipt of a RARP packet */ +extern void rarp_receive(struct ip_udp_hdr *ip, unsigned len); +extern void RarpRequest(void);	/* Send a RARP request */ + +/**********************************************************************/ + +#endif /* __RARP_H__ */ +#endif diff --git a/roms/u-boot/net/sntp.c b/roms/u-boot/net/sntp.c new file mode 100644 index 00000000..5de19526 --- /dev/null +++ b/roms/u-boot/net/sntp.c @@ -0,0 +1,92 @@ +/* + * SNTP support driver + * + * Masami Komiya <mkomiya@sonare.it> 2005 + * + */ + +#include <common.h> +#include <command.h> +#include <net.h> +#include <rtc.h> + +#include "sntp.h" + +#define SNTP_TIMEOUT 10000UL + +static int SntpOurPort; + +static void +SntpSend(void) +{ +	struct sntp_pkt_t pkt; +	int pktlen = SNTP_PACKET_LEN; +	int sport; + +	debug("%s\n", __func__); + +	memset(&pkt, 0, sizeof(pkt)); + +	pkt.li = NTP_LI_NOLEAP; +	pkt.vn = NTP_VERSION; +	pkt.mode = NTP_MODE_CLIENT; + +	memcpy((char *)NetTxPacket + NetEthHdrSize() + IP_UDP_HDR_SIZE, +		(char *)&pkt, pktlen); + +	SntpOurPort = 10000 + (get_timer(0) % 4096); +	sport = NTP_SERVICE_PORT; + +	NetSendUDPPacket(NetServerEther, NetNtpServerIP, sport, SntpOurPort, +		pktlen); +} + +static void +SntpTimeout(void) +{ +	puts("Timeout\n"); +	net_set_state(NETLOOP_FAIL); +	return; +} + +static void +SntpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src, +	    unsigned len) +{ +	struct sntp_pkt_t *rpktp = (struct sntp_pkt_t *)pkt; +	struct rtc_time tm; +	ulong seconds; + +	debug("%s\n", __func__); + +	if (dest != SntpOurPort) +		return; + +	/* +	 * As the RTC's used in U-Boot sepport second resolution only +	 * we simply ignore the sub-second field. +	 */ +	memcpy(&seconds, &rpktp->transmit_timestamp, sizeof(ulong)); + +	to_tm(ntohl(seconds) - 2208988800UL + NetTimeOffset, &tm); +#if defined(CONFIG_CMD_DATE) +	rtc_set(&tm); +#endif +	printf("Date: %4d-%02d-%02d Time: %2d:%02d:%02d\n", +		tm.tm_year, tm.tm_mon, tm.tm_mday, +		tm.tm_hour, tm.tm_min, tm.tm_sec); + +	net_set_state(NETLOOP_SUCCESS); +} + +void +SntpStart(void) +{ +	debug("%s\n", __func__); + +	NetSetTimeout(SNTP_TIMEOUT, SntpTimeout); +	net_set_udp_handler(SntpHandler); +	memset(NetServerEther, 0, sizeof(NetServerEther)); + +	SntpSend(); +} diff --git a/roms/u-boot/net/sntp.h b/roms/u-boot/net/sntp.h new file mode 100644 index 00000000..bf5bf0bb --- /dev/null +++ b/roms/u-boot/net/sntp.h @@ -0,0 +1,58 @@ +/* + * (C) Masami Komiya <mkomiya@sonare.it> 2005 + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#ifndef __SNTP_H__ +#define __SNTP_H__ + +#define NTP_SERVICE_PORT	123 +#define SNTP_PACKET_LEN		48 + + +/* Leap Indicator */ +#define NTP_LI_NOLEAP		0x0 +#define NTP_LI_61SECS		0x1 +#define NTP_LI_59SECS		0x2 +#define NTP_LI_ALARM		0x3 + +/* Version */ + +#define NTP_VERSION		4 + +/* Mode */ +#define NTP_MODE_RESERVED	0 +#define NTP_MODE_SYMACTIVE	1	/* Symmetric Active */ +#define NTP_MODE_SYMPASSIVE	2	/* Symmetric Passive */ +#define NTP_MODE_CLIENT		3 +#define NTP_MODE_SERVER		4 +#define NTP_MODE_BROADCAST	5 +#define NTP_MODE_NTPCTRL	6	/* Reserved for NTP control message */ +#define NTP_MODE_PRIVATE	7	/* Reserved for private use */ + +struct sntp_pkt_t { +#if __LITTLE_ENDIAN +	uchar mode:3; +	uchar vn:3; +	uchar li:2; +#else +	uchar li:2; +	uchar vn:3; +	uchar mode:3; +#endif +	uchar stratum; +	uchar poll; +	uchar precision; +	uint root_delay; +	uint root_dispersion; +	uint reference_id; +	unsigned long long reference_timestamp; +	unsigned long long originate_timestamp; +	unsigned long long receive_timestamp; +	unsigned long long transmit_timestamp; +}; + +extern void SntpStart(void);	/* Begin SNTP */ + +#endif /* __SNTP_H__ */ diff --git a/roms/u-boot/net/tftp.c b/roms/u-boot/net/tftp.c new file mode 100644 index 00000000..966d1cfb --- /dev/null +++ b/roms/u-boot/net/tftp.c @@ -0,0 +1,949 @@ +/* + * Copyright 1994, 1995, 2000 Neil Russell. + * (See License) + * Copyright 2000, 2001 DENX Software Engineering, Wolfgang Denk, wd@denx.de + * Copyright 2011 Comelit Group SpA, + *                Luca Ceresoli <luca.ceresoli@comelit.it> + */ + +#include <common.h> +#include <command.h> +#include <net.h> +#include "tftp.h" +#include "bootp.h" +#ifdef CONFIG_SYS_DIRECT_FLASH_TFTP +#include <flash.h> +#endif + +/* Well known TFTP port # */ +#define WELL_KNOWN_PORT	69 +/* Millisecs to timeout for lost pkt */ +#define TIMEOUT		5000UL +#ifndef	CONFIG_NET_RETRY_COUNT +/* # of timeouts before giving up */ +# define TIMEOUT_COUNT	10 +#else +# define TIMEOUT_COUNT  (CONFIG_NET_RETRY_COUNT * 2) +#endif +/* Number of "loading" hashes per line (for checking the image size) */ +#define HASHES_PER_LINE	65 + +/* + *	TFTP operations. + */ +#define TFTP_RRQ	1 +#define TFTP_WRQ	2 +#define TFTP_DATA	3 +#define TFTP_ACK	4 +#define TFTP_ERROR	5 +#define TFTP_OACK	6 + +static ulong TftpTimeoutMSecs = TIMEOUT; +static int TftpTimeoutCountMax = TIMEOUT_COUNT; +static ulong time_start;   /* Record time we started tftp */ + +/* + * These globals govern the timeout behavior when attempting a connection to a + * TFTP server. TftpRRQTimeoutMSecs specifies the number of milliseconds to + * wait for the server to respond to initial connection. Second global, + * TftpRRQTimeoutCountMax, gives the number of such connection retries. + * TftpRRQTimeoutCountMax must be non-negative and TftpRRQTimeoutMSecs must be + * positive. The globals are meant to be set (and restored) by code needing + * non-standard timeout behavior when initiating a TFTP transfer. + */ +ulong TftpRRQTimeoutMSecs = TIMEOUT; +int TftpRRQTimeoutCountMax = TIMEOUT_COUNT; + +enum { +	TFTP_ERR_UNDEFINED           = 0, +	TFTP_ERR_FILE_NOT_FOUND      = 1, +	TFTP_ERR_ACCESS_DENIED       = 2, +	TFTP_ERR_DISK_FULL           = 3, +	TFTP_ERR_UNEXPECTED_OPCODE   = 4, +	TFTP_ERR_UNKNOWN_TRANSFER_ID  = 5, +	TFTP_ERR_FILE_ALREADY_EXISTS = 6, +}; + +static IPaddr_t TftpRemoteIP; +/* The UDP port at their end */ +static int	TftpRemotePort; +/* The UDP port at our end */ +static int	TftpOurPort; +static int	TftpTimeoutCount; +/* packet sequence number */ +static ulong	TftpBlock; +/* last packet sequence number received */ +static ulong	TftpLastBlock; +/* count of sequence number wraparounds */ +static ulong	TftpBlockWrap; +/* memory offset due to wrapping */ +static ulong	TftpBlockWrapOffset; +static int	TftpState; +#ifdef CONFIG_TFTP_TSIZE +/* The file size reported by the server */ +static int	TftpTsize; +/* The number of hashes we printed */ +static short	TftpNumchars; +#endif +#ifdef CONFIG_CMD_TFTPPUT +static int	TftpWriting;	/* 1 if writing, else 0 */ +static int	TftpFinalBlock;	/* 1 if we have sent the last block */ +#else +#define TftpWriting	0 +#endif + +#define STATE_SEND_RRQ	1 +#define STATE_DATA	2 +#define STATE_TOO_LARGE	3 +#define STATE_BAD_MAGIC	4 +#define STATE_OACK	5 +#define STATE_RECV_WRQ	6 +#define STATE_SEND_WRQ	7 + +/* default TFTP block size */ +#define TFTP_BLOCK_SIZE		512 +/* sequence number is 16 bit */ +#define TFTP_SEQUENCE_SIZE	((ulong)(1<<16)) + +#define DEFAULT_NAME_LEN	(8 + 4 + 1) +static char default_filename[DEFAULT_NAME_LEN]; + +#ifndef CONFIG_TFTP_FILE_NAME_MAX_LEN +#define MAX_LEN 128 +#else +#define MAX_LEN CONFIG_TFTP_FILE_NAME_MAX_LEN +#endif + +static char tftp_filename[MAX_LEN]; + +/* 512 is poor choice for ethernet, MTU is typically 1500. + * Minus eth.hdrs thats 1468.  Can get 2x better throughput with + * almost-MTU block sizes.  At least try... fall back to 512 if need be. + * (but those using CONFIG_IP_DEFRAG may want to set a larger block in cfg file) + */ +#ifdef CONFIG_TFTP_BLOCKSIZE +#define TFTP_MTU_BLOCKSIZE CONFIG_TFTP_BLOCKSIZE +#else +#define TFTP_MTU_BLOCKSIZE 1468 +#endif + +static unsigned short TftpBlkSize = TFTP_BLOCK_SIZE; +static unsigned short TftpBlkSizeOption = TFTP_MTU_BLOCKSIZE; + +#ifdef CONFIG_MCAST_TFTP +#include <malloc.h> +#define MTFTP_BITMAPSIZE	0x1000 +static unsigned *Bitmap; +static int PrevBitmapHole, Mapsize = MTFTP_BITMAPSIZE; +static uchar ProhibitMcast, MasterClient; +static uchar Multicast; +static int Mcast_port; +static ulong TftpEndingBlock; /* can get 'last' block before done..*/ + +static void parse_multicast_oack(char *pkt, int len); + +static void +mcast_cleanup(void) +{ +	if (Mcast_addr) +		eth_mcast_join(Mcast_addr, 0); +	if (Bitmap) +		free(Bitmap); +	Bitmap = NULL; +	Mcast_addr = Multicast = Mcast_port = 0; +	TftpEndingBlock = -1; +} + +#endif	/* CONFIG_MCAST_TFTP */ + +static inline void +store_block(int block, uchar *src, unsigned len) +{ +	ulong offset = block * TftpBlkSize + TftpBlockWrapOffset; +	ulong newsize = offset + len; +#ifdef CONFIG_SYS_DIRECT_FLASH_TFTP +	int i, rc = 0; + +	for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { +		/* start address in flash? */ +		if (flash_info[i].flash_id == FLASH_UNKNOWN) +			continue; +		if (load_addr + offset >= flash_info[i].start[0]) { +			rc = 1; +			break; +		} +	} + +	if (rc) { /* Flash is destination for this packet */ +		rc = flash_write((char *)src, (ulong)(load_addr+offset), len); +		if (rc) { +			flash_perror(rc); +			net_set_state(NETLOOP_FAIL); +			return; +		} +	} else +#endif /* CONFIG_SYS_DIRECT_FLASH_TFTP */ +	{ +		(void)memcpy((void *)(load_addr + offset), src, len); +	} +#ifdef CONFIG_MCAST_TFTP +	if (Multicast) +		ext2_set_bit(block, Bitmap); +#endif + +	if (NetBootFileXferSize < newsize) +		NetBootFileXferSize = newsize; +} + +/* Clear our state ready for a new transfer */ +static void new_transfer(void) +{ +	TftpLastBlock = 0; +	TftpBlockWrap = 0; +	TftpBlockWrapOffset = 0; +#ifdef CONFIG_CMD_TFTPPUT +	TftpFinalBlock = 0; +#endif +} + +#ifdef CONFIG_CMD_TFTPPUT +/** + * Load the next block from memory to be sent over tftp. + * + * @param block	Block number to send + * @param dst	Destination buffer for data + * @param len	Number of bytes in block (this one and every other) + * @return number of bytes loaded + */ +static int load_block(unsigned block, uchar *dst, unsigned len) +{ +	/* We may want to get the final block from the previous set */ +	ulong offset = ((int)block - 1) * len + TftpBlockWrapOffset; +	ulong tosend = len; + +	tosend = min(NetBootFileXferSize - offset, tosend); +	(void)memcpy(dst, (void *)(save_addr + offset), tosend); +	debug("%s: block=%d, offset=%ld, len=%d, tosend=%ld\n", __func__, +		block, offset, len, tosend); +	return tosend; +} +#endif + +static void TftpSend(void); +static void TftpTimeout(void); + +/**********************************************************************/ + +static void show_block_marker(void) +{ +#ifdef CONFIG_TFTP_TSIZE +	if (TftpTsize) { +		ulong pos = TftpBlock * TftpBlkSize + TftpBlockWrapOffset; + +		while (TftpNumchars < pos * 50 / TftpTsize) { +			putc('#'); +			TftpNumchars++; +		} +	} else +#endif +	{ +		if (((TftpBlock - 1) % 10) == 0) +			putc('#'); +		else if ((TftpBlock % (10 * HASHES_PER_LINE)) == 0) +			puts("\n\t "); +	} +} + +/** + * restart the current transfer due to an error + * + * @param msg	Message to print for user + */ +static void restart(const char *msg) +{ +	printf("\n%s; starting again\n", msg); +#ifdef CONFIG_MCAST_TFTP +	mcast_cleanup(); +#endif +	NetStartAgain(); +} + +/* + * Check if the block number has wrapped, and update progress + * + * TODO: The egregious use of global variables in this file should be tidied. + */ +static void update_block_number(void) +{ +	/* +	 * RFC1350 specifies that the first data packet will +	 * have sequence number 1. If we receive a sequence +	 * number of 0 this means that there was a wrap +	 * around of the (16 bit) counter. +	 */ +	if (TftpBlock == 0 && TftpLastBlock != 0) { +		TftpBlockWrap++; +		TftpBlockWrapOffset += TftpBlkSize * TFTP_SEQUENCE_SIZE; +		TftpTimeoutCount = 0; /* we've done well, reset thhe timeout */ +	} else { +		show_block_marker(); +	} +} + +/* The TFTP get or put is complete */ +static void tftp_complete(void) +{ +#ifdef CONFIG_TFTP_TSIZE +	/* Print hash marks for the last packet received */ +	while (TftpTsize && TftpNumchars < 49) { +		putc('#'); +		TftpNumchars++; +	} +#endif +	time_start = get_timer(time_start); +	if (time_start > 0) { +		puts("\n\t ");	/* Line up with "Loading: " */ +		print_size(NetBootFileXferSize / +			time_start * 1000, "/s"); +	} +	puts("\ndone\n"); +	net_set_state(NETLOOP_SUCCESS); +} + +static void +TftpSend(void) +{ +	uchar *pkt; +	uchar *xp; +	int len = 0; +	ushort *s; + +#ifdef CONFIG_MCAST_TFTP +	/* Multicast TFTP.. non-MasterClients do not ACK data. */ +	if (Multicast +	 && (TftpState == STATE_DATA) +	 && (MasterClient == 0)) +		return; +#endif +	/* +	 *	We will always be sending some sort of packet, so +	 *	cobble together the packet headers now. +	 */ +	pkt = NetTxPacket + NetEthHdrSize() + IP_UDP_HDR_SIZE; + +	switch (TftpState) { +	case STATE_SEND_RRQ: +	case STATE_SEND_WRQ: +		xp = pkt; +		s = (ushort *)pkt; +#ifdef CONFIG_CMD_TFTPPUT +		*s++ = htons(TftpState == STATE_SEND_RRQ ? TFTP_RRQ : +			TFTP_WRQ); +#else +		*s++ = htons(TFTP_RRQ); +#endif +		pkt = (uchar *)s; +		strcpy((char *)pkt, tftp_filename); +		pkt += strlen(tftp_filename) + 1; +		strcpy((char *)pkt, "octet"); +		pkt += 5 /*strlen("octet")*/ + 1; +		strcpy((char *)pkt, "timeout"); +		pkt += 7 /*strlen("timeout")*/ + 1; +		sprintf((char *)pkt, "%lu", TftpTimeoutMSecs / 1000); +		debug("send option \"timeout %s\"\n", (char *)pkt); +		pkt += strlen((char *)pkt) + 1; +#ifdef CONFIG_TFTP_TSIZE +		pkt += sprintf((char *)pkt, "tsize%c%lu%c", +				0, NetBootFileXferSize, 0); +#endif +		/* try for more effic. blk size */ +		pkt += sprintf((char *)pkt, "blksize%c%d%c", +				0, TftpBlkSizeOption, 0); +#ifdef CONFIG_MCAST_TFTP +		/* Check all preconditions before even trying the option */ +		if (!ProhibitMcast) { +			Bitmap = malloc(Mapsize); +			if (Bitmap && eth_get_dev()->mcast) { +				free(Bitmap); +				Bitmap = NULL; +				pkt += sprintf((char *)pkt, "multicast%c%c", +					0, 0); +			} +		} +#endif /* CONFIG_MCAST_TFTP */ +		len = pkt - xp; +		break; + +	case STATE_OACK: +#ifdef CONFIG_MCAST_TFTP +		/* My turn!  Start at where I need blocks I missed.*/ +		if (Multicast) +			TftpBlock = ext2_find_next_zero_bit(Bitmap, +							    (Mapsize*8), 0); +		/*..falling..*/ +#endif + +	case STATE_RECV_WRQ: +	case STATE_DATA: +		xp = pkt; +		s = (ushort *)pkt; +		s[0] = htons(TFTP_ACK); +		s[1] = htons(TftpBlock); +		pkt = (uchar *)(s + 2); +#ifdef CONFIG_CMD_TFTPPUT +		if (TftpWriting) { +			int toload = TftpBlkSize; +			int loaded = load_block(TftpBlock, pkt, toload); + +			s[0] = htons(TFTP_DATA); +			pkt += loaded; +			TftpFinalBlock = (loaded < toload); +		} +#endif +		len = pkt - xp; +		break; + +	case STATE_TOO_LARGE: +		xp = pkt; +		s = (ushort *)pkt; +		*s++ = htons(TFTP_ERROR); +			*s++ = htons(3); + +		pkt = (uchar *)s; +		strcpy((char *)pkt, "File too large"); +		pkt += 14 /*strlen("File too large")*/ + 1; +		len = pkt - xp; +		break; + +	case STATE_BAD_MAGIC: +		xp = pkt; +		s = (ushort *)pkt; +		*s++ = htons(TFTP_ERROR); +		*s++ = htons(2); +		pkt = (uchar *)s; +		strcpy((char *)pkt, "File has bad magic"); +		pkt += 18 /*strlen("File has bad magic")*/ + 1; +		len = pkt - xp; +		break; +	} + +	NetSendUDPPacket(NetServerEther, TftpRemoteIP, TftpRemotePort, +			 TftpOurPort, len); +} + +#ifdef CONFIG_CMD_TFTPPUT +static void icmp_handler(unsigned type, unsigned code, unsigned dest, +			 IPaddr_t sip, unsigned src, uchar *pkt, unsigned len) +{ +	if (type == ICMP_NOT_REACH && code == ICMP_NOT_REACH_PORT) { +		/* Oh dear the other end has gone away */ +		restart("TFTP server died"); +	} +} +#endif + +static void +TftpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src, +	    unsigned len) +{ +	__be16 proto; +	__be16 *s; +	int i; + +	if (dest != TftpOurPort) { +#ifdef CONFIG_MCAST_TFTP +		if (Multicast +		 && (!Mcast_port || (dest != Mcast_port))) +#endif +			return; +	} +	if (TftpState != STATE_SEND_RRQ && src != TftpRemotePort && +	    TftpState != STATE_RECV_WRQ && TftpState != STATE_SEND_WRQ) +		return; + +	if (len < 2) +		return; +	len -= 2; +	/* warning: don't use increment (++) in ntohs() macros!! */ +	s = (__be16 *)pkt; +	proto = *s++; +	pkt = (uchar *)s; +	switch (ntohs(proto)) { + +	case TFTP_RRQ: +		break; + +	case TFTP_ACK: +#ifdef CONFIG_CMD_TFTPPUT +		if (TftpWriting) { +			if (TftpFinalBlock) { +				tftp_complete(); +			} else { +				/* +				 * Move to the next block. We want our block +				 * count to wrap just like the other end! +				 */ +				int block = ntohs(*s); +				int ack_ok = (TftpBlock == block); + +				TftpBlock = (unsigned short)(block + 1); +				update_block_number(); +				if (ack_ok) +					TftpSend(); /* Send next data block */ +			} +		} +#endif +		break; + +	default: +		break; + +#ifdef CONFIG_CMD_TFTPSRV +	case TFTP_WRQ: +		debug("Got WRQ\n"); +		TftpRemoteIP = sip; +		TftpRemotePort = src; +		TftpOurPort = 1024 + (get_timer(0) % 3072); +		new_transfer(); +		TftpSend(); /* Send ACK(0) */ +		break; +#endif + +	case TFTP_OACK: +		debug("Got OACK: %s %s\n", +			pkt, +			pkt + strlen((char *)pkt) + 1); +		TftpState = STATE_OACK; +		TftpRemotePort = src; +		/* +		 * Check for 'blksize' option. +		 * Careful: "i" is signed, "len" is unsigned, thus +		 * something like "len-8" may give a *huge* number +		 */ +		for (i = 0; i+8 < len; i++) { +			if (strcmp((char *)pkt+i, "blksize") == 0) { +				TftpBlkSize = (unsigned short) +					simple_strtoul((char *)pkt+i+8, NULL, +						       10); +				debug("Blocksize ack: %s, %d\n", +					(char *)pkt+i+8, TftpBlkSize); +			} +#ifdef CONFIG_TFTP_TSIZE +			if (strcmp((char *)pkt+i, "tsize") == 0) { +				TftpTsize = simple_strtoul((char *)pkt+i+6, +							   NULL, 10); +				debug("size = %s, %d\n", +					 (char *)pkt+i+6, TftpTsize); +			} +#endif +		} +#ifdef CONFIG_MCAST_TFTP +		parse_multicast_oack((char *)pkt, len-1); +		if ((Multicast) && (!MasterClient)) +			TftpState = STATE_DATA;	/* passive.. */ +		else +#endif +#ifdef CONFIG_CMD_TFTPPUT +		if (TftpWriting) { +			/* Get ready to send the first block */ +			TftpState = STATE_DATA; +			TftpBlock++; +		} +#endif +		TftpSend(); /* Send ACK or first data block */ +		break; +	case TFTP_DATA: +		if (len < 2) +			return; +		len -= 2; +		TftpBlock = ntohs(*(__be16 *)pkt); + +		update_block_number(); + +		if (TftpState == STATE_SEND_RRQ) +			debug("Server did not acknowledge timeout option!\n"); + +		if (TftpState == STATE_SEND_RRQ || TftpState == STATE_OACK || +		    TftpState == STATE_RECV_WRQ) { +			/* first block received */ +			TftpState = STATE_DATA; +			TftpRemotePort = src; +			new_transfer(); + +#ifdef CONFIG_MCAST_TFTP +			if (Multicast) { /* start!=1 common if mcast */ +				TftpLastBlock = TftpBlock - 1; +			} else +#endif +			if (TftpBlock != 1) {	/* Assertion */ +				printf("\nTFTP error: " +				       "First block is not block 1 (%ld)\n" +				       "Starting again\n\n", +					TftpBlock); +				NetStartAgain(); +				break; +			} +		} + +		if (TftpBlock == TftpLastBlock) { +			/* +			 *	Same block again; ignore it. +			 */ +			break; +		} + +		TftpLastBlock = TftpBlock; +		TftpTimeoutCountMax = TIMEOUT_COUNT; +		NetSetTimeout(TftpTimeoutMSecs, TftpTimeout); + +		store_block(TftpBlock - 1, pkt + 2, len); + +		/* +		 *	Acknowledge the block just received, which will prompt +		 *	the remote for the next one. +		 */ +#ifdef CONFIG_MCAST_TFTP +		/* if I am the MasterClient, actively calculate what my next +		 * needed block is; else I'm passive; not ACKING +		 */ +		if (Multicast) { +			if (len < TftpBlkSize)  { +				TftpEndingBlock = TftpBlock; +			} else if (MasterClient) { +				TftpBlock = PrevBitmapHole = +					ext2_find_next_zero_bit( +						Bitmap, +						(Mapsize*8), +						PrevBitmapHole); +				if (TftpBlock > ((Mapsize*8) - 1)) { +					printf("tftpfile too big\n"); +					/* try to double it and retry */ +					Mapsize <<= 1; +					mcast_cleanup(); +					NetStartAgain(); +					return; +				} +				TftpLastBlock = TftpBlock; +			} +		} +#endif +		TftpSend(); + +#ifdef CONFIG_MCAST_TFTP +		if (Multicast) { +			if (MasterClient && (TftpBlock >= TftpEndingBlock)) { +				puts("\nMulticast tftp done\n"); +				mcast_cleanup(); +				net_set_state(NETLOOP_SUCCESS); +			} +		} else +#endif +		if (len < TftpBlkSize) +			tftp_complete(); +		break; + +	case TFTP_ERROR: +		printf("\nTFTP error: '%s' (%d)\n", +		       pkt + 2, ntohs(*(__be16 *)pkt)); + +		switch (ntohs(*(__be16 *)pkt)) { +		case TFTP_ERR_FILE_NOT_FOUND: +		case TFTP_ERR_ACCESS_DENIED: +			puts("Not retrying...\n"); +			eth_halt(); +			net_set_state(NETLOOP_FAIL); +			break; +		case TFTP_ERR_UNDEFINED: +		case TFTP_ERR_DISK_FULL: +		case TFTP_ERR_UNEXPECTED_OPCODE: +		case TFTP_ERR_UNKNOWN_TRANSFER_ID: +		case TFTP_ERR_FILE_ALREADY_EXISTS: +		default: +			puts("Starting again\n\n"); +#ifdef CONFIG_MCAST_TFTP +			mcast_cleanup(); +#endif +			NetStartAgain(); +			break; +		} +		break; +	} +} + + +static void +TftpTimeout(void) +{ +	if (++TftpTimeoutCount > TftpTimeoutCountMax) { +		restart("Retry count exceeded"); +	} else { +		puts("T "); +		NetSetTimeout(TftpTimeoutMSecs, TftpTimeout); +		if (TftpState != STATE_RECV_WRQ) +			TftpSend(); +	} +} + + +void TftpStart(enum proto_t protocol) +{ +	char *ep;             /* Environment pointer */ + +	/* +	 * Allow the user to choose TFTP blocksize and timeout. +	 * TFTP protocol has a minimal timeout of 1 second. +	 */ +	ep = getenv("tftpblocksize"); +	if (ep != NULL) +		TftpBlkSizeOption = simple_strtol(ep, NULL, 10); + +	ep = getenv("tftptimeout"); +	if (ep != NULL) +		TftpTimeoutMSecs = simple_strtol(ep, NULL, 10); + +	if (TftpTimeoutMSecs < 1000) { +		printf("TFTP timeout (%ld ms) too low, " +			"set minimum = 1000 ms\n", +			TftpTimeoutMSecs); +		TftpTimeoutMSecs = 1000; +	} + +	debug("TFTP blocksize = %i, timeout = %ld ms\n", +		TftpBlkSizeOption, TftpTimeoutMSecs); + +	TftpRemoteIP = NetServerIP; +	if (BootFile[0] == '\0') { +		sprintf(default_filename, "%02X%02X%02X%02X.img", +			NetOurIP & 0xFF, +			(NetOurIP >>  8) & 0xFF, +			(NetOurIP >> 16) & 0xFF, +			(NetOurIP >> 24) & 0xFF); + +		strncpy(tftp_filename, default_filename, MAX_LEN); +		tftp_filename[MAX_LEN-1] = 0; + +		printf("*** Warning: no boot file name; using '%s'\n", +			tftp_filename); +	} else { +		char *p = strchr(BootFile, ':'); + +		if (p == NULL) { +			strncpy(tftp_filename, BootFile, MAX_LEN); +			tftp_filename[MAX_LEN-1] = 0; +		} else { +			TftpRemoteIP = string_to_ip(BootFile); +			strncpy(tftp_filename, p + 1, MAX_LEN); +			tftp_filename[MAX_LEN-1] = 0; +		} +	} + +	printf("Using %s device\n", eth_get_name()); +	printf("TFTP %s server %pI4; our IP address is %pI4", +#ifdef CONFIG_CMD_TFTPPUT +	       protocol == TFTPPUT ? "to" : "from", +#else +		"from", +#endif +		&TftpRemoteIP, &NetOurIP); + +	/* Check if we need to send across this subnet */ +	if (NetOurGatewayIP && NetOurSubnetMask) { +		IPaddr_t OurNet	= NetOurIP    & NetOurSubnetMask; +		IPaddr_t RemoteNet	= TftpRemoteIP & NetOurSubnetMask; + +		if (OurNet != RemoteNet) +			printf("; sending through gateway %pI4", +			       &NetOurGatewayIP); +	} +	putc('\n'); + +	printf("Filename '%s'.", tftp_filename); + +	if (NetBootFileSize) { +		printf(" Size is 0x%x Bytes = ", NetBootFileSize<<9); +		print_size(NetBootFileSize<<9, ""); +	} + +	putc('\n'); +#ifdef CONFIG_CMD_TFTPPUT +	TftpWriting = (protocol == TFTPPUT); +	if (TftpWriting) { +		printf("Save address: 0x%lx\n", save_addr); +		printf("Save size:    0x%lx\n", save_size); +		NetBootFileXferSize = save_size; +		puts("Saving: *\b"); +		TftpState = STATE_SEND_WRQ; +		new_transfer(); +	} else +#endif +	{ +		printf("Load address: 0x%lx\n", load_addr); +		puts("Loading: *\b"); +		TftpState = STATE_SEND_RRQ; +	} + +	time_start = get_timer(0); +	TftpTimeoutCountMax = TftpRRQTimeoutCountMax; + +	NetSetTimeout(TftpTimeoutMSecs, TftpTimeout); +	net_set_udp_handler(TftpHandler); +#ifdef CONFIG_CMD_TFTPPUT +	net_set_icmp_handler(icmp_handler); +#endif +	TftpRemotePort = WELL_KNOWN_PORT; +	TftpTimeoutCount = 0; +	/* Use a pseudo-random port unless a specific port is set */ +	TftpOurPort = 1024 + (get_timer(0) % 3072); + +#ifdef CONFIG_TFTP_PORT +	ep = getenv("tftpdstp"); +	if (ep != NULL) +		TftpRemotePort = simple_strtol(ep, NULL, 10); +	ep = getenv("tftpsrcp"); +	if (ep != NULL) +		TftpOurPort = simple_strtol(ep, NULL, 10); +#endif +	TftpBlock = 0; + +	/* zero out server ether in case the server ip has changed */ +	memset(NetServerEther, 0, 6); +	/* Revert TftpBlkSize to dflt */ +	TftpBlkSize = TFTP_BLOCK_SIZE; +#ifdef CONFIG_MCAST_TFTP +	mcast_cleanup(); +#endif +#ifdef CONFIG_TFTP_TSIZE +	TftpTsize = 0; +	TftpNumchars = 0; +#endif + +	TftpSend(); +} + +#ifdef CONFIG_CMD_TFTPSRV +void +TftpStartServer(void) +{ +	tftp_filename[0] = 0; + +	printf("Using %s device\n", eth_get_name()); +	printf("Listening for TFTP transfer on %pI4\n", &NetOurIP); +	printf("Load address: 0x%lx\n", load_addr); + +	puts("Loading: *\b"); + +	TftpTimeoutCountMax = TIMEOUT_COUNT; +	TftpTimeoutCount = 0; +	TftpTimeoutMSecs = TIMEOUT; +	NetSetTimeout(TftpTimeoutMSecs, TftpTimeout); + +	/* Revert TftpBlkSize to dflt */ +	TftpBlkSize = TFTP_BLOCK_SIZE; +	TftpBlock = 0; +	TftpOurPort = WELL_KNOWN_PORT; + +#ifdef CONFIG_TFTP_TSIZE +	TftpTsize = 0; +	TftpNumchars = 0; +#endif + +	TftpState = STATE_RECV_WRQ; +	net_set_udp_handler(TftpHandler); + +	/* zero out server ether in case the server ip has changed */ +	memset(NetServerEther, 0, 6); +} +#endif /* CONFIG_CMD_TFTPSRV */ + +#ifdef CONFIG_MCAST_TFTP +/* Credits: atftp project. + */ + +/* pick up BcastAddr, Port, and whether I am [now] the master-client. * + * Frame: + *    +-------+-----------+---+-------~~-------+---+ + *    |  opc  | multicast | 0 | addr, port, mc | 0 | + *    +-------+-----------+---+-------~~-------+---+ + * The multicast addr/port becomes what I listen to, and if 'mc' is '1' then + * I am the new master-client so must send ACKs to DataBlocks.  If I am not + * master-client, I'm a passive client, gathering what DataBlocks I may and + * making note of which ones I got in my bitmask. + * In theory, I never go from master->passive.. + * .. this comes in with pkt already pointing just past opc + */ +static void parse_multicast_oack(char *pkt, int len) +{ +	int i; +	IPaddr_t addr; +	char *mc_adr, *port,  *mc; + +	mc_adr = port = mc = NULL; +	/* march along looking for 'multicast\0', which has to start at least +	 * 14 bytes back from the end. +	 */ +	for (i = 0; i < len-14; i++) +		if (strcmp(pkt+i, "multicast") == 0) +			break; +	if (i >= (len-14)) /* non-Multicast OACK, ign. */ +		return; + +	i += 10; /* strlen multicast */ +	mc_adr = pkt+i; +	for (; i < len; i++) { +		if (*(pkt+i) == ',') { +			*(pkt+i) = '\0'; +			if (port) { +				mc = pkt+i+1; +				break; +			} else { +				port = pkt+i+1; +			} +		} +	} +	if (!port || !mc_adr || !mc) +		return; +	if (Multicast && MasterClient) { +		printf("I got a OACK as master Client, WRONG!\n"); +		return; +	} +	/* ..I now accept packets destined for this MCAST addr, port */ +	if (!Multicast) { +		if (Bitmap) { +			printf("Internal failure! no mcast.\n"); +			free(Bitmap); +			Bitmap = NULL; +			ProhibitMcast = 1; +			return ; +		} +		/* I malloc instead of pre-declare; so that if the file ends +		 * up being too big for this bitmap I can retry +		 */ +		Bitmap = malloc(Mapsize); +		if (!Bitmap) { +			printf("No Bitmap, no multicast. Sorry.\n"); +			ProhibitMcast = 1; +			return; +		} +		memset(Bitmap, 0, Mapsize); +		PrevBitmapHole = 0; +		Multicast = 1; +	} +	addr = string_to_ip(mc_adr); +	if (Mcast_addr != addr) { +		if (Mcast_addr) +			eth_mcast_join(Mcast_addr, 0); +		Mcast_addr = addr; +		if (eth_mcast_join(Mcast_addr, 1)) { +			printf("Fail to set mcast, revert to TFTP\n"); +			ProhibitMcast = 1; +			mcast_cleanup(); +			NetStartAgain(); +		} +	} +	MasterClient = (unsigned char)simple_strtoul((char *)mc, NULL, 10); +	Mcast_port = (unsigned short)simple_strtoul(port, NULL, 10); +	printf("Multicast: %s:%d [%d]\n", mc_adr, Mcast_port, MasterClient); +	return; +} + +#endif /* Multicast TFTP */ diff --git a/roms/u-boot/net/tftp.h b/roms/u-boot/net/tftp.h new file mode 100644 index 00000000..2b686e3c --- /dev/null +++ b/roms/u-boot/net/tftp.h @@ -0,0 +1,30 @@ +/* + *	LiMon - BOOTP/TFTP. + * + *	Copyright 1994, 1995, 2000 Neil Russell. + *	Copyright 2011 Comelit Group SpA + *	               Luca Ceresoli <luca.ceresoli@comelit.it> + *	(See License) + */ + +#ifndef __TFTP_H__ +#define __TFTP_H__ + +/**********************************************************************/ +/* + *	Global functions and variables. + */ + +/* tftp.c */ +void TftpStart(enum proto_t protocol);	/* Begin TFTP get/put */ + +#ifdef CONFIG_CMD_TFTPSRV +extern void TftpStartServer(void);	/* Wait for incoming TFTP put */ +#endif + +extern ulong TftpRRQTimeoutMSecs; +extern int TftpRRQTimeoutCountMax; + +/**********************************************************************/ + +#endif /* __TFTP_H__ */  | 
