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:
parent
ccdb64ad45
commit
59bc57d5d5
7254 changed files with 1810270 additions and 7 deletions
59
root/package/utils/sysupgrade-helper/src/net/Makefile
Normal file
59
root/package/utils/sysupgrade-helper/src/net/Makefile
Normal 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
|
||||
|
||||
#########################################################################
|
236
root/package/utils/sysupgrade-helper/src/net/arp.c
Normal file
236
root/package/utils/sysupgrade-helper/src/net/arp.c
Normal 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;
|
||||
}
|
||||
}
|
30
root/package/utils/sysupgrade-helper/src/net/arp.h
Normal file
30
root/package/utils/sysupgrade-helper/src/net/arp.h
Normal 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__ */
|
946
root/package/utils/sysupgrade-helper/src/net/bootp.c
Normal file
946
root/package/utils/sysupgrade-helper/src/net/bootp.c
Normal 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 */
|
95
root/package/utils/sysupgrade-helper/src/net/bootp.h
Normal file
95
root/package/utils/sysupgrade-helper/src/net/bootp.h
Normal 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__ */
|
366
root/package/utils/sysupgrade-helper/src/net/cdp.c
Normal file
366
root/package/utils/sysupgrade-helper/src/net/cdp.c
Normal 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();
|
||||
}
|
21
root/package/utils/sysupgrade-helper/src/net/cdp.h
Normal file
21
root/package/utils/sysupgrade-helper/src/net/cdp.h
Normal 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
|
206
root/package/utils/sysupgrade-helper/src/net/dns.c
Normal file
206
root/package/utils/sysupgrade-helper/src/net/dns.c
Normal 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();
|
||||
}
|
39
root/package/utils/sysupgrade-helper/src/net/dns.h
Normal file
39
root/package/utils/sysupgrade-helper/src/net/dns.h
Normal 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
|
559
root/package/utils/sysupgrade-helper/src/net/eth.c
Normal file
559
root/package/utils/sysupgrade-helper/src/net/eth.c
Normal 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";
|
||||
}
|
336
root/package/utils/sysupgrade-helper/src/net/link_local.c
Normal file
336
root/package/utils/sysupgrade-helper/src/net/link_local.c
Normal 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();
|
||||
}
|
24
root/package/utils/sysupgrade-helper/src/net/link_local.h
Normal file
24
root/package/utils/sysupgrade-helper/src/net/link_local.h
Normal 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
|
1449
root/package/utils/sysupgrade-helper/src/net/net.c
Normal file
1449
root/package/utils/sysupgrade-helper/src/net/net.c
Normal file
File diff suppressed because it is too large
Load diff
43
root/package/utils/sysupgrade-helper/src/net/net_rand.h
Normal file
43
root/package/utils/sysupgrade-helper/src/net/net_rand.h
Normal 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__ */
|
751
root/package/utils/sysupgrade-helper/src/net/nfs.c
Normal file
751
root/package/utils/sysupgrade-helper/src/net/nfs.c
Normal 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();
|
||||
}
|
80
root/package/utils/sysupgrade-helper/src/net/nfs.h
Normal file
80
root/package/utils/sysupgrade-helper/src/net/nfs.h
Normal 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__ */
|
115
root/package/utils/sysupgrade-helper/src/net/ping.c
Normal file
115
root/package/utils/sysupgrade-helper/src/net/ping.c
Normal 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;*/
|
||||
}
|
||||
}
|
34
root/package/utils/sysupgrade-helper/src/net/ping.h
Normal file
34
root/package/utils/sysupgrade-helper/src/net/ping.h
Normal 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
|
116
root/package/utils/sysupgrade-helper/src/net/rarp.c
Normal file
116
root/package/utils/sysupgrade-helper/src/net/rarp.c
Normal 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);
|
||||
}
|
45
root/package/utils/sysupgrade-helper/src/net/rarp.h
Normal file
45
root/package/utils/sysupgrade-helper/src/net/rarp.h
Normal 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
|
92
root/package/utils/sysupgrade-helper/src/net/sntp.c
Normal file
92
root/package/utils/sysupgrade-helper/src/net/sntp.c
Normal 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();
|
||||
}
|
61
root/package/utils/sysupgrade-helper/src/net/sntp.h
Normal file
61
root/package/utils/sysupgrade-helper/src/net/sntp.h
Normal 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__ */
|
959
root/package/utils/sysupgrade-helper/src/net/tftp.c
Normal file
959
root/package/utils/sysupgrade-helper/src/net/tftp.c
Normal 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 */
|
27
root/package/utils/sysupgrade-helper/src/net/tftp.h
Normal file
27
root/package/utils/sysupgrade-helper/src/net/tftp.h
Normal 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__ */
|
Loading…
Add table
Add a link
Reference in a new issue