diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/Makefile | 59 | ||||
-rw-r--r-- | net/arp.c | 236 | ||||
-rw-r--r-- | net/arp.h | 30 | ||||
-rw-r--r-- | net/bootp.c | 963 | ||||
-rw-r--r-- | net/bootp.h | 95 | ||||
-rw-r--r-- | net/cdp.c | 366 | ||||
-rw-r--r-- | net/cdp.h | 21 | ||||
-rw-r--r-- | net/dns.c | 206 | ||||
-rw-r--r-- | net/dns.h | 39 | ||||
-rw-r--r-- | net/eth.c | 569 | ||||
-rw-r--r-- | net/link_local.c | 345 | ||||
-rw-r--r-- | net/link_local.h | 24 | ||||
-rw-r--r-- | net/net.c | 1476 | ||||
-rw-r--r-- | net/net_rand.h | 43 | ||||
-rw-r--r-- | net/nfs.c | 787 | ||||
-rw-r--r-- | net/nfs.h | 80 | ||||
-rw-r--r-- | net/ping.c | 115 | ||||
-rw-r--r-- | net/ping.h | 31 | ||||
-rw-r--r-- | net/rarp.c | 116 | ||||
-rw-r--r-- | net/rarp.h | 45 | ||||
-rw-r--r-- | net/sntp.c | 92 | ||||
-rw-r--r-- | net/sntp.h | 61 | ||||
-rw-r--r-- | net/tftp.c | 946 | ||||
-rw-r--r-- | net/tftp.h | 30 |
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__ */ |