📄 ethernet.c
字号:
/* ethernet.c:
* This code supports most of the generic ethernet/IP/ARP/UDP stuff.
*
* General notice:
* This code is part of a boot-monitor package developed as a generic base
* platform for embedded system designs. As such, it is likely to be
* distributed to various projects beyond the control of the original
* author. Please notify the author of any enhancements made or bugs found
* so that all may benefit from the changes. In addition, notification back
* to the author will allow the new user to pick up changes that may have
* been made by other users after this version of the code was distributed.
*
* Note1: the majority of this code was edited with 4-space tabs.
* Note2: as more and more contributions are accepted, the term "author"
* is becoming a mis-representation of credit.
*
* Original author: Ed Sutter
* Email: esutter@lucent.com
* Phone: 908-582-2351
*/
#include "config.h"
#include "endian.h"
#include "stddefs.h"
#include "genlib.h"
#if INCLUDE_ETHERNET
#include "cpuio.h"
#include "ether.h"
#include "monflags.h"
#include "cli.h"
void ShowEthernetStats(void);
void executeMONCMD(void);
void processMONCMD(struct ether_header *,ushort);
int SendIPMonChar(uchar,int);
char IPMonCmdLine[CMDLINESIZE];
int IPMonCmdVerbose;
#if INCLUDE_DHCPBOOT
#define dhcpStateCheck() dhcpStateCheck()
#define dhcpDisable() dhcpDisable()
#define ShowDhcpStats() ShowDhcpStats()
#else
#define dhcpStateCheck()
#define dhcpDisable()
#define ShowDhcpStats()
#endif
#if INCLUDE_TFTP
#define tftpStateCheck() tftpStateCheck()
#define tftpInit() tftpInit()
#define ShowTftpStats() ShowTftpStats()
#else
#define tftpStateCheck()
#define tftpInit()
#define ShowTftpStats()
#endif
char *Etheradd, *IPadd; /* Pointers to ascii addresses */
uchar BinIpAddr[4]; /* Space for binary IP address */
uchar BinEnetAddr[6]; /* Space for binary MAC address */
int EtherVerbose; /* Verbosity flag (see ether.h). */
int EtherIsActive; /* Non-zero if ethernet is up. */
int EtherIPERRCnt; /* Number of IP errors detected. */
int EtherUDPERRCnt; /* Number of UDP errors detected. */
int EtherXFRAMECnt; /* Number of packets transmitted. */
int EtherRFRAMECnt; /* Number of packets received. */
int EtherPollNesting; /* Incremented when pollethernet() is called. */
int MaxEtherPollNesting; /* High-warter mark of EtherPollNesting. */
int IPMonCmdActive; /* Set if MONCMD is in progress. */
ushort UniqueIpId;
ulong IPMonCmdHdrBuf[(sizeof(struct ether_header) + sizeof(struct ip) + sizeof(struct Udphdr) + 128)/(sizeof(ulong))];
struct ether_header *IPMonCmdHdr;
/* AppPktPtr & AppPktLen:
* These two values are used to allow the monitor's ethernet driver
* to easily (not necessarily most efficiently) hook up to an application
* that needs to be able to send and/or receive ethernet packets.
* Refer to discussion above monRecvEnetPkt().
*/
char *AppPktPtr;
int AppPktLen;
/* Ports used by the monitor have defaults, but can be redefined using
* shell variables:
*/
ushort MoncmdPort; /* shell var: MCMDPORT */
ushort GdbPort; /* shell var: GDBPORT */
ushort DhcpClientPort; /* shell var: DCLIPORT */
ushort DhcpServerPort; /* shell var: DSRVPORT */
ushort TftpPort; /* shell var: TFTPPORT */
ushort TftpSrcPort; /* shell var: TFTPPORT */
uchar BroadcastAddr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
uchar AllZeroAddr[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
struct pinfo {
int pnum;
char *pname;
} protocols[] = {
{ IP_IP, "IP" },
{ IP_ICMP, "ICMP" },
{ IP_IGMP, "IGMP" },
{ IP_GGP, "GGP" },
{ IP_TCP, "TCP" },
{ IP_PUP, "PUP" },
{ IP_UDP, "UDP" },
{ 0,0 },
};
struct enet_verbosity {
char letter;
ulong flags;
} enet_verbose_tbl[] = {
{ '0', 0 },
{ 'a', SHOW_ARP | SHOW_BROADCAST },
{ 'c', SHOW_BADCSUM },
{ 'C', SHOW_BADCSUM | SHOW_BADCSUMV },
{ 'd', SHOW_DHCP },
#if INCLUDE_GDB
{ 'g', SHOW_GDB },
#endif
{ 'i', SHOW_INCOMING },
{ 'I', SHOW_INCOMING | SHOW_BROADCAST },
{ 'o', SHOW_OUTGOING },
{ 'p', SHOW_PHY },
{ 't', SHOW_TFTP_STATE },
{ 'x', SHOW_HEX },
{ 'X', SHOW_HEX | SHOW_ASCII },
{ 0,0 }
};
int
SetEthernetVerbosity(char *letters)
{
ulong verbose = 0;
struct enet_verbosity *evp = enet_verbose_tbl;
while(*letters) {
evp = enet_verbose_tbl;
while(evp->letter) {
if (*letters == evp->letter) {
verbose |= evp->flags;
break;
}
evp++;
}
if (evp->letter == 0) {
printf("Invalid verbosity: '%c'\n",*letters);
return(CMD_PARAM_ERROR);
}
letters++;
}
EtherVerbose = verbose;
return(CMD_SUCCESS);
}
char *EtherHelp[] = {
"Ethernet interface",
"-[d:pt:v:V] {on | off | mac | stat | {print I|i|O pkt len}}",
#if INCLUDE_VERBOSEHELP
"Options...",
" -d {1|0} driver debug mode (1=on)",
" -p {1|0} promiscuous mode (1=on)",
" -t self-test ethernet interface",
" -v {flgs} enable specific verbosity...",
" 0: turn off verbosity",
" a: enable ARP trace",
" c: print csum errmsg",
" C: dump csum errpkt",
" d: enable DHCP trace",
#if INCLUDE_GDB
" g: enable GDB trace",
#endif
" i: incoming packets (minus broadcast)",
" I: incoming packets (including broadcast)",
" o: outgoing packets",
" p: phy r/w accesses",
" t: enable TFTP trace",
" x: enable hex dump (requires i,I or o)",
" X: same as 'x' plus ascii",
" -V full verbosity (same as -v Iodtx)",
#endif
0
};
int
Ether(int argc,char *argv[])
{
int opt;
while ((opt=getopt(argc,argv,"d:p:s:tv:V")) != -1) {
switch(opt) {
case 'd':
if (*optarg == '1')
EtherVerbose |= SHOW_DRIVER_DEBUG;
else
EtherVerbose &= ~SHOW_DRIVER_DEBUG;
return(CMD_SUCCESS);
case 'p':
if (*optarg == '1')
enablePromiscuousReception();
else
disablePromiscuousReception();
return(CMD_SUCCESS);
case 't':
enselftest(1);
return(CMD_SUCCESS);
case 'V':
EtherVerbose = SHOW_ALL;
return(CMD_SUCCESS);
case 'v':
return(SetEthernetVerbosity(optarg));
default:
return(CMD_PARAM_ERROR);
}
}
if (argc <= optind)
return(CMD_SUCCESS);
if (!strcmp(argv[optind],"off")) {
enreset();
EtherIsActive = 0;
return(CMD_SUCCESS);
}
else if (!strcmp(argv[optind],"print")) {
if (argc == optind+4) {
int len, mode;
ulong overbose;
struct ether_header *pkt;
overbose = EtherVerbose;
switch(argv[optind+1][0]) {
case 'O':
mode = ETHER_OUTGOING;
EtherVerbose = SHOW_ALL;
break;
case 'I':
mode = ETHER_INCOMING;
EtherVerbose = SHOW_ALL;
break;
case 'i':
mode = ETHER_INCOMING;
EtherVerbose = (SHOW_ALL & ~SHOW_BROADCAST);
break;
default:
return(CMD_PARAM_ERROR);
}
pkt = (struct ether_header *)strtol(argv[optind+2],0,0);
len = (int)strtol(argv[optind+3],0,0);
printPkt(pkt,len,mode);
EtherVerbose = overbose;
return(CMD_SUCCESS);
}
else
return(CMD_PARAM_ERROR);
}
else if (!strcmp(argv[optind],"mac")) {
storeMac(1);
return(CMD_SUCCESS);
}
else if (!strcmp(argv[optind],"stat")) {
ShowEthernetStats();
ShowEtherdevStats();
ShowDhcpStats();
ShowTftpStats();
return(CMD_SUCCESS);
}
else if (strcmp(argv[optind],"on"))
return(CMD_PARAM_ERROR);
EthernetStartup(EtherVerbose,0);
return(CMD_SUCCESS);
}
void
ShowEthernetStats(void)
{
printf("Ethernet interface currently %sabled.\n",
EtherIsActive ? "en" : "dis");
printf("Transmitted frames: %d\n",EtherXFRAMECnt);
printf("Received frames: %d\n",EtherRFRAMECnt);
printf("IP hdr cksum errors: %d\n",EtherIPERRCnt);
printf("UDP pkt cksum errors: %d\n",EtherUDPERRCnt);
printf("Max pollethernet nest: %d\n",MaxEtherPollNesting);
}
/* DisableEthernet():
* Shut down the interface, and return the state of the
* interface prior to forcing the shut down.
*/
int
DisableEthernet(void)
{
int eia;
eia = EtherIsActive;
EtherIsActive = 0;
IPMonCmdActive = 0;
DisableEtherdev();
return(eia);
}
int
EthernetStartup(int verbose, int justreset)
{
/* Initialize the retransmission delay calculator: */
RetransmitDelay(DELAY_INIT_DHCP);
EtherIPERRCnt = 0;
EtherXFRAMECnt = 0;
EtherRFRAMECnt = 0;
EtherUDPERRCnt = 0;
IPMonCmdActive = 0;
EtherPollNesting = 0;
MaxEtherPollNesting = 0;
DHCPState = DHCPSTATE_NOTUSED;
if (getenv("ETHERNET_DEBUG"))
EtherVerbose |= SHOW_DRIVER_DEBUG;
else
EtherVerbose = 0;
/* Setup all the IP addresses used by the monitor... */
if (getAddresses() == -1)
return(-1);
/* Call device specific startup code: */
if (EtherdevStartup(verbose) < 0)
return(-1);
/* Initialize some TFTP state... */
tftpInit();
#if INCLUDE_DHCPBOOT
/* If EthernetStartup is called as a result of anything other than a
* target reset, don't startup any DHCP/BOOTP transaction...
*/
if (!justreset)
dhcpDisable();
#endif
EtherIsActive = 1;
return(0);
}
/* pollethernet():
* Called at a few critical points in the monitor code to poll the
* ethernet device and keep track of the state of DHCP and TFTP.
*/
int
pollethernet(void)
{
int pcnt;
if ((!EtherIsActive) || (EtherPollNesting > 4))
return(0);
EtherPollNesting++;
if (EtherPollNesting > MaxEtherPollNesting)
MaxEtherPollNesting = EtherPollNesting;
pcnt = polletherdev();
if (IPMonCmdLine[0] != 0)
executeMONCMD();
dhcpStateCheck();
tftpStateCheck();
EtherPollNesting--;
return(pcnt);
}
/* getAddresses():
* Try getting ether/ip addresses from environment.
* If not there, try getting them from some target-specific interface.
* If not there, then get them from raw flash.
* If not there, just use the hard-coded default.
* Also, load all port numbers from shell variables, else default.
*
* Discussion regarding etheraddr[]...
* The purpose of this array is to provide a point in flash that is
* initialized to 0xff by the code (see reset.s). This then allows some
* other mechanism (storeMAC() or bed of nails, etc..) to program this
* location to some non-0xff value. This allows the base monitor image to
* be common, but then be modified by other code or external hardware.
*/
int
getAddresses(void)
{
char *mcmdPort, *gdbPort, *dcliPort, *dsrvPort, *tftpPort;
/* Set up port numbers: */
mcmdPort = getenv("MCMDPORT");
gdbPort = getenv("GDBPORT");
dcliPort = getenv("DCLIPORT");
dsrvPort = getenv("DSRVPORT");
tftpPort = getenv("TFTPPORT");
if (mcmdPort)
MoncmdPort = (ushort)strtol(mcmdPort,0,0);
else
MoncmdPort = IPPORT_MONCMD;
if (gdbPort)
GdbPort = (ushort)strtol(gdbPort,0,0);
else
GdbPort = IPPORT_GDB;
if (dcliPort)
DhcpClientPort = (ushort)strtol(dcliPort,0,0);
else
DhcpClientPort = IPPORT_DHCP_CLIENT;
if (dsrvPort)
DhcpServerPort = (ushort)strtol(dsrvPort,0,0);
else
DhcpServerPort = IPPORT_DHCP_SERVER;
if (tftpPort)
TftpPort = (ushort)strtol(tftpPort,0,0);
else
TftpPort = IPPORT_TFTP; /* 69 */
TftpSrcPort = IPPORT_TFTPSRC; /* 8888 */
/* Retrieve MAC address and store in shell variable ETHERADD...
* First see if the shell variable is already loaded.
* If not see if some target-specific interface has it.
* If not see if the the string is stored in raw flash (usually this
* storage is initialized in reset.s of the target-specific code).
* Finally, as a last resort, use the default set up in config.h.
*/
//stone add for set MAC use DEFAULT_ETHERADD;
if (!(Etheradd = getenv("ETHERADD"))) {
if (!(Etheradd = extGetEtherAdd())) {
//if (etheraddr[0] != 0xff)
//Etheradd = etheraddr;
//else
Etheradd = DEFAULT_ETHERADD;
}
//char *stone = extGetEtherAdd();
setenv("ETHERADD",Etheradd);
}
//stone add for test
//Etheradd = DEFAULT_ETHERADD;
//setenv("ETHERADD",Etheradd);
//stone add end
/* Apply the same logic as above to the IP address... */
if (!(IPadd = getenv("IPADD"))) {
if (!(IPadd = extGetIpAdd()))
IPadd = DEFAULT_IPADD;
setenv("IPADD",IPadd);
}
/* Convert addresses to binary:
*/
if (EtherToBin(Etheradd,BinEnetAddr) < 0)
return(-1);
/* If the ethernet address is 0:0:0:0:0:0, then we
* return an error here so that the interface is not
* brought up.
*/
if (memcmp(BinEnetAddr,AllZeroAddr,6) == 0) {
static int firsttime;
if (firsttime == 0) {
printf("\nNULL MAC address, network interface disabled.\n");
firsttime = 1;
}
return(-1);
}
#if INCLUDE_DHCPBOOT
if (DhcpIPCheck(IPadd) == -1)
return(-1);
#else
if (IpToBin(IPadd,BinIpAddr) < 0)
return(-1);
#endif
/* Initialize a unique number based on MAC: */
UniqueIpId = xcrc16(BinEnetAddr,6);
return(0);
}
/* processPACKET():
* This is the top level of the message processing after a complete
* packet has been received over ethernet. It's all just a lot of
* parsing to determine whether the message is for this board's IP
* address (broadcast reception may be enabled), and the type of
* incoming protocol. Once that is determined, the packet is either
* processed (TFTP, DHCP, ARP, ICMP-ECHO, etc...) or discarded.
*/
void
processPACKET(struct ether_header *ehdr, ushort size)
{
int i;
ushort *datap, udpport;
ulong csum;
struct ip *ihdr;
struct Udphdr *uhdr;
WATCHDOG_MACRO;
EtherRFRAMECnt++;
printPkt(ehdr,size,ETHER_INCOMING);
/* AppPktPtr is used by monRecvEnetPkt() so that an application can
* use the monitor's ethernet driver. For more info, refer to notes
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -