Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/*
* 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
*
* 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
* - own IP address
* We want: - network time
* Next step: none
*/
#include <common.h>
#include <watchdog.h>
#include <command.h>
#include <net.h>
#include "bootp.h"
#include "tftp.h"
#ifdef CONFIG_STATUS_LED
#include <status_led.h>
#include <miiphy.h>
#endif
#if defined(CONFIG_CMD_SNTP)
#if defined(CONFIG_CDP_VERSION)
#include <timestamp.h>
#endif
DECLARE_GLOBAL_DATA_PTR;
/* Milliseconds before trying ARP again */
# define ARP_TIMEOUT 5000UL
# define ARP_TIMEOUT CONFIG_ARP_TIMEOUT
# define ARP_TIMEOUT_COUNT 5 /* # of timeouts before giving up */
# define ARP_TIMEOUT_COUNT CONFIG_NET_RETRY_COUNT
/* Our subnet mask (0=unknown) */
IPaddr_t NetOurSubnetMask;
/* Our gateways IP address */
/* Our DNS IP address */
#if defined(CONFIG_BOOTP_DNS2)
/* Our 2nd DNS IP address */
#endif
/* Our NIS domain */
char NetOurNISDomain[32] = {0,};
char NetOurHostName[32] = {0,};
char NetOurRootPath[64] = {0,};
/* Our bootfile size in blocks */
#ifdef CONFIG_MCAST_TFTP /* Multicast TFTP */
IPaddr_t Mcast_addr;
#endif
/* The actual transferred size of the bootfile (in bytes) */
ulong NetBootFileXferSize;
/* Our ethernet address */
uchar NetOurEther[6];
/* Boot server enet address */
/* Our IP addr (0 = unknown) */
IPaddr_t NetOurIP;
/* Server IP addr (0 = unknown) */
IPaddr_t NetServerIP;
/* Current receive packet */
volatile 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)(volatile void *, int len) = 0;
#endif
#if defined(CONFIG_CMD_CDP)
/* Ethernet bcast address */
uchar NetCDPAddr[6] = { 0x01, 0x00, 0x0c, 0xcc, 0xcc, 0xcc };
/* Network loop state */
int NetState;
/* Tried all network devices */
/* Network loop restarted */
/* At least one device configured */
/* 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_PING)
/* the ip address to ping */
IPaddr_t NetPingIP;
static void PingStart(void);
#endif
#if defined(CONFIG_CMD_CDP)
static void CDPStart(void);
#endif
#if defined(CONFIG_CMD_SNTP)
/* NTP server IP address */
IPaddr_t NetNtpServerIP;
/* offset time from UTC */
#ifdef CONFIG_NETCONSOLE
void NcStart(void);
int nc_input_packet(uchar *pkt, unsigned dest, unsigned src, unsigned len);
#endif
volatile uchar PktBuf[(PKTBUFSRX+1) * PKTSIZE_ALIGN + PKTALIGN];
/* Receive packet */
volatile uchar *NetRxPackets[PKTBUFSRX];
/* Current RX packet handler */
static rxhand_f *packetHandler;
/* Current timeout handler */
static thand_f *timeHandler;
/* Time base value */
static ulong timeStart;
/* Current timeout value */
static ulong timeDelta;
/* THE transmit packet */
volatile uchar *NetTxPacket;
static int net_check_prereq(proto_t protocol);
/**********************************************************************/
IPaddr_t NetArpWaitPacketIP;
IPaddr_t NetArpWaitReplyIP;
/* MAC address of waiting packet's destination */
uchar *NetArpWaitPacketMAC;
/* THE transmit packet */
uchar *NetArpWaitTxPacket;
uchar NetArpWaitPacketBuf[PKTSIZE_ALIGN + PKTALIGN];
ulong NetArpWaitTimerStart;
int NetArpWaitTry;
debug("ARP broadcast %d\n", NetArpWaitTry);
pkt += NetSetEther(pkt, NetBcastAddr, PROT_ARP);
arp->ar_hrd = htons(ARP_ETHER);
arp->ar_pro = htons(PROT_IP);
arp->ar_op = htons(ARPOP_REQUEST);
memcpy(&arp->ar_data[0], NetOurEther, 6);
NetWriteIP((uchar *) &arp->ar_data[6], NetOurIP);
/* dest ET addr = 0 */
arp->ar_data[i] = 0;
if ((NetArpWaitPacketIP & NetOurSubnetMask) !=
(NetOurIP & NetOurSubnetMask)) {
if (NetOurGatewayIP == 0) {
puts("## Warning: gatewayip needed but not set\n");
NetArpWaitReplyIP = NetArpWaitPacketIP;
} else {
NetArpWaitReplyIP = NetOurGatewayIP;
}
} else {
NetArpWaitReplyIP = NetArpWaitPacketIP;
}
NetWriteIP((uchar *) &arp->ar_data[16], NetArpWaitReplyIP);
(void) eth_send(NetTxPacket, (pkt - NetTxPacket) + ARP_HDR_SIZE);
}
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();
}
}
}
static int env_changed_id;
/* update only when the environment has changed */
NetOurGatewayIP = getenv_IPaddr("gatewayip");
NetOurSubnetMask = getenv_IPaddr("netmask");
NetServerIP = getenv_IPaddr("serverip");
#if defined(CONFIG_CMD_DNS)
NetOurDNSIP = getenv_IPaddr("dnsip");
#endif
/**********************************************************************/
/*
* Main network processing loop.
*/
int
NetLoop(proto_t protocol)
{
bd_t *bd = gd->bd;
#ifdef CONFIG_NET_MULTI
NetRestarted = 0;
NetDevExists = 0;
#endif
/* XXX problem with bss workaround */
NetArpWaitPacketMAC = NULL;
NetArpWaitTxPacket = NULL;
NetArpWaitPacketIP = 0;
NetArpWaitReplyIP = 0;
NetArpWaitTxPacket = NULL;
NetTxPacket = NULL;
if (!NetTxPacket) {
int i;
/*
* Setup packet buffers, aligned correctly.
*/
NetTxPacket = &PktBuf[0] + (PKTALIGN - 1);
NetTxPacket -= (ulong)NetTxPacket % PKTALIGN;
}
if (!NetArpWaitTxPacket) {
NetArpWaitTxPacket = &NetArpWaitPacketBuf[0] + (PKTALIGN - 1);
NetArpWaitTxPacket -= (ulong)NetArpWaitTxPacket % PKTALIGN;
NetArpWaitTxPacketSize = 0;
if (eth_init(bd) < 0) {
eth_halt();
memcpy(NetOurEther, eth_get_dev()->enetaddr, 6);
eth_getenv_enetaddr("ethaddr", NetOurEther);
#endif
NetState = 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.
*/
switch (net_check_prereq(protocol)) {
#ifdef CONFIG_NET_MULTI
case 2:
/* network device not configured */
break;
#endif /* CONFIG_NET_MULTI */
case 0:
#ifdef CONFIG_NET_MULTI
NetDevExists = 1;
#endif
switch (protocol) {
case TFTP:
/* always use ARP to get server ethernet address */
#if defined(CONFIG_CMD_DHCP)
DhcpRequest(); /* Basically same as BOOTP */
break;
#if defined(CONFIG_CMD_PING)
#if defined(CONFIG_CMD_NFS)
case NFS:
NfsStart();
break;
#if defined(CONFIG_CMD_CDP)
case CDP:
CDPStart();
break;
#endif
#ifdef CONFIG_NETCONSOLE
case NETCONS:
NcStart();
break;
#if defined(CONFIG_CMD_SNTP)
case SNTP:
SntpStart();
break;
#endif
#if defined(CONFIG_CMD_DNS)
case DNS:
DnsStart();
break;
default:
break;
}
NetBootFileXferSize = 0;
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);
status_led_set(STATUS_LED_RED, STATUS_LED_ON);
#endif /* CONFIG_SYS_FAULT_ECHO_LINK_DOWN, ... */
/*
* Main packet reception loop. Loop receiving packets until
*/
for (;;) {
WATCHDOG_RESET();
#ifdef CONFIG_SHOW_ACTIVITY
{
extern void show_activity(int arg);
show_activity(1);
}
#endif
/*
* Check the ethernet for a new packet. The ethernet
* receive routine will process it.
*/
/*
* Abort if ctrl-c was pressed.
*/
if (ctrlc()) {
/*
* Check for a timeout, and run the timeout handler
* if we have one.
*/
if (timeHandler && ((get_timer(0) - timeStart) > timeDelta)) {
#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);
status_led_set(STATUS_LED_RED, STATUS_LED_ON);
#endif /* CONFIG_SYS_FAULT_ECHO_LINK_DOWN, ... */
x = timeHandler;
timeHandler = (thand_f *)0;
(*x)();
}
switch (NetState) {
case NETLOOP_RESTART:
#ifdef CONFIG_NET_MULTI
NetRestarted = 1;
#endif
goto restart;
case NETLOOP_SUCCESS:
if (NetBootFileXferSize > 0) {
printf("Bytes transferred = %ld (%lx hex)\n",
NetBootFileXferSize,
NetBootFileXferSize);
sprintf(buf, "%lX", NetBootFileXferSize);
sprintf(buf, "%lX", (unsigned long)load_addr);
setenv("fileaddr", buf);
}
eth_halt();
return NetBootFileXferSize;
case NETLOOP_FAIL:
}
}
}
/**********************************************************************/
static void
startAgainTimeout(void)
{
NetState = NETLOOP_RESTART;
}
static void
startAgainHandler(uchar *pkt, unsigned dest, IPaddr_t sip,
unsigned src, unsigned len)
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();
NetState = NETLOOP_FAIL;
return;
}
NetSetTimeout(10000UL, startAgainTimeout);
NetSetHandler(startAgainHandler);
#if !defined(CONFIG_NET_DO_NOT_TRY_ANOTHER)
eth_try_another(!NetRestarted);
NetSetTimeout(10000UL, startAgainTimeout);
NetSetHandler(startAgainHandler);
}
/**********************************************************************/
/*
* Miscelaneous bits.
*/
void
NetSetHandler(rxhand_f *f)
NetSetTimeout(ulong iv, thand_f *f)
{
if (iv == 0) {
timeHandler = (thand_f *)0;
} else {
timeHandler = f;
timeStart = get_timer(0);
timeDelta = iv;
NetSendPacket(volatile uchar *pkt, int len)
int
NetSendUDPPacket(uchar *ether, IPaddr_t dest, int dport, int sport, int len)
{
/* 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;
/*
* if MAC address was not discovered yet, save the packet and do
* an ARP request
*/
if (memcmp(ether, NetEtherNullAddr, 6) == 0) {
NetArpWaitPacketIP = dest;
NetArpWaitPacketMAC = ether;
pkt = NetArpWaitTxPacket;
pkt += NetSetEther(pkt, NetArpWaitPacketMAC, PROT_IP);
NetSetIP(pkt, dest, dport, sport, len);
memcpy(pkt + IP_HDR_SIZE, (uchar *)NetTxPacket +
(pkt - (uchar *)NetArpWaitTxPacket) + IP_HDR_SIZE, len);
NetArpWaitTxPacketSize = (pkt - NetArpWaitTxPacket) +
IP_HDR_SIZE + len;
/* and do the ARP request */
NetArpWaitTry = 1;
NetArpWaitTimerStart = get_timer(0);
ArpRequest();
return 1; /* waiting */
}
debug("sending UDP to %08lx/%pM\n", dest, ether);
pkt += NetSetEther(pkt, ether, PROT_IP);
NetSetIP(pkt, dest, dport, sport, len);
(void) eth_send(NetTxPacket, (pkt - NetTxPacket) + IP_HDR_SIZE + len);
#if defined(CONFIG_CMD_PING)
static ushort PingSeqNo;
int PingSend(void)
{
static uchar mac[6];
volatile IP_t *ip;
volatile ushort *s;
/* XXX always send arp request */
memcpy(mac, NetEtherNullAddr, 6);
NetArpWaitPacketIP = NetPingIP;
NetArpWaitPacketMAC = mac;
pkt = NetArpWaitTxPacket;
pkt += NetSetEther(pkt, mac, PROT_IP);
* Construct an IP and ICMP header.
* (need to set no fragment bit - XXX)
/* IP_HDR_SIZE / 4 (not including UDP) */
ip->ip_hl_v = 0x45;
ip->ip_tos = 0;
ip->ip_len = htons(IP_HDR_SIZE_NO_UDP + 8);
ip->ip_id = htons(NetIPID++);
ip->ip_off = htons(IP_FLAGS_DFRAG); /* Don't fragment */
ip->ip_ttl = 255;
ip->ip_p = 0x01; /* ICMP */
ip->ip_sum = 0;
/* already in network byte order */
NetCopyIP((void *)&ip->ip_src, &NetOurIP);
NetCopyIP((void *)&ip->ip_dst, &NetPingIP);
ip->ip_sum = ~NetCksum((uchar *)ip, IP_HDR_SIZE_NO_UDP / 2);
s = &ip->udp_src; /* XXX ICMP starts here */
s[0] = htons(0x0800); /* echo-request, code */
s[1] = 0; /* checksum */
s[3] = htons(PingSeqNo++); /* sequence number */
s[1] = ~NetCksum((uchar *)s, 8/2);
/* size of the waiting packet */
NetArpWaitTxPacketSize =
(pkt - NetArpWaitTxPacket) + IP_HDR_SIZE_NO_UDP + 8;
/* and do the ARP request */
NetArpWaitTry = 1;
NetArpWaitTimerStart = get_timer(0);
ArpRequest();
return 1; /* waiting */
}
static void
{
eth_halt();
NetState = NETLOOP_FAIL; /* we did not get the reply */
}
PingHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src,
unsigned len)
return;
NetState = NETLOOP_SUCCESS;
}
static void PingStart(void)
{
#if defined(CONFIG_NET_MULTI)
printf("Using %s device\n", eth_get_name());
NetSetTimeout(10000UL, PingTimeout);
NetSetHandler(PingHandler);
#if defined(CONFIG_CMD_CDP)
#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;
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);
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
}
/* 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;
}
int CDPSendTrigger(void)
{
volatile uchar *pkt;
volatile ushort *s;
volatile ushort *cp;
Ethernet_t *et;
int len;
ushort chksum;
#if defined(CONFIG_CDP_DEVICE_ID) || defined(CONFIG_CDP_PORT_ID) || \
defined(CONFIG_CDP_VERSION) || defined(CONFIG_CDP_PLATFORM)
pkt = NetTxPacket;
et = (Ethernet_t *)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 = (volatile 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);
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
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;