summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/Makefile59
-rw-r--r--net/arp.c236
-rw-r--r--net/arp.h30
-rw-r--r--net/bootp.c963
-rw-r--r--net/bootp.h95
-rw-r--r--net/cdp.c366
-rw-r--r--net/cdp.h21
-rw-r--r--net/dns.c206
-rw-r--r--net/dns.h39
-rw-r--r--net/eth.c569
-rw-r--r--net/link_local.c345
-rw-r--r--net/link_local.h24
-rw-r--r--net/net.c1476
-rw-r--r--net/net_rand.h43
-rw-r--r--net/nfs.c787
-rw-r--r--net/nfs.h80
-rw-r--r--net/ping.c115
-rw-r--r--net/ping.h31
-rw-r--r--net/rarp.c116
-rw-r--r--net/rarp.h45
-rw-r--r--net/sntp.c92
-rw-r--r--net/sntp.h61
-rw-r--r--net/tftp.c946
-rw-r--r--net/tftp.h30
24 files changed, 6775 insertions, 0 deletions
diff --git a/net/Makefile b/net/Makefile
new file mode 100644
index 0000000..e7764ce
--- /dev/null
+++ b/net/Makefile
@@ -0,0 +1,59 @@
+#
+# (C) Copyright 2000-2006
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+
+include $(TOPDIR)/config.mk
+
+# CFLAGS += -DDEBUG
+
+LIB = $(obj)libnet.o
+
+COBJS-$(CONFIG_CMD_NET) += arp.o
+COBJS-$(CONFIG_CMD_NET) += bootp.o
+COBJS-$(CONFIG_CMD_CDP) += cdp.o
+COBJS-$(CONFIG_CMD_DNS) += dns.o
+COBJS-$(CONFIG_CMD_NET) += eth.o
+COBJS-$(CONFIG_CMD_LINK_LOCAL) += link_local.o
+COBJS-$(CONFIG_CMD_NET) += net.o
+COBJS-$(CONFIG_CMD_NFS) += nfs.o
+COBJS-$(CONFIG_CMD_PING) += ping.o
+COBJS-$(CONFIG_CMD_RARP) += rarp.o
+COBJS-$(CONFIG_CMD_SNTP) += sntp.o
+COBJS-$(CONFIG_CMD_NET) += tftp.o
+
+COBJS := $(sort $(COBJS-y))
+SRCS := $(COBJS:.o=.c)
+OBJS := $(addprefix $(obj),$(COBJS))
+
+all: $(LIB)
+
+$(LIB): $(obj).depend $(OBJS)
+ $(call cmd_link_o_target, $(OBJS))
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/net/arp.c b/net/arp.c
new file mode 100644
index 0000000..20c6b2d
--- /dev/null
+++ b/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/net/arp.h b/net/arp.h
new file mode 100644
index 0000000..bfd57e0
--- /dev/null
+++ b/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/net/bootp.c b/net/bootp.c
new file mode 100644
index 0000000..4300f1c
--- /dev/null
+++ b/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);
+ 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/net/bootp.h b/net/bootp.h
new file mode 100644
index 0000000..ecbcc4d
--- /dev/null
+++ b/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/net/cdp.c b/net/cdp.c
new file mode 100644
index 0000000..3d9559e
--- /dev/null
+++ b/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/net/cdp.h b/net/cdp.h
new file mode 100644
index 0000000..ec7315a
--- /dev/null
+++ b/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/net/dns.c b/net/dns.c
new file mode 100644
index 0000000..ff9ddff
--- /dev/null
+++ b/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/net/dns.h b/net/dns.h
new file mode 100644
index 0000000..277c093
--- /dev/null
+++ b/net/dns.h
@@ -0,0 +1,39 @@
+/*
+ * (C) Masami Komiya <mkomiya@sonare.it> 2005
+ * Copyright 2009, Robin Getz <rgetz@blackfin.uclinux.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+#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/net/eth.c b/net/eth.c
new file mode 100644
index 0000000..321d5b1
--- /dev/null
+++ b/net/eth.c
@@ -0,0 +1,569 @@
+/*
+ * (C) Copyright 2001-2010
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#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);
+}
+
+#ifdef CONFIG_RANDOM_MACADDR
+void eth_random_enetaddr(uchar *enetaddr)
+{
+ uint32_t rval;
+
+ srand(get_timer(0));
+
+ rval = rand();
+ enetaddr[0] = rval & 0xff;
+ enetaddr[1] = (rval >> 8) & 0xff;
+ enetaddr[2] = (rval >> 16) & 0xff;
+
+ rval = rand();
+ enetaddr[3] = rval & 0xff;
+ enetaddr[4] = (rval >> 8) & 0xff;
+ enetaddr[5] = (rval >> 16) & 0xff;
+
+ /* make sure it's local and unicast */
+ enetaddr[0] = (enetaddr[0] | 0x02) & ~0x01;
+}
+#endif
+
+/*
+ * 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)
+ 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/net/link_local.c b/net/link_local.c
new file mode 100644
index 0000000..4152fae
--- /dev/null
+++ b/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/net/link_local.h b/net/link_local.h
new file mode 100644
index 0000000..bb99816
--- /dev/null
+++ b/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/net/net.c b/net/net.c
new file mode 100644
index 0000000..7663b9c
--- /dev/null
+++ b/net/net.c
@@ -0,0 +1,1476 @@
+/*
+ * 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;
+
+/**********************************************************************/
+
+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:
+ 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, ... */
+
+ /*
+ * 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_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/net/net_rand.h b/net/net_rand.h
new file mode 100644
index 0000000..ba9d064
--- /dev/null
+++ b/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/net/nfs.c b/net/nfs.c
new file mode 100644
index 0000000..381b75f
--- /dev/null
+++ b/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/net/nfs.h b/net/nfs.h
new file mode 100644
index 0000000..a5a1b43
--- /dev/null
+++ b/net/nfs.h
@@ -0,0 +1,80 @@
+/*
+ * (C) Masami Komiya <mkomiya@sonare.it> 2004
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+#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/net/ping.c b/net/ping.c
new file mode 100644
index 0000000..0710b92
--- /dev/null
+++ b/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/net/ping.h b/net/ping.h
new file mode 100644
index 0000000..8c71be4
--- /dev/null
+++ b/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/net/rarp.c b/net/rarp.c
new file mode 100644
index 0000000..49185b4
--- /dev/null
+++ b/net/rarp.c
@@ -0,0 +1,116 @@
+/*
+ * (C) Copyright 2000-2002
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#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/net/rarp.h b/net/rarp.h
new file mode 100644
index 0000000..ebd748e
--- /dev/null
+++ b/net/rarp.h
@@ -0,0 +1,45 @@
+/*
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#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/net/sntp.c b/net/sntp.c
new file mode 100644
index 0000000..5de1952
--- /dev/null
+++ b/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/net/sntp.h b/net/sntp.h
new file mode 100644
index 0000000..1d0046e
--- /dev/null
+++ b/net/sntp.h
@@ -0,0 +1,61 @@
+/*
+ * (C) Masami Komiya <mkomiya@sonare.it> 2005
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+#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/net/tftp.c b/net/tftp.c
new file mode 100644
index 0000000..6d333d5
--- /dev/null
+++ b/net/tftp.c
@@ -0,0 +1,946 @@
+/*
+ * 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) {
+ 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);
+}
+#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/net/tftp.h b/net/tftp.h
new file mode 100644
index 0000000..2b686e3
--- /dev/null
+++ b/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__ */
OpenPOWER on IntegriCloud