1
0
Fork 0
mirror of https://github.com/Ysurac/openmptcprouter.git synced 2025-03-09 15:40:20 +00:00

Fix for RUTX platform

This commit is contained in:
Ycarus (Yannick Chabanois) 2022-03-28 18:17:07 +02:00
parent ccdb64ad45
commit 59bc57d5d5
7254 changed files with 1810270 additions and 7 deletions

View file

@ -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
#########################################################################

View file

@ -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;
IPaddr_t NetArpWaitReplyIP;
/* MAC address of waiting packet's destination */
uchar *NetArpWaitPacketMAC;
int NetArpWaitTxPacketSize;
ulong NetArpWaitTimerStart;
int NetArpWaitTry;
uchar *NetArpTxPacket; /* THE ARP transmit packet */
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;
}
}

View file

@ -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__ */

View file

@ -0,0 +1,946 @@
/*
* 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)
dhcp_state_t dhcp_state = INIT;
unsigned long dhcp_leasetime;
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();
}
}
/*
* 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;
size_t vci_strlen;
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);
}
}
*e++ = 60; /* Vendor Class Identifier */
vci_strlen = strlen(CONFIG_BOOTP_VCI_STRING);
*e++ = vci_strlen;
memcpy(e, CONFIG_BOOTP_VCI_STRING, vci_strlen);
e += vci_strlen;
#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_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;
#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 */

View file

@ -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__ */

View file

@ -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();
}

View file

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

View file

@ -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();
}

View file

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

View file

@ -0,0 +1,559 @@
/*
* (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 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, *eth_current;
struct eth_device *eth_get_dev(void)
{
return 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);
}
if (dev->write_hwaddr &&
!eth_mac_skip(eth_number) &&
is_valid_ether_addr(dev->enetaddr))
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;
}
if (length < eth_rcv_bufs[eth_rcv_current].length)
return -1;
length = eth_rcv_bufs[eth_rcv_current].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";
}

View file

@ -0,0 +1,336 @@
/*
* 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 && (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;
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;
}
if (arp->ar_op == htons(ARPOP_REQUEST)
&& memcmp(&arp->ar_tpa, &ip, ARP_PLEN) == 0
&& memcmp(&arp->ar_tha, 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();
}

View file

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

File diff suppressed because it is too large Load diff

View file

@ -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__ */

View file

@ -0,0 +1,751 @@
/*
* 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
#define NFS_TIMEOUT 2000UL
static int fs_mounted;
static unsigned long rpc_id;
static int nfs_offset = -1;
static int nfs_len;
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 -1;
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 -1;
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 -1;
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 -1;
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 -1;
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 -1;
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, NfsTimeout);
NfsSend();
}
}
static void
NfsHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src, unsigned len)
{
int rlen;
debug("%s\n", __func__);
if (dest != NfsOurPort)
return;
switch (NfsState) {
case STATE_PRCLOOKUP_PROG_MOUNT_REQ:
rpc_lookup_reply(PROG_MOUNT, pkt, len);
NfsState = STATE_PRCLOOKUP_PROG_NFS_REQ;
NfsSend();
break;
case STATE_PRCLOOKUP_PROG_NFS_REQ:
rpc_lookup_reply(PROG_NFS, pkt, len);
NfsState = STATE_MOUNT_REQ;
NfsSend();
break;
case STATE_MOUNT_REQ:
if (nfs_mount_reply(pkt, len)) {
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:
if (nfs_umountall_reply(pkt, len)) {
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:
if (nfs_lookup_reply(pkt, len)) {
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:
if (nfs_readlink_reply(pkt, len)) {
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();
}

View file

@ -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__ */

View file

@ -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;*/
}
}

View file

@ -0,0 +1,34 @@
/*
* 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_PING)
#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__ */
#endif

View file

@ -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);
}

View file

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

View file

@ -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();
}

View file

@ -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__ */

View file

@ -0,0 +1,959 @@
/*
* 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
DECLARE_GLOBAL_DATA_PTR;
/* 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;
/*
* 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(unsigned 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 */
{
/* The file to be tftp'ed should not overwrite the
* code/stack area
*/
if ((load_addr + newsize) >= IPQ_TFTP_MAX_ADDR) {
puts("\nError file size too large\n");
TftpState = STATE_TOO_LARGE;
net_set_state(NETLOOP_FAIL);
return;
}
(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
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)
{
ushort proto;
ushort *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 = (ushort *)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(*(ushort *)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(*(ushort *)pkt));
switch (ntohs(*(ushort *)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);
/*
* Do not load files to the reserved region or the
* region where U-Boot is relocated to.
*/
if ((load_addr < IPQ_TFTP_MIN_ADDR) ||
(load_addr >= IPQ_TFTP_MAX_ADDR)) {
puts("Error specified load address not allowed\n");
TftpState = STATE_TOO_LARGE;
net_set_state(NETLOOP_FAIL);
return;
}
puts("Loading: *\b");
TftpState = STATE_SEND_RRQ;
}
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 */

View file

@ -0,0 +1,27 @@
/*
* 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
/**********************************************************************/
#endif /* __TFTP_H__ */