diff options
author | root <root@lamia.panaceas.james.local> | 2015-12-19 13:13:57 +0000 |
---|---|---|
committer | root <root@lamia.panaceas.james.local> | 2015-12-19 14:18:03 +0000 |
commit | 1a2238d1bddc823df06f67312d96ccf9de2893cc (patch) | |
tree | c58a3944d674a667f133ea5a730f5037e57d3d2e /cfe/cfe/net | |
download | bootloader-1a2238d1bddc823df06f67312d96ccf9de2893cc.tar.gz bootloader-1a2238d1bddc823df06f67312d96ccf9de2893cc.tar.bz2 bootloader-1a2238d1bddc823df06f67312d96ccf9de2893cc.zip |
CFE from danitool [without hostTools dir]: https://mega.nz/#!mwZyFK7a!CPT3BKC8dEw29kubtdYxhB91G9vIIismTkgzQ3iUy3k
Diffstat (limited to 'cfe/cfe/net')
-rw-r--r-- | cfe/cfe/net/README | 17 | ||||
-rw-r--r-- | cfe/cfe/net/dev_tcpconsole.c | 341 | ||||
-rw-r--r-- | cfe/cfe/net/mii.h | 187 | ||||
-rwxr-xr-x | cfe/cfe/net/net_api.c | 1095 | ||||
-rwxr-xr-x | cfe/cfe/net/net_api.h | 179 | ||||
-rwxr-xr-x | cfe/cfe/net/net_arp.c | 865 | ||||
-rw-r--r-- | cfe/cfe/net/net_dhcp.c | 869 | ||||
-rwxr-xr-x | cfe/cfe/net/net_dns.c | 354 | ||||
-rw-r--r-- | cfe/cfe/net/net_ebuf.h | 165 | ||||
-rw-r--r-- | cfe/cfe/net/net_ether.c | 705 | ||||
-rw-r--r-- | cfe/cfe/net/net_ether.h | 89 | ||||
-rw-r--r-- | cfe/cfe/net/net_icmp.c | 312 | ||||
-rwxr-xr-x | cfe/cfe/net/net_ip.c | 722 | ||||
-rwxr-xr-x | cfe/cfe/net/net_ip.h | 159 | ||||
-rw-r--r-- | cfe/cfe/net/net_ip_internal.h | 128 | ||||
-rwxr-xr-x | cfe/cfe/net/net_nmrp.c | 656 | ||||
-rwxr-xr-x | cfe/cfe/net/net_nmrp.h | 1 | ||||
-rwxr-xr-x | cfe/cfe/net/net_tcp.c | 2215 | ||||
-rwxr-xr-x | cfe/cfe/net/net_tcp.h | 83 | ||||
-rwxr-xr-x | cfe/cfe/net/net_tcp_internal.h | 244 | ||||
-rw-r--r-- | cfe/cfe/net/net_tcpbuf.c | 298 | ||||
-rw-r--r-- | cfe/cfe/net/net_tcpbuf.h | 83 | ||||
-rw-r--r-- | cfe/cfe/net/net_tftp.c | 921 | ||||
-rw-r--r-- | cfe/cfe/net/net_udp.c | 637 |
24 files changed, 11325 insertions, 0 deletions
diff --git a/cfe/cfe/net/README b/cfe/cfe/net/README new file mode 100644 index 0000000..f4c1c8c --- /dev/null +++ b/cfe/cfe/net/README @@ -0,0 +1,17 @@ + +This directory contains the network modules for CFE. The +following standards are implemented here: + +* Ethernet datalink +* ARP +* IP +* UDP +* ICMP +* TFTP +* BOOTP/DHCP +* DNS (for queries only) +* TCP + +If anyone really screams for it, I've considered putting a simple +(very simple) TCP stack in here as well, but it doesn't seem worth it. + diff --git a/cfe/cfe/net/dev_tcpconsole.c b/cfe/cfe/net/dev_tcpconsole.c new file mode 100644 index 0000000..43b4f1c --- /dev/null +++ b/cfe/cfe/net/dev_tcpconsole.c @@ -0,0 +1,341 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * TCP Console Driver File: dev_tcpconsole.c + * + * Evil hack: A console driver that uses a TCP socket. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +#include "lib_types.h" +#include "lib_malloc.h" +#include "lib_printf.h" +#include "cfe_iocb.h" +#include "cfe_device.h" +#include "cfe_ioctl.h" +#include "addrspace.h" + +#include "cfe_timer.h" + +#include "bsp_config.h" + +#if CFG_TCP +#include "net_ebuf.h" +#include "net_api.h" + +/* + * Friendly warning: Don't put printfs in here or enable any + * debugging messages in the TCP stack if you're really + * going to use this for your console device. You'll end up + * with a recursion loop! + */ + +/* ********************************************************************* + * Constants + ********************************************************************* */ + +#define TCPCONSOLE_DEFAULT_PORT 23 /* telnet */ + +/* ********************************************************************* + * Forward + ********************************************************************* */ + +static void tcpconsole_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr); + +static int tcpconsole_open(cfe_devctx_t *ctx); +static int tcpconsole_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int tcpconsole_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat); +static int tcpconsole_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int tcpconsole_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int tcpconsole_close(cfe_devctx_t *ctx); + +const static cfe_devdisp_t tcpconsole_dispatch = { + tcpconsole_open, + tcpconsole_read, + tcpconsole_inpstat, + tcpconsole_write, + tcpconsole_ioctl, + tcpconsole_close, + NULL, + NULL +}; + +const cfe_driver_t tcpconsole = { + "TCP Console", + "tcpconsole", + CFE_DEV_SERIAL, + &tcpconsole_dispatch, + tcpconsole_probe +}; + + +/* ********************************************************************* + * tcpconsole structure + ********************************************************************* */ + +/* + * States our connection can be in + */ + +#define TCPCONSTAT_IDLE 0 +#define TCPCONSTAT_LISTEN 1 +#define TCPCONSTAT_CONNECTED 2 +#define TCPCONSTAT_DISCONNECTED 3 +#define TCPCONSTAT_BROKEN 4 + +/* + * state information + */ + +typedef struct tcpconsole_s { + int tcp_socket; + int tcp_status; + int tcp_port; +} tcpconsole_t; + + +static void tcpconsole_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr) +{ + tcpconsole_t *softc; + char descr[80]; + + softc = (tcpconsole_t *) KMALLOC(sizeof(tcpconsole_t),0); + if (softc) { + softc->tcp_socket = -1; + softc->tcp_status = TCPCONSTAT_IDLE; + xsprintf(descr, "%s", drv->drv_description); + + if (probe_a == 0) probe_a = TCPCONSOLE_DEFAULT_PORT; + + softc->tcp_port = (int)probe_a; + + cfe_attach(drv, softc, NULL, descr); + } +} + + +static int tcpconsole_isready(tcpconsole_t *softc,int *rxbytes) +{ + int res; + int connstat,rxeof; + + res = tcp_status(softc->tcp_socket,&connstat,rxbytes,&rxeof); + + /* + * Return: + * -1 if we could not get status + * 0 if we are not connected or are connected and at EOF + * 1 if we are connected. + */ + + if (res < 0) return res; + if (connstat != TCPSTATUS_CONNECTED) return 0; + if (!rxeof) return 1; + + return 0; +} + + +static int tcpconsole_process(tcpconsole_t *softc) +{ + int res = 0; + + switch (softc->tcp_status) { + case TCPCONSTAT_IDLE: + /* Idle, set up listening socket */ + res = tcp_socket(); + if (res < 0) { + softc->tcp_status = TCPCONSTAT_BROKEN; + return res; + } + softc->tcp_socket = res; + + res = tcp_listen(softc->tcp_socket,softc->tcp_port); + + if (res < 0) { + tcp_close(softc->tcp_socket); + softc->tcp_status = TCPCONSTAT_BROKEN; + softc->tcp_socket = -1; + return res; + } + softc->tcp_status = TCPCONSTAT_LISTEN; + break; + + case TCPCONSTAT_LISTEN: + /* Still waiting for a connection */ + res = 0; + if (tcpconsole_isready(softc,NULL) > 0) { + softc->tcp_status = TCPCONSTAT_CONNECTED; + } + break; + + case TCPCONSTAT_CONNECTED: + res = 0; /* do nothing, we're okay */ + break; + + case TCPCONSTAT_DISCONNECTED: + /* Currently connected, kill off this connection */ + tcp_close(softc->tcp_socket); + softc->tcp_socket = -1; + softc->tcp_status = TCPCONSTAT_IDLE; + break; + + case TCPCONSTAT_BROKEN: + /* Broken. Stay broken. */ + res = 0; + break; + } + + return res; +} + + +static int tcpconsole_open(cfe_devctx_t *ctx) +{ + return 0; +} + +static int tcpconsole_read(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + tcpconsole_t *softc = ctx->dev_softc; + unsigned char *bptr; + int blen; + int res; + + POLL(); + tcpconsole_process(softc); + + buffer->buf_retlen = 0; + + if (softc->tcp_status == TCPCONSTAT_CONNECTED) { + bptr = buffer->buf_ptr; + blen = buffer->buf_length; + + if (tcpconsole_isready(softc,NULL) <= 0) { + softc->tcp_status = TCPCONSTAT_DISCONNECTED; + return 0; + } + + res = tcp_recv(softc->tcp_socket,bptr,blen); + + if (res > 0) { + buffer->buf_retlen = res; + } + } + + return 0; +} + +static int tcpconsole_inpstat(cfe_devctx_t *ctx, iocb_inpstat_t *inpstat) +{ + tcpconsole_t *softc = ctx->dev_softc; + unsigned int rxbytes; + + POLL(); + tcpconsole_process(softc); + + inpstat->inp_status = 0; + + if (softc->tcp_status == TCPCONSTAT_CONNECTED) { + if (tcpconsole_isready(softc,&rxbytes) <= 0) { + softc->tcp_status = TCPCONSTAT_DISCONNECTED; + } + else { + inpstat->inp_status = (rxbytes > 0); + } + } + + return 0; +} + +static int tcpconsole_write(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + tcpconsole_t *softc = ctx->dev_softc; + unsigned char *bptr; + int blen; + int res; + + POLL(); + tcpconsole_process(softc); + + buffer->buf_retlen = 0; + + if (softc->tcp_status == TCPCONSTAT_CONNECTED) { + bptr = buffer->buf_ptr; + blen = buffer->buf_length; + + if (tcpconsole_isready(softc,NULL) <= 0) { + softc->tcp_status = TCPCONSTAT_DISCONNECTED; + return 0; + } + + res = tcp_send(softc->tcp_socket,bptr,blen); + + if (res > 0) { + buffer->buf_retlen = res; + } + } + + return 0; +} + +static int tcpconsole_ioctl(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + return -1; +} + +static int tcpconsole_close(cfe_devctx_t *ctx) +{ + tcpconsole_t *softc = ctx->dev_softc; + + if (softc->tcp_status == TCPCONSTAT_CONNECTED) { + softc->tcp_status = TCPCONSTAT_DISCONNECTED; + } + + return 0; +} + + +#endif diff --git a/cfe/cfe/net/mii.h b/cfe/cfe/net/mii.h new file mode 100644 index 0000000..ac56958 --- /dev/null +++ b/cfe/cfe/net/mii.h @@ -0,0 +1,187 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * MII register definitions File: mii.h + * + * Register and bit definitions for the standard MII management + * interface. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + +#ifndef _MII_H_ +#define _MII_H_ + +/* Access/command codes */ + +#define MII_COMMAND_START 0x01 +#define MII_COMMAND_READ 0x02 +#define MII_COMMAND_WRITE 0x01 +#define MII_COMMAND_ACK 0x02 + + +/* Registers */ + +#define MII_BMCR 0x00 /* Basic Mode Control (rw) */ +#define MII_BMSR 0x01 /* Basic Mode Status (ro) */ +#define MII_PHYIDR1 0x02 +#define MII_PHYIDR2 0x03 +#define MII_ANAR 0x04 /* Autonegotiation Advertisement */ +#define MII_ANLPAR 0x05 /* Autonegotiation Link Partner Ability (rw) */ +#define MII_ANER 0x06 /* Autonegotiation Expansion */ +#define MII_K1CTL 0x09 /* 1000baseT control */ +#define MII_K1STSR 0x0A /* 1K Status Register (ro) */ +#define MII_AUXCTL 0x18 /* aux control register */ + + +/* Basic Mode Control register (RW) */ + +#define BMCR_RESET 0x8000 +#define BMCR_LOOPBACK 0x4000 +#define BMCR_SPEED0 0x2000 +#define BMCR_ANENABLE 0x1000 +#define BMCR_POWERDOWN 0x0800 +#define BMCR_ISOLATE 0x0400 +#define BMCR_RESTARTAN 0x0200 +#define BMCR_DUPLEX 0x0100 +#define BMCR_COLTEST 0x0080 +#define BMCR_SPEED1 0x0040 +#define BMCR_SPEED1000 (BMCR_SPEED1) +#define BMCR_SPEED100 (BMCR_SPEED0) +#define BMCR_SPEED10 0 + + +/* Basic Mode Status register (RO) */ + +#define BMSR_100BT4 0x8000 +#define BMSR_100BT_FDX 0x4000 +#define BMSR_100BT_HDX 0x2000 +#define BMSR_10BT_FDX 0x1000 +#define BMSR_10BT_HDX 0x0800 +#define BMSR_100BT2_FDX 0x0400 +#define BMSR_100BT2_HDX 0x0200 +#define BMSR_1000BT_XSR 0x0100 +#define BMSR_PRESUP 0x0040 +#define BMSR_ANCOMPLETE 0x0020 +#define BMSR_REMFAULT 0x0010 +#define BMSR_AUTONEG 0x0008 +#define BMSR_LINKSTAT 0x0004 +#define BMSR_JABDETECT 0x0002 +#define BMSR_EXTCAPAB 0x0001 + + +/* PHY Identifer registers (RO) */ + +#define PHYIDR1 0x2000 +#define PHYIDR2 0x5C60 + + +/* Autonegotiation Advertisement register (RW) */ + +#define ANAR_NP 0x8000 +#define ANAR_RF 0x2000 +#define ANAR_ASYPAUSE 0x0800 +#define ANAR_PAUSE 0x0400 +#define ANAR_T4 0x0200 +#define ANAR_TXFD 0x0100 +#define ANAR_TXHD 0x0080 +#define ANAR_10FD 0x0040 +#define ANAR_10HD 0x0020 +#define ANAR_PSB 0x001F + +#define PSB_802_3 0x0001 /* 802.3 */ + +/* Autonegotiation Link Partner Abilities register (RW) */ + +#define ANLPAR_NP 0x8000 +#define ANLPAR_ACK 0x4000 +#define ANLPAR_RF 0x2000 +#define ANLPAR_ASYPAUSE 0x0800 +#define ANLPAR_PAUSE 0x0400 +#define ANLPAR_T4 0x0200 +#define ANLPAR_TXFD 0x0100 +#define ANLPAR_TXHD 0x0080 +#define ANLPAR_10FD 0x0040 +#define ANLPAR_10HD 0x0020 +#define ANLPAR_PSB 0x001F + + +/* Autonegotiation Expansion register (RO) */ + +#define ANER_PDF 0x0010 +#define ANER_LPNPABLE 0x0008 +#define ANER_NPABLE 0x0004 +#define ANER_PAGERX 0x0002 +#define ANER_LPANABLE 0x0001 + + +#define ANNPTR_NP 0x8000 +#define ANNPTR_MP 0x2000 +#define ANNPTR_ACK2 0x1000 +#define ANNPTR_TOGTX 0x0800 +#define ANNPTR_CODE 0x0008 + +#define ANNPRR_NP 0x8000 +#define ANNPRR_MP 0x2000 +#define ANNPRR_ACK3 0x1000 +#define ANNPRR_TOGTX 0x0800 +#define ANNPRR_CODE 0x0008 + + +#define K1TCR_TESTMODE 0x0000 +#define K1TCR_MSMCE 0x1000 +#define K1TCR_MSCV 0x0800 +#define K1TCR_RPTR 0x0400 +#define K1TCR_1000BT_FDX 0x200 +#define K1TCR_1000BT_HDX 0x100 + +#define K1STSR_MSMCFLT 0x8000 +#define K1STSR_MSCFGRES 0x4000 +#define K1STSR_LRSTAT 0x2000 +#define K1STSR_RRSTAT 0x1000 +#define K1STSR_LP1KFD 0x0800 +#define K1STSR_LP1KHD 0x0400 +#define K1STSR_LPASMDIR 0x0200 + +#define K1SCR_1KX_FDX 0x8000 +#define K1SCR_1KX_HDX 0x4000 +#define K1SCR_1KT_FDX 0x2000 +#define K1SCR_1KT_HDX 0x1000 + +#endif /* _MII_H_ */ diff --git a/cfe/cfe/net/net_api.c b/cfe/cfe/net/net_api.c new file mode 100755 index 0000000..c9f119e --- /dev/null +++ b/cfe/cfe/net/net_api.c @@ -0,0 +1,1095 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * Top-level API to network File: net_api.c + * + * This routine contains the highest-level API to the network + * routines. The global handle to the network state is right here. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +#include "bsp_config.h" + +#include "lib_types.h" +#include "lib_string.h" +#include "lib_queue.h" +#include "lib_malloc.h" +#include "lib_printf.h" + +#include "cfe_iocb.h" +#include "cfe_devfuncs.h" +#include "cfe_ioctl.h" +#include "cfe_timer.h" + +#include "cfe_error.h" + +#include "net_ebuf.h" +#include "net_ether.h" + +#include "cfe_timer.h" + +#include "net_ip.h" +#include "net_ip_internal.h" +#include "net_api.h" + +#include "env_subr.h" + +#if CFG_TCP +#include "net_tcp.h" +#endif + + +/* ********************************************************************* + * Structures + ********************************************************************* */ + +/* + * Net context. All the soft context structures of all the + * layers of the network stack are bundled here. There's only one + * of these in the system when the network is active. + */ + +typedef struct net_ctx_s { + /* Global info */ + int64_t timer; + + /* device name */ + char *devname; + + /* Run-time info for IP interface */ + ip_info_t *ipinfo; + + /* Info for Ethernet interface */ + ether_info_t *ethinfo; + + /* Info specific to UDP */ + udp_info_t *udpinfo; + + /* Info specific to ICMP */ + icmp_info_t *icmpinfo; + +#if CFG_TCP + /* Info specific to TCP */ + tcp_info_t *tcpinfo; +#endif +} net_ctx_t; + +/* ********************************************************************* + * Globals + ********************************************************************* */ + +/* Foxconn add start by Cliff Wang, 03/23/2010 */ +net_ctx_t *netctx = NULL; +// static net_ctx_t *netctx = NULL; +/* Foxconn add end by Cliff Wang, 03/23/2010 */ + +/* ********************************************************************* + * UDP INTERFACE + ********************************************************************* */ + +/* ********************************************************************* + * udp_alloc() + * + * Allocate an ebuf with fields reserved for the UDP layer. + * + * Input parameters: + * nothing + * + * Return value: + * pointer to ebuf, or NULL if no EBUFs are available + ********************************************************************* */ + +ebuf_t *udp_alloc(void) +{ + if (!netctx) return NULL; + return _udp_alloc(netctx->udpinfo); +} + +/* ********************************************************************* + * udp_free(buf) + * + * Return an ebuf to the pool. The ebuf was presumably allocated + * via udp_alloc() first. + * + * Input parameters: + * buf - ebuf to return to the pool + * + * Return value: + * nothing + ********************************************************************* */ +void udp_free(ebuf_t *buf) +{ + if (!netctx) return; + _udp_free(netctx->udpinfo,buf); +} + +/* ********************************************************************* + * udp_socket(port) + * + * Open a UDP socket. Once open, datagrams sent on the socket will + * go to the specified port number. You can change the port later + * using the "udp_connect" function. + * + * Input parameters: + * port - port number + * + * Return value: + * UDP port handle, or -1 if no ports are available. + ********************************************************************* */ + +int udp_socket(uint16_t port) +{ + if (!netctx) return -1; + + return _udp_socket(netctx->udpinfo,port); +} + +/* ********************************************************************* + * udp_close(sock) + * + * Close a udp socket. You pass this handle returned from a previous + * call to udp_open. + * + * Input parameters: + * handle - UDP port handle, from udp_open() + * + * Return value: + * nothing + ********************************************************************* */ + +void udp_close(int portnum) +{ + if (!netctx) return; + + _udp_close(netctx->udpinfo,portnum); +} + + +/* ********************************************************************* + * udp_send(s,buf,dest) + * + * Send a datagram to the specified destination address. The + * source and destination UDP port numbers are taken from the + * values passed to earlier calls to udp_open, udp_bind, and + * udp_connect. + * + * Input parameters: + * s - socket handle, from udp_open + * buf - ebuf to send (allocated via udp_alloc) + * dest - pointer to 4-byte destination IP address + * + * Return value: + * 0 if ok + * <0 if an error occured. + ********************************************************************* */ + +int udp_send(int s,ebuf_t *buf,uint8_t *dest) +{ + if (!netctx) return -1; + + return _udp_send(netctx->udpinfo,s,buf,dest); +} + +/* ********************************************************************* + * udp_bind(s,port) + * + * Re-"bind" the specified udp socket to a new source port. + * This changes the source port number that will be transmitted + * in subsequent calls to udp_send() + * + * Input parameters: + * s - socket handle + * port - new port number + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +int udp_bind(int s,uint16_t port) +{ + if (!netctx) return -1; + + return _udp_bind(netctx->udpinfo,s,port); +} + + +/* ********************************************************************* + * udp_connect(s,port) + * + * Set the port number to be used in the destination port field + * for subsequent calls to udp_send(). + * + * Input parameters: + * s - udp socket handle + * port - new destination port number + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +int udp_connect(int s,uint16_t port) +{ + if (!netctx) return -1; + + return _udp_connect(netctx->udpinfo,s,port); +} + +/* ********************************************************************* + * udp_recv(s) + * + * Return the next packet from the receive queue for this port. + * If no packets are available, NULL is returned. + * + * Input parameters: + * s - udp port handle + * + * Return value: + * ebuf (if a packet is available) + * NULL (no packet available) + ********************************************************************* */ + +ebuf_t *udp_recv(int s) +{ + if (!netctx) return NULL; + + return _udp_recv(netctx->udpinfo,s); +} + + +/* ********************************************************************* + * udp_recv_with_timeout(s,seconds) + * + * Return the next packet from the receive queue for this socket, + * waiting for one to arrive if there are none available. + * + * Input parameters: + * s - udp socket handle + * seconds - number of seconds to wait + * + * Return value: + * ebuf (if a packet is available) + * NULL (no packet available after timeout) + ********************************************************************* */ + +ebuf_t *udp_recv_with_timeout(int s,int seconds) +{ + ebuf_t *buf = NULL; + int64_t timer; + + if (!netctx) return NULL; + + TIMER_SET(timer,seconds*CFE_HZ); + + while (!TIMER_EXPIRED(timer)) { + POLL(); + buf = _udp_recv(netctx->udpinfo,s); + if (buf) break; + } + + return buf; +} + + + +#if CFG_TCP +/* ********************************************************************* + * TCP INTERFACE + ********************************************************************* */ + + +/* ********************************************************************* + * tcp_socket() + * + * Create a new TCP port. + * + * Input parameters: + * nothing. + * + * Return value: + * TCP port handle, or <0 if no ports are available. + ********************************************************************* */ + +int tcp_socket(void) +{ + if (!netctx) return -1; + + return _tcp_socket(netctx->tcpinfo); +} + +/* ********************************************************************* + * tcp_connect(handle,dest,port) + * + * Connect to a remote TCP destination. + * + * Input parameters: + * handle - returned from tcp_create + * dest - destination IP address + * port - destination port number + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int tcp_connect(int s,uint8_t *dest,uint16_t port) +{ + int res; + unsigned int flags; + unsigned int connflag; + + if (!netctx) return -1; + + /* + * Get socket's blocking status + * If nonblocking, just call the tcp stack + * and return what it returns. + */ + + res = _tcp_getflags(netctx->tcpinfo,s,&flags); + if (res < 0) return res; + + if (flags & TCPFLG_NBIO) { + return _tcp_connect(netctx->tcpinfo,s,dest,port); + } + + /* + * Otherwise, call connect and poll till the status + * changes. We want to see a transition to the + * CONNECTED state, so we loop while we see "CONNECTING" + * and return a status based on what it changes to. + */ + + res = _tcp_connect(netctx->tcpinfo,s,dest,port); + if (res < 0) return res; + connflag = TCPSTATUS_NOTCONN; + + for (;;) { + POLL(); + + res = _tcp_status(netctx->tcpinfo,s,&connflag,NULL,NULL); + if (res < 0) break; + + if (connflag == TCPSTATUS_CONNECTING) continue; + break; + } + + if (connflag != TCPSTATUS_CONNECTED) return CFE_ERR_NOTCONN; + + return res; +} + +/* ********************************************************************* + * tcp_close(s) + * + * Disconnect a connection (cleanly) + * + * Input parameters: + * s - handle from tcp_create + * + * Return value: + * 0 if ok + * else error + ********************************************************************* */ + +int tcp_close(int s) +{ + if (!netctx) return -1; + + return _tcp_close(netctx->tcpinfo,s); +} + + + +/* ********************************************************************* + * tcp_send(s,buf,len) + * + * Send a buffer to the other TCP, buffering as much data as + * will fit in the send buffer. + * + * Input parameters: + * s - port handle, from tcp_open + * buf - buffer pointer + * len - length of buffer to send + * + * Return value: + * >=0 if ok (number of bytes sent) + * <0 if an error occured. + ********************************************************************* */ + +int tcp_send(int s,uint8_t *buf,int len) +{ + unsigned int flags; + int res; + int total = 0; + + if (!netctx) return -1; + + /* + * Get socket's blocking status + * If nonblocking, just call the tcp stack + * and return what it returns. + */ + + res = _tcp_getflags(netctx->tcpinfo,s,&flags); + if (res < 0) return res; + + if (flags & TCPFLG_NBIO) { + return _tcp_send(netctx->tcpinfo,s,buf,len); + } + + /* + * The first time we'll check the return code for an + * error so we can pass up the failure. + */ + + res = _tcp_send(netctx->tcpinfo,s,buf,len); + if (res < 0) return res; + + buf += res; + len -= res; + total += res; + + while (len > 0) { + /* + * Give the TCP stack and devices a chance to run + */ + + POLL(); + + /* + * Try to send some more. If we get an error, get out. + * otherwise, keep going till all the data is gone. + */ + + res = _tcp_send(netctx->tcpinfo,s,buf,len); + if (res < 0) break; + buf += res; + len -= res; + total += res; + } + + /* + * If we sent nothing and have an error, return the error. + * Otherwise return the amount of data we sent. + */ + if ((total == 0) && (res < 0)) return res; + else return total; +} + +/* ********************************************************************* + * tcp_recv(s,buf,len) + * + * Receive data from the remote TCP session + * + * Input parameters: + * s - port handle, from tcp_open + * buf - buffer pointer + * len - length of buffer to send + * + * Return value: + * >=0 if ok (number of bytes received) + * <0 if an error occured. + ********************************************************************* */ + +int tcp_recv(int s,uint8_t *buf,int len) +{ + unsigned int flags; + int res; + int total = 0; + + if (!netctx) return -1; + + /* + * Get socket's blocking status + * If nonblocking, just call the tcp stack + * and return what it returns. + */ + + res = _tcp_getflags(netctx->tcpinfo,s,&flags); + if (res < 0) return res; + + if (flags & TCPFLG_NBIO) { + return _tcp_recv(netctx->tcpinfo,s,buf,len); + } + + /* + * The first time we'll check the return code for an + * error so we can pass up the failure. + */ + + res = _tcp_recv(netctx->tcpinfo,s,buf,len); + if (res < 0) return res; + + buf += res; + len -= res; + total += res; + + while (len > 0) { + /* + * Give the TCP stack and devices a chance to run + */ + + POLL(); + + /* + * Try to receive some more. If we get an error, get out. + * otherwise, keep going till all the data is gone. + */ + + res = _tcp_recv(netctx->tcpinfo,s,buf,len); + if (res < 0) break; + + if (res == 0) { + _tcp_status(netctx->tcpinfo,s,&flags,NULL,NULL); + if (flags != TCPSTATUS_CONNECTED) { + res = CFE_ERR_NOTCONN; + break; + } + } + + buf += res; + len -= res; + total += res; + } + + /* + * If we sent received and have an error, return the error. + * Otherwise return the amount of data we sent. + */ + if ((total == 0) && (res < 0)) return res; + else return total; + +} + +/* ********************************************************************* + * tcp_bind(s,port) + * + * Re-"bind" the specified tcp port handle to a new source port. + * + * Used for listening sockets. + * + * Input parameters: + * s - port handle + * port - new port number + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +int tcp_bind(int s,uint16_t port) +{ + if (!netctx) return -1; + + return _tcp_bind(netctx->tcpinfo,s,port); +} + +/* ********************************************************************* + * tcp_peeraddr(s,addr,port) + * + * Return the address of the remote peer. + * + * Input parameters: + * s - port handle + * addr - points to 4-byte buffer to receive IP address + * port - points to uint16 to receive port number + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +int tcp_peeraddr(int s,uint8_t *addr,uint16_t *port) +{ + if (!netctx) return -1; + + return _tcp_peeraddr(netctx->tcpinfo,s,addr,port); +} + +/* ********************************************************************* + * tcp_setflags(s,addr,flags) + * + * Set per-socket flags (nodelay, etc.) + * + * Input parameters: + * s - port handle + * flags - flags for this socket + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +int tcp_setflags(int s,unsigned int flags) +{ + if (!netctx) return -1; + + return _tcp_setflags(netctx->tcpinfo,s,flags); +} + +/* ********************************************************************* + * tcp_getflags(s,addr,flags) + * + * Get per-socket flags (nodelay, etc.) + * + * Input parameters: + * s - port handle + * flags - flags for this socket + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +int tcp_getflags(int s,unsigned int *flags) +{ + if (!netctx) return -1; + + return _tcp_getflags(netctx->tcpinfo,s,flags); +} + + +/* ********************************************************************* + * tcp_listen(s) + * + * Set the socket into "listen" mode. + * + * Input parameters: + * s - port handle + * port - port # to listen on + * + * Return value: + * 0 if ok + * else error + ********************************************************************* */ + +int tcp_listen(int s,uint16_t port) +{ + if (!netctx) return -1; + + return _tcp_listen(netctx->tcpinfo,s,port); +} + +/* ********************************************************************* + * tcp_status(s,connflag,rxready,rxeof) + * + * Return the TCP connection's status + * + * Input parameters: + * s - port handle + * connflag - points to flag to receive connected status + * rxready - returns # of bytes ready to receive + * rxeof - returns TRUE if we've been FINed. + * + * Return value: + * 0 if ok + * else error + ********************************************************************* */ + +int tcp_status(int s,unsigned int *connflag,int *rxready,int *rxeof) +{ + if (!netctx) return -1; + + return _tcp_status(netctx->tcpinfo,s,connflag,rxready,rxeof); +} + +/* ********************************************************************* + * tcp_debug(s,arg) + * + * Call the debug routine in the tcp stack. + * + * Input parameters: + * s - socket handle + * arg - passed to debug routine + * + * Return value: + * return value from debug routine + ********************************************************************* */ + +int tcp_debug(int s,int arg) +{ + if (!netctx) return -1; + return _tcp_debug(netctx->tcpinfo,s,arg); +} + +#endif + +/* ********************************************************************* + * ARP FUNCTIONS + ********************************************************************* */ + + +/* ********************************************************************* + * arp_add(destip,desthw) + * + * Add a permanent entry to the ARP table. This entry will + * persist until deleted or the interface is deactivated. + * This may cause a stale entry to be deleted if the table is full + * + * Input parameters: + * destip - pointer to 4-byte destination IP address + * desthw - pointer to 6-byte destination hardware address + * + * Return value: + * nothing + ********************************************************************* */ + +void arp_add(uint8_t *destip,uint8_t *desthw) +{ + if (netctx) _arp_add(netctx->ipinfo,destip,desthw); +} + +/* ********************************************************************* + * arp_lookup(destip) + * + * Look up the hardware address for an IP address. + * + * Input parameters: + * destip - pointer to 4-byte IP address + * + * Return value: + * pointer to 6-byte hardware address, or NULL if there are + * no matching entries in the table. + ********************************************************************* */ + +uint8_t *arp_lookup(uint8_t *destip) +{ + if (!netctx) return NULL; + return _arp_lookup(netctx->ipinfo,destip); +} + + +/* ********************************************************************* + * arp_enumerate(entrynum,ipaddr,hwaddr) + * + * Return an entry from the ARP table. + * + * Input parameters: + * entrynum - entry number to return, starting with zero + * ipaddr - pointer to 4 bytes to receive IP address + * hwaddr - pointer to 6 bytes to receive hardware address + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int arp_enumerate(int entrynum,uint8_t *ipaddr,uint8_t *hwaddr) +{ + if (!netctx) return -1; + return _arp_enumerate(netctx->ipinfo,entrynum,ipaddr,hwaddr); +} + +/* ********************************************************************* + * arp_delete(ipaddr) + * + * Delete an entry from the ARP table. + * + * Input parameters: + * ipaddr - pointer to 4-byte IP address + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int arp_delete(uint8_t *ipaddr) +{ + if (!netctx) return -1; + return _arp_delete(netctx->ipinfo,ipaddr); +} + +/* ********************************************************************* + * ICMP FUNCTIONS + ********************************************************************* */ + +/* ********************************************************************* + * icmp_ping(dest,seq,len) + * + * Ping a remote host, transmitting the ICMP_ECHO message and + * waiting for the corresponding ICMP_ECHO_REPLY. + * + * Input parameters: + * dest - pointer to 4-byte destination IP address + * seq - sequence number to put in to the ICMP packet + * len - length of data to place in ICMP packet + * + * Return value: + * 0 if ok (remote host responded) + * else error code + ********************************************************************* */ + +int icmp_ping(uint8_t *dest,int seq,int len) +{ + if (!netctx) return -1; + return _icmp_ping(netctx->icmpinfo,dest,seq,len); +} + +/* ********************************************************************* + * INIT/CONFIG FUNCTIONS + ********************************************************************* */ + +/* ********************************************************************* + * net_getparam(param) + * + * Return a parameter from the current IP configuration. This is + * the main call to set the IP address, netmask, gateway, + * name server, host name, etc. + * + * Input parameters: + * param - parameter number (see net_api.h) + * + * Return value: + * pointer to value of parameter, or NULL if parameter + * ID is invalid + ********************************************************************* */ + +uint8_t *net_getparam(int param) +{ + if (!netctx) return NULL; + if (param == NET_DEVNAME) return (uint8_t *) netctx->devname; + return _ip_getparam(netctx->ipinfo,param); + +} + +/* ********************************************************************* + * net_setparam(param,ptr) + * + * Set the value of an IP configuration parameter + * + * Input parameters: + * param - parameter number (see net_api.h) + * ptr - pointer to parameter's new value + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int net_setparam(int param,uint8_t *ptr) +{ + if (!netctx) return NULL; + return _ip_setparam(netctx->ipinfo,param,ptr); +} + +/* ********************************************************************* + * net_poll() + * + * Process background tasks for the network stack, maintaining + * the ARP table, receive queues, etc. + * + * Input parameters: + * nothing + * + * Return value: + * nothing + ********************************************************************* */ + +static void net_poll(void *arg) +{ + if (netctx) { + eth_poll(netctx->ethinfo); + if (TIMER_EXPIRED(netctx->timer)) { + _ip_timer_tick(netctx->ipinfo); + TIMER_SET(netctx->timer,CFE_HZ); + } + } +} + +/* ********************************************************************* + * net_init(devname) + * + * Initialize the network interface. This is the main call, once + * completed you should call net_setparam to set up the network + * addresses and stuff. + * + * Input parameters: + * devname - CFE device name for network device + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int net_init(char *devname) +{ + net_ctx_t *ctx; + + if (netctx) net_uninit(); + + ctx = KMALLOC(sizeof(net_ctx_t),0); + + if (!ctx) return -1; + + ctx->devname = strdup(devname); + + ctx->ethinfo = eth_init(devname); + if (ctx->ethinfo == NULL) { + return -1; + } + + ctx->ipinfo = _ip_init(ctx->ethinfo); + if (ctx->ipinfo == NULL) { + eth_uninit(ctx->ethinfo); + return -1; + } + + ctx->udpinfo = _udp_init(ctx->ipinfo,ctx->ipinfo); + if (ctx->udpinfo == NULL) { + _ip_uninit(ctx->ipinfo); + eth_uninit(ctx->ethinfo); + return -1; + } + + ctx->icmpinfo = _icmp_init(ctx->ipinfo); + if (ctx->icmpinfo == NULL) { + _udp_uninit(ctx->udpinfo); + _ip_uninit(ctx->ipinfo); + eth_uninit(ctx->ethinfo); + return -1; + } + + cfe_bg_add(net_poll,ctx); + TIMER_SET(ctx->timer,CFE_HZ); + +#if CFG_TCP + ctx->tcpinfo = _tcp_init(ctx->ipinfo,ctx->ipinfo); + cfe_bg_add(_tcp_poll,ctx->tcpinfo); +#endif + + netctx = ctx; + + return 0; +} + + +/* ********************************************************************* + * net_uninit() + * + * Uninitialize the network, deallocating all resources allocated + * to the network and closing all open device handles + * + * Input parameters: + * nothing + * + * Return value: + * nothing + ********************************************************************* */ + +void net_uninit(void) +{ + if (netctx) { +#if CFG_TCP + cfe_bg_remove(_tcp_poll); + _tcp_uninit(netctx->tcpinfo); +#endif + TIMER_CLEAR(netctx->timer); + _icmp_uninit(netctx->icmpinfo); + _udp_uninit(netctx->udpinfo); + _ip_uninit(netctx->ipinfo); + eth_uninit(netctx->ethinfo); + KFREE(netctx->devname); + KFREE(netctx); + netctx = NULL; + cfe_bg_remove(net_poll); + } +} + + +/* ********************************************************************* + * net_setnetvars() + * + * Set environment variables related to the network. + * + * Input parameters: + * nothing + * + * Return value: + * nothing + ********************************************************************* */ + +void net_setnetvars(void) +{ + char *x; + uint8_t *addr; + char str[60]; + + /* Clear out all the environment variables */ + env_delenv("NET_DEVICE"); + env_delenv("NET_IPADDR"); + env_delenv("NET_NETMASK"); + env_delenv("NET_GATEWAY"); + env_delenv("NET_NAMESERVER"); + env_delenv("NET_DOMAIN"); + + x = (char *) net_getparam(NET_DEVNAME); + if (!x) { + return; + } + + x = (char *) net_getparam(NET_DEVNAME); + if (x) env_setenv("NET_DEVICE",x,ENV_FLG_BUILTIN); + + x = (char *) net_getparam(NET_DOMAIN); + if (x) env_setenv("NET_DOMAIN",x,ENV_FLG_BUILTIN); + + addr = net_getparam(NET_IPADDR); + if (addr) { + xsprintf(str,"%I",addr); + env_setenv("NET_IPADDR",str,ENV_FLG_BUILTIN); + } + + addr = net_getparam(NET_NETMASK); + if (addr) { + xsprintf(str,"%I",addr); + env_setenv("NET_NETMASK",str,ENV_FLG_BUILTIN); + } + + addr = net_getparam(NET_GATEWAY); + if (addr) { + xsprintf(str,"%I",addr); + env_setenv("NET_GATEWAY",str,ENV_FLG_BUILTIN); + } + + addr = net_getparam(NET_NAMESERVER); + if (addr) { + xsprintf(str,"%I",addr); + env_setenv("NET_NAMESERVER",str,ENV_FLG_BUILTIN); + } + +} + diff --git a/cfe/cfe/net/net_api.h b/cfe/cfe/net/net_api.h new file mode 100755 index 0000000..f28b917 --- /dev/null +++ b/cfe/cfe/net/net_api.h @@ -0,0 +1,179 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * High-level network API defs File: net_api.h + * + * This module contains prototypes and constants for the + * network (TCP/IP) interface. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +/* ********************************************************************* + * Constants + ********************************************************************* */ + +#ifndef IP_ADDR_LEN +#define IP_ADDR_LEN 4 +#endif + +/* ********************************************************************* + * DHCP Protocol + ********************************************************************* */ + +typedef struct dhcpreply_s { + uint8_t dr_ipaddr[IP_ADDR_LEN]; + uint8_t dr_netmask[IP_ADDR_LEN]; + uint8_t dr_gateway[IP_ADDR_LEN]; + uint8_t dr_nameserver[IP_ADDR_LEN]; + uint8_t dr_dhcpserver[IP_ADDR_LEN]; + uint8_t dr_bootserver[IP_ADDR_LEN]; + char *dr_hostname; + char *dr_domainname; + char *dr_bootfile; + char *dr_rootpath; + char *dr_swapserver; + char *dr_script; + char *dr_options; +} dhcpreply_t; + + +int dhcp_bootrequest(dhcpreply_t **reply); +void dhcp_free_reply(dhcpreply_t *reply); +void dhcp_set_envvars(dhcpreply_t *reply); + +/* ********************************************************************* + * IP Layer + ********************************************************************* */ + +void ip_uninit(void); +int ip_init(char *,uint8_t *); +ebuf_t *ip_alloc(void); +void ip_free(ebuf_t *buf); +int ip_send(ebuf_t *buf,uint8_t *destaddr,uint8_t proto); +uint16_t ip_chksum(uint16_t initchksum,uint8_t *ptr,int len); + +/* ********************************************************************* + * UDP Layer + ********************************************************************* */ + +ebuf_t *udp_alloc(void); +void udp_free(ebuf_t *buf); + +int udp_socket(uint16_t port); +int udp_bind(int portnum,uint16_t port); +int udp_connect(int portnum,uint16_t port); +void udp_close(int portnum); +int udp_send(int portnum,ebuf_t *buf,uint8_t *dest); +ebuf_t *udp_recv_with_timeout(int portnum,int seconds); +ebuf_t *udp_recv(int portnum); + +/* ********************************************************************* + * TCP Layer + ********************************************************************* */ + +#if CFG_TCP +#ifndef TCPFLG_NODELAY /* XXX should be kept in sync with net_tcp.h */ +#define TCPFLG_NODELAY 1 /* disable nagle */ +#define TCPFLG_NBIO 2 /* non-blocking I/O */ + +#define TCPSTATUS_NOTCONN 0 +#define TCPSTATUS_CONNECTING 1 +#define TCPSTATUS_CONNECTED 2 +#endif +int tcp_socket(void); +void tcp_destroy(int portnum); +int tcp_connect(int s,uint8_t *dest,uint16_t port); +int tcp_close(int s); +int tcp_send(int s,uint8_t *buf,int len); +int tcp_recv(int s,uint8_t *buf,int len); +int tcp_bind(int s,uint16_t port); +int tcp_peeraddr(int s,uint8_t *addr,uint16_t *port); +int tcp_listen(int s,uint16_t port); +int tcp_status(int s,unsigned int *connflag,int *rxready,int *rxeof); +int tcp_debug(int s,int arg); +int tcp_setflags(int s,unsigned int flags); +int tcp_getflags(int s,unsigned int *flags); +#endif + +/* ********************************************************************* + * ARP Layer + ********************************************************************* */ + +uint8_t *arp_lookup(uint8_t *destip); +void arp_add(uint8_t *destip,uint8_t *desthw); +int arp_enumerate(int entrynum,uint8_t *ipaddr,uint8_t *hwaddr); +int arp_delete(uint8_t *ipaddr); + +/* ********************************************************************* + * Network Configuration + ********************************************************************* */ + +#ifndef NET_IPADDR +#define NET_IPADDR 0 +#define NET_NETMASK 1 +#define NET_GATEWAY 2 +#define NET_NAMESERVER 3 +#define NET_HWADDR 4 +#define NET_DOMAIN 5 +#define NET_HOSTNAME 6 +#define NET_SPEED 7 +#define NET_LOOPBACK 8 +#endif +#define NET_DEVNAME 10 + +uint8_t *net_getparam(int param); +int net_setparam(int param,uint8_t *ptr); +int net_init(char *devname); +void net_uninit(void); +void net_setnetvars(void); + +/* ********************************************************************* + * DNS + ********************************************************************* */ + +int dns_lookup(char *hostname,uint8_t *ipaddr); + +/* ********************************************************************* + * ICMP + ********************************************************************* */ + +int icmp_ping(uint8_t *dest,int seq,int len); + diff --git a/cfe/cfe/net/net_arp.c b/cfe/cfe/net/net_arp.c new file mode 100755 index 0000000..1c12457 --- /dev/null +++ b/cfe/cfe/net/net_arp.c @@ -0,0 +1,865 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * Address Resolution Protocol File: net_arp.c + * + * This module implements RFC826, the Address Resolution Protocol. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + + +#include "lib_types.h" +#include "lib_string.h" +#include "lib_queue.h" +#include "lib_malloc.h" +#include "lib_printf.h" + +#include "net_ebuf.h" +#include "net_ether.h" + +#include "net_ip.h" +#include "net_ip_internal.h" + + +/* ********************************************************************* + * Prototypes + ********************************************************************* */ + +static int arp_tx_query(ip_info_t *ipi,uint8_t *destaddr); +static void arp_delete(arpentry_t *ae); +static void arp_tx_waiting(arpentry_t *ae); +static arpentry_t *arp_new_entry(ip_info_t *ipi); +static arpentry_t *arp_find_entry(ip_info_t *ipi,uint8_t *ipaddr); +static int arp_rx_query(ip_info_t *ipi,uint8_t *srcaddr, + uint8_t *targethw,uint8_t *targetip); +static int arp_rx_response(ip_info_t *ipi,uint8_t *senderhw, + uint8_t *senderip); +static int arp_rx_callback(ebuf_t *buf,void *ref); + + +/* ********************************************************************* + * arp_tx_query(ipi,destaddr) + * + * Transmit an ARP QUERY message. ARP QUERY messages are sent + * to the Ethernet broadcast address. + * + * Input parameters: + * ipi - IP information + * destaddr - IP address to query + * + * Return value: + * 0 - success + * <0 - failure + ********************************************************************* */ + +static int arp_tx_query(ip_info_t *ipi,uint8_t *destaddr) +{ + ebuf_t *buf; + uint8_t hwaddr[ENET_ADDR_LEN]; + + /* + * Get a buffer. + */ + + buf = eth_alloc(ipi->eth_info,ipi->arp_port); + if (!buf) return -1; + + /* + * fill in the fields + */ + + ebuf_append_u16_be(buf,ARP_HWADDRSPACE_ETHERNET); + ebuf_append_u16_be(buf,PROTOSPACE_IP); + ebuf_append_u8(buf,ENET_ADDR_LEN); + ebuf_append_u8(buf,IP_ADDR_LEN); + ebuf_append_u16_be(buf,ARP_OPCODE_REQUEST); + ebuf_append_bytes(buf,ipi->arp_hwaddr,ENET_ADDR_LEN); + ebuf_append_bytes(buf,ipi->net_info.ip_addr,IP_ADDR_LEN); + memset(hwaddr,0,ENET_ADDR_LEN); + ebuf_append_bytes(buf,hwaddr,ENET_ADDR_LEN); + ebuf_append_bytes(buf,destaddr,IP_ADDR_LEN); + + /* + * Transmit the packet + */ + + eth_send(buf,(uint8_t *)eth_broadcast); + eth_free(buf); + + return 0; +} + + +/* ********************************************************************* + * arp_delete(ae) + * + * Delete an ARP entry. The usual reason for calling this routine + * is to reclaim unused ARP entries, but an ARP entry may be + * manually deleted as well. + * + * Input parameters: + * ae - arp entry + * + * Return value: + * nothing + ********************************************************************* */ + +static void arp_delete(arpentry_t *ae) +{ + ebuf_t *buf; + + /* + * Free any buffers associated with the ARP entry. + */ + + while ((buf = (ebuf_t *) q_deqnext(&(ae->ae_txqueue)))) { + eth_free(buf); + } + + /* + * Reset the important fields + */ + + ae->ae_timer = 0; + ae->ae_usage = 0; + ae->ae_retries = 0; + ae->ae_state = ae_unused; +} + + +/* ********************************************************************* + * _arp_add(ipi,destip,desthw) + * + * Add a new ARP entry to the ARP table [internal routine]. If + * there is no room in the table, some other entry is kicked + * out. + * + * Input parameters: + * ipi - IP information + * destip - target IP address + * desthw - target hardware address + * + * Return value: + * nothing + ********************************************************************* */ + +void _arp_add(ip_info_t *ipi,uint8_t *destip,uint8_t *desthw) +{ + arpentry_t *ae; + + ae = arp_find_entry(ipi,desthw); + if (!ae) { + ae = arp_new_entry(ipi); + } + + memcpy(ae->ae_ipaddr,destip,IP_ADDR_LEN); + memcpy(ae->ae_ethaddr,desthw,ENET_ADDR_LEN); + + ae->ae_retries = 0; + ae->ae_timer = 0; /* keep forever */ + ae->ae_permanent = TRUE; + ae->ae_state = ae_established; + + arp_tx_waiting(ae); +} + + +/* ********************************************************************* + * _arp_lookup(ipi,destip) + * + * Look up an ARP entry [internal routine]. Given an IP address, + * return the hardware address to send the packets to, or + * NULL if no ARP entry exists. + * + * Input parameters: + * ipi - IP information + * destip - destination IP address + * + * Return value: + * pointer to Ethernet hardware address, or NULL if not found + ********************************************************************* */ + +uint8_t *_arp_lookup(ip_info_t *ipi,uint8_t *destip) +{ + arpentry_t *ae; + + ae = arp_find_entry(ipi,destip); + if (ae == NULL) return NULL; + + return ae->ae_ethaddr; +} + +/* ********************************************************************* + * _arp_lookup_and_send(ipi,buf,dest) + * + * Transmit a packet [internal routine]. This routine is called + * by the IP layer when it wants to send a packet. We look + * through the ARP table to find a suitable destination host and + * transmit the packet. If there is no ARP entry, an ARP request + * is transmitted and the packet is saved on a queue for when + * the address is resolved. + * + * Input parameters: + * ipi - IP information + * buf - ebuf to transmit + * dest - destination IP address + * + * Return value: + * 0 if ok + * <0 if error + ********************************************************************* */ + +int _arp_lookup_and_send(ip_info_t *ipi,ebuf_t *buf,uint8_t *dest) +{ + arpentry_t *ae; + + ae = arp_find_entry(ipi,dest); + + if (ae == NULL) { + /* + * No ARP entry yet, create one and send the query + */ + ae = arp_new_entry(ipi); + memcpy(ae->ae_ipaddr,dest,IP_ADDR_LEN); + q_enqueue(&(ae->ae_txqueue),(queue_t *) buf); + ae->ae_retries = ARP_QUERY_RETRIES; + ae->ae_timer = ARP_QUERY_TIMER; + ae->ae_state = ae_arping; + arp_tx_query(ipi,ae->ae_ipaddr); + } + else { + /* + * have an ARP entry. If established, just send the + * packet now. Otherwise, queue on arp queue if there's room. + */ + if (ae->ae_state == ae_established) { + ae->ae_usage++; + if (!ae->ae_permanent) { + ae->ae_timer = ARP_KEEP_TIMER; + } + eth_send(buf,ae->ae_ethaddr); + eth_free(buf); + } + else { + if (q_count(&(ae->ae_txqueue)) < ARP_TXWAIT_MAX) { + q_enqueue(&(ae->ae_txqueue),(queue_t *) buf); + } + else { + /* no room, silently drop */ + eth_free(buf); + } + } + } + + return 0; +} + +/* ********************************************************************* + * arp_tx_waiting(ae) + * + * Transmit all pending packets on the specified ARP entry's + * queue. Packets get queued to an ARP entry when the address + * has not completed resolution. Once resolved, this routine + * is called to flush the packets out. + * + * Input parameters: + * ae - arp entry + * + * Return value: + * nothing + ********************************************************************* */ + +static void arp_tx_waiting(arpentry_t *ae) +{ + ebuf_t *buf; + + while ((buf = (ebuf_t *) q_deqnext(&(ae->ae_txqueue)))) { + eth_send(buf,ae->ae_ethaddr); + eth_free(buf); + } +} + + +/* ********************************************************************* + * arp_new_entry(ipi) + * + * Create a new ARP entry, deleting an active entry if necessary. + * + * Input parameters: + * ipi - IP information + * + * Return value: + * arp entry pointer + ********************************************************************* */ + +static arpentry_t *arp_new_entry(ip_info_t *ipi) +{ + arpentry_t *ae; + arpentry_t *victim = NULL; + int idx; + int minusage = 0x7FFFFFFF; + + /* + * First scan the table and find an empty entry. + */ + + ae = ipi->arp_table; + for (idx = 0; idx < ARP_TABLE_SIZE; idx++,ae++) { + if (ae->ae_state == ae_unused) { + return ae; + } + } + + /* + * If all entries are in use, pick the one with the + * lowest usage count. This isn't very scientific, + * and perhaps should use a timer of some sort. + */ + + ae = ipi->arp_table; + for (idx = 0; idx < ARP_TABLE_SIZE; idx++,ae++) { + if (ae->ae_usage < minusage) { + victim = ae; + minusage = ae->ae_usage; + } + } + + /* + * In the highly unlikely event that all entries have + * overflow values in their usage counters, just take the + * first table entry. + */ + + if (victim == NULL) victim = ipi->arp_table; + + /* + * Clear out the old entry and use it. + */ + + arp_delete(victim); + + return victim; +} + +/* ********************************************************************* + * arp_find_entry(ipi,ipaddr) + * + * Find an ARP entry in the table. Given an IP address, this + * routine locates the corresponding ARP table entry. We also + * reset the expiration timer for the ARP entry, to prevent + * it from from being deleted. + * + * Input parameters: + * ipi - IP info + * ipaddr - IP address we're looking for + * + * Return value: + * arp entry pointer, or NULL if not found + ********************************************************************* */ + +static arpentry_t *arp_find_entry(ip_info_t *ipi,uint8_t *ipaddr) +{ + arpentry_t *ae; + int idx; + + ae = ipi->arp_table; + + for (idx = 0; idx < ARP_TABLE_SIZE; idx++,ae++) { + if (ae->ae_state != ae_unused) { + if (memcmp(ae->ae_ipaddr,ipaddr,IP_ADDR_LEN) == 0) { + if (ae->ae_state == ae_established) + ae->ae_timer = ARP_KEEP_TIMER; + return ae; + } + } + } + return NULL; +} + + +/* ********************************************************************* + * arp_rx_query(ipi,srcaddr,targethw,targetip) + * + * Process a received ARP QUERY message. When we get an ARP, + * transmit a reply to the sender. + * + * Input parameters: + * ipi - IP information + * srcaddr - source IP address + * targethw - target hardware address + * targetip - target IP address (should be our address) + * + * Input parameters: + * 0 if ok + * else <0 = error + ********************************************************************* */ + +static int arp_rx_query(ip_info_t *ipi,uint8_t *srcaddr, + uint8_t *targethw,uint8_t *targetip) +{ + ebuf_t *txbuf; + + /* + * Allocate a packet and form the reply + */ + + txbuf = eth_alloc(ipi->eth_info,ipi->arp_port); + if (!txbuf) return -1; + + ebuf_append_u16_be(txbuf,ARP_HWADDRSPACE_ETHERNET); + ebuf_append_u16_be(txbuf,PROTOSPACE_IP); + ebuf_append_u8(txbuf,ENET_ADDR_LEN); + ebuf_append_u8(txbuf,IP_ADDR_LEN); + ebuf_append_u16_be(txbuf,ARP_OPCODE_REPLY); + + ebuf_append_bytes(txbuf,ipi->arp_hwaddr,ENET_ADDR_LEN); + ebuf_append_bytes(txbuf,ipi->net_info.ip_addr,IP_ADDR_LEN); + + ebuf_append_bytes(txbuf,targethw,ENET_ADDR_LEN); + ebuf_append_bytes(txbuf,targetip,IP_ADDR_LEN); + + eth_send(txbuf,srcaddr); + eth_free(txbuf); + + return 0; +} + +/* ********************************************************************* + * arp_rx_response(ipi,senderhw,senderip) + * + * Process a received ARP RESPONSE packet. This packet contains + * the hardware address of some host we were querying. Fill + * in the rest of the entries in the ARP table and + * transmit any pending packets. + * + * Input parameters: + * ipi - IP information + * senderhw - sender's hardware address + * senderip - sender's IP address + * + * Return value: + * 0 + ********************************************************************* */ + +static int arp_rx_response(ip_info_t *ipi,uint8_t *senderhw,uint8_t *senderip) +{ + int idx; + arpentry_t *ae; + + ae = ipi->arp_table; + + for (idx = 0; idx < ARP_TABLE_SIZE; idx++,ae++) { + if (ae->ae_state != ae_unused) { + if (memcmp(ae->ae_ipaddr,senderip,IP_ADDR_LEN) == 0) { + memcpy(ae->ae_ethaddr,senderhw,ENET_ADDR_LEN); + ae->ae_state = ae_established; + ae->ae_timer = ARP_KEEP_TIMER; + ae->ae_retries = 0; + ae->ae_permanent = FALSE; + arp_tx_waiting(ae); + } + } + } + + return 0; +} + +/* ********************************************************************* + * arp_rx_callback(buf,ref) + * + * Callback for ARP protocol packets. This routine is called + * by the datalink layer when we receive an ARP packet. Parse + * the packet and call any packet-specific processing routines + * + * Input parameters: + * buf - ebuf that we received + * ref - reference data when we opened the port. This is + * our IP information structure + * + * Return value: + * ETH_DROP or ETH_KEEP. + ********************************************************************* */ + +static int arp_rx_callback(ebuf_t *buf,void *ref) +{ + ip_info_t *ipi = ref; + uint16_t t16; + uint8_t t8; + uint16_t opcode; + uint8_t senderip[IP_ADDR_LEN]; + uint8_t senderhw[ENET_ADDR_LEN]; + uint8_t targetip[IP_ADDR_LEN]; + uint8_t targethw[ENET_ADDR_LEN]; + + /* + * ARP packets have to be at least 28 bytes + */ + + if (ebuf_length(buf) < 28) goto drop; + + /* + * We only do the Ethernet hardware space + */ + + ebuf_get_u16_be(buf,t16); + if (t16 != ARP_HWADDRSPACE_ETHERNET) goto drop; + + /* + * We only do the IP protocol space + */ + + ebuf_get_u16_be(buf,t16); + if (t16 != PROTOSPACE_IP) goto drop; + + /* + * The IP and Ethernet address lengths had better be right. + */ + + ebuf_get_u8(buf,t8); + if (t8 != ENET_ADDR_LEN) goto drop; + + ebuf_get_u8(buf,t8); + if (t8 != IP_ADDR_LEN) goto drop; + + /* + * Get the opcode and other fields. + */ + + ebuf_get_u16_be(buf,opcode); + + ebuf_get_bytes(buf,senderhw,ENET_ADDR_LEN); + ebuf_get_bytes(buf,senderip,IP_ADDR_LEN); + ebuf_get_bytes(buf,targethw,ENET_ADDR_LEN); + ebuf_get_bytes(buf,targetip,IP_ADDR_LEN); + + /* + * If it's not for us, just drop it. + */ + + if (memcmp(targetip,ipi->net_info.ip_addr,IP_ADDR_LEN) != 0) goto drop; + + /* + * Dispatch to an appropriate routine. + */ + + switch (opcode) { + case ARP_OPCODE_REQUEST: + arp_rx_query(ipi,ebuf_srcaddr(buf),senderhw,senderip); + break; + case ARP_OPCODE_REPLY: + arp_rx_response(ipi,senderhw,senderip); + break; + } + +drop: + return ETH_DROP; +} + +/* ********************************************************************* + * _arp_timer_tick(ipi) + * + * ARP timer processing [internal routine]. This routine + * counts down timer ticks in the ARP entries, causing retransmits + * or ARP entry expirations to happen. + * + * Input parameters: + * ipi - IP information + * + * Return value: + * nothing + ********************************************************************* */ + +void _arp_timer_tick(ip_info_t *ipi) +{ + int idx; + arpentry_t *ae; + + ae = ipi->arp_table; + + /* + * Walk through the ARP table. + */ + + for (idx = 0; idx < ARP_TABLE_SIZE; idx++,ae++) { + + switch (ae->ae_state) { + case ae_unused: + /* + * Unused entry. Do nothing. + */ + break; + + case ae_arping: + /* + * Entry is arping. Count down the timer, and retransmit + * the ARP message. + */ + ae->ae_timer--; + if (ae->ae_timer <= 0) { + if (ae->ae_retries == 0) { + arp_delete(ae); + } + else { + ae->ae_retries--; + ae->ae_timer = ARP_QUERY_TIMER; + arp_tx_query(ipi,ae->ae_ipaddr); + } + } + break; + + case ae_established: + /* + * Established entry. Count down the timer and + * delete the ARP entry. If the timer is zero + * already, it's a permanent ARP entry. + */ + if (ae->ae_timer == 0) break; + ae->ae_timer--; + if (ae->ae_timer == 0) arp_delete(ae); + break; + } + } + +} + +/* ********************************************************************* + * _arp_send_gratuitous(ipi) + * + * Transmit the "gratuitous arp" (an ARP for our own IP address). + * This is done customarily when an interface is initialized. + * + * Input parameters: + * ipi - IP information + * + * Return value: + * nothing + ********************************************************************* */ + +void _arp_send_gratuitous(ip_info_t *ipi) +{ + if (!ip_addriszero(ipi->net_info.ip_addr)) { + arp_tx_query(ipi,ipi->net_info.ip_addr); + } +} + +/* ********************************************************************* + * _arp_init(ipi) + * + * Initialize the ARP layer [internal routine] + * + * Input parameters: + * ipi - IP information + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int _arp_init(ip_info_t *ipi) +{ + int8_t arpproto[2]; + int idx; + arpentry_t *ae; + + /* + * Allocate space for the ARP table + */ + + ipi->arp_table = KMALLOC(ARP_TABLE_SIZE*sizeof(arpentry_t),0); + + if (ipi->arp_table == NULL) return NULL; + + /* + * Initialize the ARP table. + */ + + ae = ipi->arp_table; + for (idx = 0; idx < ARP_TABLE_SIZE; idx++) { + ae->ae_state = ae_unused; + ae->ae_timer = 0; + ae->ae_usage = 0; + ae->ae_retries = 0; + ae->ae_permanent = 0; + q_init(&(ae->ae_txqueue)); + ae++; + } + + /* + * Open the Ethernet portal for ARP packets + */ + + arpproto[0] = (PROTOSPACE_ARP >> 8) & 0xFF; + arpproto[1] = (PROTOSPACE_ARP & 0xFF); + ipi->arp_port = eth_open(ipi->eth_info,ETH_PTYPE_DIX,arpproto,arp_rx_callback,ipi); + + if (ipi->arp_port < 0) { + KFREE(ipi->arp_table); + ipi->arp_table = NULL; + return -1; + } + + /* + * Remember our hardware address + */ + + eth_gethwaddr(ipi->eth_info,ipi->arp_hwaddr); + + /* + * Send a query for ourselves if our IP address is set + */ + + _arp_send_gratuitous(ipi); + + return 0; + +} + + +/* ********************************************************************* + * _arp_uninit(ipi) + * + * Uninitialize the ARP interface. This is called when the + * network module is shut down. + * + * Input parameters: + * ipi - IP information + * + * Return value: + * nothing + ********************************************************************* */ + +void _arp_uninit(ip_info_t *ipi) +{ + int idx; + arpentry_t *ae; + + /* + * Close the Ethernet portal + */ + + eth_close(ipi->eth_info,ipi->arp_port); + + /* + * Clear out the ARP Table. + */ + + ae = ipi->arp_table; + for (idx = 0; idx < ARP_TABLE_SIZE; idx++) { + if (ae->ae_state != ae_unused) arp_delete(ae); + ae++; + } + + /* + * Free up the memory. + */ + + KFREE(ipi->arp_table); + ipi->arp_table = NULL; + ipi->arp_port = -1; +} + + +/* ********************************************************************* + * _arp_enumerate(ipi,entrynum,ipaddr,hwaddr) + * + * Enumerate the ARP table. This is used by user-interface + * routines to display the current contents of the ARP table. + * + * Input parameters: + * ipi - IP information + * entrynum - entry index + * ipaddr - buffer to copy entry's IP address to + * hwaddr - buffer to copy entry's hardware address to + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int _arp_enumerate(ip_info_t *ipi,int entrynum,uint8_t *ipaddr,uint8_t *hwaddr) +{ + arpentry_t *ae; + int idx; + + ae = ipi->arp_table; + for (idx = 0; idx < ARP_TABLE_SIZE; idx++) { + if (ae->ae_state != ae_unused) { + if (entrynum == 0) { + memcpy(ipaddr,ae->ae_ipaddr,IP_ADDR_LEN); + memcpy(hwaddr,ae->ae_ethaddr,ENET_ADDR_LEN); + return 0; + } + entrynum--; + } + ae++; + } + + return -1; +} + + +/* ********************************************************************* + * _arp_delete(ipi,ipaddr) + * + * Delete an ARP entry. This routine takes an IP address, looks + * up its ARP table entry, and removes it from the table. + * + * Input parameters: + * ipi - IP information + * ipaddr - IP address whose entry to delete + * + * Return value: + * 0 if entry was deleted + * <0 if entry was not found + ********************************************************************* */ + +int _arp_delete(ip_info_t *ipi,uint8_t *ipaddr) +{ + arpentry_t *ae; + + ae = arp_find_entry(ipi,ipaddr); + + if (ae) { + arp_delete(ae); + return 0; + } + return -1; +} diff --git a/cfe/cfe/net/net_dhcp.c b/cfe/cfe/net/net_dhcp.c new file mode 100644 index 0000000..84a39e6 --- /dev/null +++ b/cfe/cfe/net/net_dhcp.c @@ -0,0 +1,869 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * DHCP Client File: net_dhcp.c + * + * This module contains a DHCP client. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + +#include "lib_types.h" +#include "lib_string.h" +#include "lib_queue.h" +#include "lib_malloc.h" +#include "lib_printf.h" + +#include "env_subr.h" + +#include "cfe_iocb.h" +#include "cfe_devfuncs.h" +#include "cfe_ioctl.h" +#include "cfe_timer.h" +#include "cfe_error.h" + +#include "net_ebuf.h" +#include "net_ether.h" + +#include "cfe.h" + +#include "net_api.h" + +/* ********************************************************************* + * Constants + ********************************************************************* */ + + +#define UDP_PORT_BOOTPS 67 +#define UDP_PORT_BOOTPC 68 + +#define DHCP_REQ_TIMEOUT (1*CFE_HZ) +#define DHCP_NUM_RETRIES 8 + +#define DHCP_OP_BOOTREQUEST 1 +#define DHCP_OP_BOOTREPLY 2 + +#define DHCP_HWTYPE_ETHERNET 1 + +#define DHCP_TAG_FUNCTION 53 +#define DHCP_FUNCTION_DISCOVER 1 +#define DHCP_FUNCTION_OFFER 2 +#define DHCP_FUNCTION_REQUEST 3 +#define DHCP_FUNCTION_ACK 5 + +#define DHCP_TAG_NETMASK 1 +#define DHCP_TAG_DOMAINNAME 0x0F +#define DHCP_TAG_GATEWAY 0x03 +#define DHCP_TAG_NAMESERVER 0x06 +#define DHCP_TAG_SWAPSERVER 0x10 +#define DHCP_TAG_ROOTPATH 0x11 +#define DHCP_TAG_EXTENSIONS 0x12 +#define DHCP_TAG_SERVERIDENT 54 +#define DHCP_TAG_PARAMLIST 55 +#define DHCP_TAG_CLIENTID 61 +#define DHCP_TAG_CLASSID 60 +#define DHCP_TAG_REQADDR 50 +#define DHCP_TAG_LEASE_TIME 51 +#define DHCP_TAG_SCRIPT 130 /* CFE extended option */ +#define DHCP_TAG_OPTIONS 131 /* CFE extended option */ + +#define DHCP_TAG_END 0xFF + +#define DHCP_MAGIC_NUMBER 0x63825363 + +/*#define _DEBUG_*/ + + +/* ********************************************************************* + * dhcp_set_envvars(reply) + * + * Using the supplied DHCP reply data, set environment variables + * to contain data from the reply. + * + * Input parameters: + * reply - dhcp reply + * + * Return value: + * nothing + ********************************************************************* */ + +void dhcp_set_envvars(dhcpreply_t *reply) +{ + char buffer[50]; + + env_delenv("BOOT_SERVER"); + env_delenv("BOOT_FILE"); + env_delenv("BOOT_SCRIPT"); + env_delenv("BOOT_OPTIONS"); + + if (reply->dr_bootserver[0] | reply->dr_bootserver[1] | + reply->dr_bootserver[2] | reply->dr_bootserver[3]) { + sprintf(buffer,"%I",reply->dr_bootserver); + env_setenv("BOOT_SERVER",buffer,ENV_FLG_BUILTIN); + } + + if (reply->dr_bootfile && reply->dr_bootfile[0]) { + env_setenv("BOOT_FILE",reply->dr_bootfile,ENV_FLG_BUILTIN); + } + + if (reply->dr_script && reply->dr_script[0]) { + env_setenv("BOOT_SCRIPT",reply->dr_script,ENV_FLG_BUILTIN); + } + + if (reply->dr_options && reply->dr_options[0]) { + env_setenv("BOOT_OPTIONS",reply->dr_options,ENV_FLG_BUILTIN); + } +} + +/* ********************************************************************* + * dhcp_dumptag(tag,len,buf) + * + * Dump out information from a DHCP tag + * + * Input parameters: + * tag - tag ID + * len - length of data + * buf - data + * + * Return value: + * nothing + ********************************************************************* */ + +#ifdef _DEBUG_ +static void dhcp_dumptag(uint8_t tag,uint8_t len,uint8_t *buf) +{ + unsigned int idx; + + xprintf("DHCP: "); + + switch (tag) { + case DHCP_TAG_FUNCTION: + xprintf("DHCP Function: %d\n",buf[0]); + break; + case DHCP_TAG_LEASE_TIME: + idx = (((unsigned int) buf[0]) << 24) | + (((unsigned int) buf[1]) << 16) | + (((unsigned int) buf[2]) << 8) | + (((unsigned int) buf[3]) << 0); + xprintf("Lease Time: %d seconds\n",idx); + break; + case DHCP_TAG_SERVERIDENT: + xprintf("DHCP Server ID: %d.%d.%d.%d\n", + buf[0],buf[1],buf[2],buf[3]); + break; + case DHCP_TAG_NETMASK: + xprintf("Netmask: %d.%d.%d.%d\n",buf[0],buf[1],buf[2],buf[3]); + break; + case DHCP_TAG_DOMAINNAME: + buf[len] = 0; + xprintf("Domain: %s\n",buf); + break; + case DHCP_TAG_GATEWAY: + xprintf("Gateway: %d.%d.%d.%d\n",buf[0],buf[1],buf[2],buf[3]); + break; + case DHCP_TAG_NAMESERVER: + xprintf("Nameserver: %d.%d.%d.%d\n",buf[0],buf[1],buf[2],buf[3]); + break; + case DHCP_TAG_SWAPSERVER: + buf[len] = 0; + xprintf("Swapserver: %s\n",buf); + break; + case DHCP_TAG_ROOTPATH: + buf[len] = 0; + xprintf("Rootpath: %s\n",buf); + break; + case DHCP_TAG_SCRIPT: + buf[len] = 0; + xprintf("CFE Script: %s\n",buf); + break; + case DHCP_TAG_OPTIONS: + buf[len] = 0; + xprintf("CFE Boot Options: %s\n",buf); + break; + default: + xprintf("Tag %d len %d [",tag,len); + for (idx = 0; idx < len; idx++) { + if ((buf[idx] >= 32) && (buf[idx] < 127)) xprintf("%c",buf[idx]); + } + xprintf("]\n"); + break; + + } +} +#endif + +/* ********************************************************************* + * dhcp_free_reply(reply) + * + * Free memory associated with a DHCP reply. + * + * Input parameters: + * reply - pointer to DHCP reply + * + * Return value: + * nothing + ********************************************************************* */ + +void dhcp_free_reply(dhcpreply_t *reply) +{ + if (reply->dr_hostname) KFREE(reply->dr_hostname); + if (reply->dr_domainname) KFREE(reply->dr_domainname); + if (reply->dr_bootfile) KFREE(reply->dr_bootfile); + if (reply->dr_rootpath) KFREE(reply->dr_rootpath); + if (reply->dr_swapserver) KFREE(reply->dr_swapserver); + if (reply->dr_script) KFREE(reply->dr_script); + if (reply->dr_options) KFREE(reply->dr_options); + KFREE(reply); +} + +/* ********************************************************************* + * dhcp_build_discover() + * + * Build a DHCP DISCOVER packet + * + * Input parameters: + * hwaddr - our hardware address + * idptr - pointer to int to receive the DHCP packet ID + * serveraddr - pointer to server address (REQUEST) or + * NULL (DISCOVER) + * ebufptr - receives pointer to ebuf + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +static int dhcp_build_discover(uint8_t *hwaddr, + uint32_t id, + ebuf_t **ebufptr) +{ + uint8_t ipaddr[IP_ADDR_LEN]; + ebuf_t *buf; + uint8_t junk[128]; + + /* + * Get a buffer and fill it in. + */ + + ipaddr[0] = 0; ipaddr[1] = 0; ipaddr[2] = 0; ipaddr[3] = 0; + memset(junk,0,sizeof(junk)); + + buf = udp_alloc(); + + if (buf == NULL) { + return CFE_ERR_NOMEM; + } + + memset(buf->eb_ptr,0,548); + + ebuf_append_u8(buf,DHCP_OP_BOOTREQUEST); + ebuf_append_u8(buf,DHCP_HWTYPE_ETHERNET); + ebuf_append_u8(buf,ENET_ADDR_LEN); + ebuf_append_u8(buf,0); /* hops */ + ebuf_append_u32_be(buf,id); + ebuf_append_u16_be(buf,0); /* sec since boot */ + ebuf_append_u16_be(buf,0); /* flags */ + + ebuf_append_bytes(buf,ipaddr,IP_ADDR_LEN); /* ciaddr */ + ebuf_append_bytes(buf,ipaddr,IP_ADDR_LEN); /* yiaddr */ + ebuf_append_bytes(buf,ipaddr,IP_ADDR_LEN); /* siaddr */ + ebuf_append_bytes(buf,ipaddr,IP_ADDR_LEN); /* giaddr */ + + ebuf_append_bytes(buf,hwaddr,ENET_ADDR_LEN); /* chaddr */ + ebuf_append_bytes(buf,junk,10); /* rest of chaddr */ + ebuf_append_bytes(buf,junk,64); /* sname */ + ebuf_append_bytes(buf,junk,128); /* file */ + + ebuf_append_u32_be(buf,DHCP_MAGIC_NUMBER); + + ebuf_append_u8(buf,DHCP_TAG_FUNCTION); /* function code */ + ebuf_append_u8(buf,1); + ebuf_append_u8(buf,DHCP_FUNCTION_DISCOVER); + + ebuf_append_u8(buf,DHCP_TAG_PARAMLIST); + ebuf_append_u8(buf,8); /* count of tags that follow */ + ebuf_append_u8(buf,DHCP_TAG_NETMASK); + ebuf_append_u8(buf,DHCP_TAG_DOMAINNAME); + ebuf_append_u8(buf,DHCP_TAG_GATEWAY); + ebuf_append_u8(buf,DHCP_TAG_NAMESERVER); + ebuf_append_u8(buf,DHCP_TAG_SWAPSERVER); + ebuf_append_u8(buf,DHCP_TAG_ROOTPATH); + ebuf_append_u8(buf,DHCP_TAG_SCRIPT); + ebuf_append_u8(buf,DHCP_TAG_OPTIONS); + + + ebuf_append_u8(buf,DHCP_TAG_END); /* terminator */ + + /* + * Return the packet + */ + + *ebufptr = buf; + + return 0; +} + +/* ********************************************************************* + * dhcp_build_request() + * + * Build a DHCP DISCOVER or REQUEST packet + * + * Input parameters: + * hwaddr - our hardware address + * idptr - pointer to int to receive the DHCP packet ID + * serveraddr - pointer to server address (REQUEST) or + * NULL (DISCOVER) + * ebufptr - receives pointer to ebuf + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +static int dhcp_build_request(uint8_t *hwaddr, + uint32_t id, + uint8_t *serveraddr, + uint8_t *reqip, + ebuf_t **ebufptr) +{ + uint8_t ipaddr[IP_ADDR_LEN]; + ebuf_t *buf; + uint8_t junk[128]; + + /* + * Get a buffer and fill it in. + */ + + ipaddr[0] = 0; ipaddr[1] = 0; ipaddr[2] = 0; ipaddr[3] = 0; + memset(junk,0,sizeof(junk)); + + buf = udp_alloc(); + + if (buf == NULL) { + return CFE_ERR_NOMEM; + } + + memset(buf->eb_ptr,0,548); + + ebuf_append_u8(buf,DHCP_OP_BOOTREQUEST); + ebuf_append_u8(buf,DHCP_HWTYPE_ETHERNET); + ebuf_append_u8(buf,ENET_ADDR_LEN); + ebuf_append_u8(buf,0); /* hops */ + ebuf_append_u32_be(buf,id); + ebuf_append_u16_be(buf,0); /* sec since boot */ + ebuf_append_u16_be(buf,0); /* flags */ + + ebuf_append_bytes(buf,ipaddr,IP_ADDR_LEN); /* ciaddr */ + ebuf_append_bytes(buf,ipaddr,IP_ADDR_LEN); /* yiaddr */ + ebuf_append_bytes(buf,ipaddr,IP_ADDR_LEN); /* siaddr */ + ebuf_append_bytes(buf,ipaddr,IP_ADDR_LEN); /* giaddr */ + + ebuf_append_bytes(buf,hwaddr,ENET_ADDR_LEN); /* chaddr */ + ebuf_append_bytes(buf,junk,10); /* rest of chaddr */ + + ebuf_append_bytes(buf,junk,64); /* sname */ + + ebuf_append_bytes(buf,junk,128); /* file */ + + ebuf_append_u32_be(buf,DHCP_MAGIC_NUMBER); + + ebuf_append_u8(buf,DHCP_TAG_FUNCTION); /* function code */ + ebuf_append_u8(buf,1); + + ebuf_append_u8(buf,DHCP_FUNCTION_REQUEST); + + ebuf_append_u8(buf,DHCP_TAG_REQADDR); + ebuf_append_u8(buf,IP_ADDR_LEN); + ebuf_append_bytes(buf,reqip,IP_ADDR_LEN); + + ebuf_append_u8(buf,DHCP_TAG_SERVERIDENT); /* server ID */ + ebuf_append_u8(buf,IP_ADDR_LEN); + ebuf_append_bytes(buf,serveraddr,IP_ADDR_LEN); + + ebuf_append_u8(buf,DHCP_TAG_CLIENTID); /* client ID */ + ebuf_append_u8(buf,7); + ebuf_append_u8(buf,1); + ebuf_append_bytes(buf,hwaddr,ENET_ADDR_LEN); + + ebuf_append_u8(buf,DHCP_TAG_PARAMLIST); + ebuf_append_u8(buf,8); /* count of tags that follow */ + ebuf_append_u8(buf,DHCP_TAG_NETMASK); + ebuf_append_u8(buf,DHCP_TAG_DOMAINNAME); + ebuf_append_u8(buf,DHCP_TAG_GATEWAY); + ebuf_append_u8(buf,DHCP_TAG_NAMESERVER); + ebuf_append_u8(buf,DHCP_TAG_SWAPSERVER); + ebuf_append_u8(buf,DHCP_TAG_ROOTPATH); + ebuf_append_u8(buf,DHCP_TAG_SCRIPT); + ebuf_append_u8(buf,DHCP_TAG_OPTIONS); + + + ebuf_append_u8(buf,DHCP_TAG_END); /* terminator */ + + /* + * Return the packet + */ + + *ebufptr = buf; + + return 0; +} + + +/* ********************************************************************* + * dhcp_wait_reply(s,id,reply,serveraddr) + * + * Wait for a reply from the DHCP server + * + * Input parameters: + * s - socket + * id - ID of request we sent + * reply - structure to store results in + * expfcode - expected DHCP_FUNCTION tag value + * + * Return value: + * 0 if ok (reply found) + * else error + ********************************************************************* */ + +static int dhcp_wait_reply(int s,uint32_t id,dhcpreply_t *reply,int expfcode) +{ + uint32_t tmpd; + uint8_t tmpb; + int64_t timer; + int nres = 0; + uint8_t ciaddr[IP_ADDR_LEN]; + uint8_t yiaddr[IP_ADDR_LEN]; + uint8_t siaddr[IP_ADDR_LEN]; + uint8_t giaddr[IP_ADDR_LEN]; + uint8_t junk[128]; + char *hostname; + char *bootfile; + ebuf_t *buf; + int fcode = -1; + + /* + * Set a timer for the response + */ + + TIMER_SET(timer,DHCP_REQ_TIMEOUT); + + /* + * Start waiting... + */ + + while (!TIMER_EXPIRED(timer)) { + POLL(); + + buf = udp_recv(s); + if (!buf) continue; + + ebuf_get_u8(buf,tmpb); + if (tmpb != DHCP_OP_BOOTREPLY) { + goto drop; + } + + ebuf_get_u8(buf,tmpb); + if (tmpb != DHCP_HWTYPE_ETHERNET) { + goto drop; + } + + ebuf_get_u8(buf,tmpb); + if (tmpb != ENET_ADDR_LEN) { + goto drop; + } + + ebuf_skip(buf,1); /* hops */ + + ebuf_get_u32_be(buf,tmpd); /* check ID */ + if (tmpd != id) { + goto drop; + } + + ebuf_skip(buf,2); /* seconds since boot */ + ebuf_skip(buf,2); /* flags */ + + ebuf_get_bytes(buf,ciaddr,IP_ADDR_LEN); + ebuf_get_bytes(buf,yiaddr,IP_ADDR_LEN); + ebuf_get_bytes(buf,siaddr,IP_ADDR_LEN); + ebuf_get_bytes(buf,giaddr,IP_ADDR_LEN); + + ebuf_skip(buf,16); /* hardware address */ + hostname = ebuf_ptr(buf); + ebuf_skip(buf,64); + bootfile = ebuf_ptr(buf); + + ebuf_skip(buf,128); + +#ifdef _DEBUG_ + xprintf("Client IP: %d.%d.%d.%d\n",ciaddr[0],ciaddr[1],ciaddr[2],ciaddr[3]); + xprintf("Your IP: %d.%d.%d.%d\n",yiaddr[0],yiaddr[1],yiaddr[2],yiaddr[3]); + xprintf("Server IP: %d.%d.%d.%d\n",siaddr[0],siaddr[1],siaddr[2],siaddr[3]); + xprintf("Gateway IP: %d.%d.%d.%d\n",giaddr[0],giaddr[1],giaddr[2],giaddr[3]); + xprintf("hostname: %s\n",hostname); + xprintf("boot file: %s\n",bootfile); +#endif + + memcpy(reply->dr_ipaddr,yiaddr,IP_ADDR_LEN); + memcpy(reply->dr_gateway,giaddr,IP_ADDR_LEN); + memcpy(reply->dr_bootserver,siaddr,IP_ADDR_LEN); + if (*hostname) reply->dr_hostname = strdup(hostname); + if (*bootfile) reply->dr_bootfile = strdup(bootfile); + + /* + * Test for options - look for magic number + */ + + ebuf_get_u32_be(buf,tmpd); + + memcpy(reply->dr_dhcpserver,buf->eb_usrptr,IP_ADDR_LEN); + + if (tmpd == DHCP_MAGIC_NUMBER) { + uint8_t tag; + uint8_t len; + + while (buf->eb_length > 0) { + ebuf_get_u8(buf,tag); + if (tag == DHCP_TAG_END) break; + ebuf_get_u8(buf,len); + ebuf_get_bytes(buf,junk,len); + +#ifdef _DEBUG_ + dhcp_dumptag(tag,len,junk); +#endif + + switch (tag) { + case DHCP_TAG_FUNCTION: + fcode = (uint8_t) junk[0]; + break; + case DHCP_TAG_NETMASK: + memcpy(reply->dr_netmask,junk,IP_ADDR_LEN); + break; + case DHCP_TAG_GATEWAY: + memcpy(reply->dr_gateway,junk,IP_ADDR_LEN); + break; + case DHCP_TAG_NAMESERVER: + memcpy(reply->dr_nameserver,junk,IP_ADDR_LEN); + break; + case DHCP_TAG_DOMAINNAME: + junk[len] = 0; + if (len) reply->dr_domainname = strdup(junk); + break; + case DHCP_TAG_SWAPSERVER: + junk[len] = 0; + if (len) reply->dr_swapserver = strdup(junk); + break; + case DHCP_TAG_SERVERIDENT: + if (len == IP_ADDR_LEN) { + memcpy(reply->dr_dhcpserver,junk,len); + } + break; + case DHCP_TAG_ROOTPATH: + junk[len] = 0; + if (len) reply->dr_rootpath = strdup(junk); + break; + case DHCP_TAG_SCRIPT: + junk[len] = 0; + if (len) reply->dr_script = strdup(junk); + break; + case DHCP_TAG_OPTIONS: + junk[len] = 0; + if (len) reply->dr_options = strdup(junk); + break; + } + } + } + + if (fcode != expfcode) { + goto drop; + } + + udp_free(buf); + nres++; + break; + + drop: + udp_free(buf); + } + + if (nres > 0) return 0; + else return CFE_ERR_TIMEOUT; +} + + +/* ********************************************************************* + * dhcp_do_dhcpdiscover(s,hwaddr,reply) + * + * Request an IP address from the DHCP server. On success, the + * dhcpreply_t structure will be filled in + * + * Input parameters: + * s - udp socket + * hwaddr - our hardware address + * reply - pointer to reply buffer. + * + * Return value: + * 0 if a response was received + * else error code + ********************************************************************* */ + +static int dhcp_do_dhcpdiscover(int s,uint8_t *hwaddr,dhcpreply_t *reply) +{ + ebuf_t *buf; + uint32_t id; + uint8_t ipaddr[IP_ADDR_LEN]; + int res; + + /* + * Packet ID is the current time + */ + + id = (uint32_t)cfe_ticks; + + /* + * Build the DISCOVER request + */ + + res = dhcp_build_discover(hwaddr,id,&buf); + if (res != 0) return res; + + /* + * Send the packet to the IP broadcast (255.255.255.255) + */ + + ipaddr[0] = 0xFF; ipaddr[1] = 0xFF; ipaddr[2] = 0xFF; ipaddr[3] = 0xFF; + udp_send(s,buf,ipaddr); + + /* + * Wait for a reply + */ + + res = dhcp_wait_reply(s,id,reply,DHCP_FUNCTION_OFFER); + + return res; +} + + +/* ********************************************************************* + * dhcp_do_dhcprequest(s,hwaddr,reply,discover_reply) + * + * Request an IP address from the DHCP server. On success, the + * dhcpreply_t structure will be filled in + * + * Input parameters: + * s - udp socket + * hwaddr - our hardware address + * reply - pointer to reply buffer. + * discover_reply - pointer to previously received DISCOVER data + * + * Return value: + * 0 if a response was received + * else error code + ********************************************************************* */ + +static int dhcp_do_dhcprequest(int s,uint8_t *hwaddr, + dhcpreply_t *reply, + dhcpreply_t *discover_reply) +{ + ebuf_t *buf; + uint32_t id; + uint8_t ipaddr[IP_ADDR_LEN]; + int res; + + /* + * Packet ID is the current time + */ + + id = (uint32_t)cfe_ticks; + + /* + * Build the DHCP REQUEST request + */ + + res = dhcp_build_request(hwaddr, + id, + discover_reply->dr_dhcpserver, + discover_reply->dr_ipaddr, + &buf); + + if (res != 0) return res; + + /* + * Send the packet to the IP broadcast (255.255.255.255) + */ + + ipaddr[0] = 0xFF; ipaddr[1] = 0xFF; ipaddr[2] = 0xFF; ipaddr[3] = 0xFF; + udp_send(s,buf,ipaddr); + + /* + * Wait for a reply + */ + + res = dhcp_wait_reply(s,id,reply,DHCP_FUNCTION_ACK); + + return res; +} + + +/* ********************************************************************* + * dhcp_bootrequest(reply) + * + * Request an IP address from the DHCP server. On success, the + * dhcpreply_t structure will be allocated. + * + * Input parameters: + * reply - pointer to pointer to reply. + * + * Return value: + * 0 if no responses received + * >0 for some responses received + * else error code + ********************************************************************* */ + +int dhcp_bootrequest(dhcpreply_t **rep) +{ + uint8_t *hwaddr; + int s; + dhcpreply_t *discover_reply; + dhcpreply_t *request_reply; + int nres = 0; + int retries; + uint32_t id; + + id = (uint32_t) cfe_ticks; + + /* + * Start with empty reply buffers. Since we use a portion of the + * discover reply in the request, we'll keep two of them. + */ + + discover_reply = KMALLOC(sizeof(dhcpreply_t),0); + if (discover_reply == NULL) { + return CFE_ERR_NOMEM; + } + memset(discover_reply,0,sizeof(dhcpreply_t)); + + request_reply = KMALLOC(sizeof(dhcpreply_t),0); + if (request_reply == NULL) { + KFREE(discover_reply); + return CFE_ERR_NOMEM; + } + memset(request_reply,0,sizeof(dhcpreply_t)); + + + /* + * Get our hw addr + */ + + hwaddr = net_getparam(NET_HWADDR); + if (!hwaddr) { + KFREE(discover_reply); + KFREE(request_reply); + return CFE_ERR_NETDOWN; + } + + /* + * Open UDP port + */ + + s = udp_socket(UDP_PORT_BOOTPS); + if (s < 0) { + KFREE(discover_reply); + KFREE(request_reply); + return CFE_ERR_NOHANDLES; + } + + udp_bind(s,UDP_PORT_BOOTPC); + + /* + * Do the boot request. Start by sending the OFFER message + */ + + nres = CFE_ERR_TIMEOUT; + for (retries = 0; retries < DHCP_NUM_RETRIES; retries++) { + nres = dhcp_do_dhcpdiscover(s,hwaddr,discover_reply); + if (nres == 0) break; + if (nres == CFE_ERR_TIMEOUT) continue; + break; + } + + /* + * If someone sent us a response, send the REQUEST message + * to get a lease. + */ + + if (nres == 0) { + + /* + * Now, send the REQUEST message and get a response. + */ + + for (retries = 0; retries < DHCP_NUM_RETRIES; retries++) { + nres = dhcp_do_dhcprequest(s,hwaddr, + request_reply, + discover_reply); + if (nres == 0) break; + if (nres == CFE_ERR_TIMEOUT) continue; + break; + } + } + + /* + * All done with the discover reply. + */ + + dhcp_free_reply(discover_reply); + + /* + * All done with UDP + */ + + udp_close(s); + + /* + * Return the reply info. + */ + + if (nres == 0) { + *rep = request_reply; + } + else { + *rep = NULL; + dhcp_free_reply(request_reply); + } + + return nres; +} diff --git a/cfe/cfe/net/net_dns.c b/cfe/cfe/net/net_dns.c new file mode 100755 index 0000000..89b50b1 --- /dev/null +++ b/cfe/cfe/net/net_dns.c @@ -0,0 +1,354 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * Domain Name System Resolver File: net_dns.c + * + * This module provides minimal support for looking up IP addresses + * from DNS name servers (RFCxxxx) + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +#include "lib_types.h" +#include "lib_string.h" +#include "lib_queue.h" +#include "lib_malloc.h" +#include "lib_printf.h" + +#include "cfe_timer.h" +#include "cfe_error.h" + +#include "net_ebuf.h" +#include "net_ether.h" + +#include "cfe.h" + +#include "net_api.h" + +/* ********************************************************************* + * Macros + ********************************************************************* */ + +#define ip_addriszero(a) (((a)[0]|(a)[1]|(a)[2]|(a)[3]) == 0) + +/* ********************************************************************* + * Constants + ********************************************************************* */ + +#define UDP_PORT_DNS 53 + +#define DNS_FLG_QUERY 0x0000 +#define DNS_FLG_RESPONSE 0x8000 +#define DNS_OPCODE_QUERY 0x0000 +#define DNS_FLG_AA 0x0400 +#define DNS_FLG_TC 0x0200 +#define DNS_FLG_RD 0x0100 +#define DNS_FLG_RA 0x0080 +#define DNS_RCODE_MASK 0x000F +#define DNS_RCODE_OK 0x0000 +#define DNS_RCODE_NAMEERR 0x0003 + + +#define QTYPE_HOSTADDR 1 +#define QCLASS_INTERNET 1 + +#define DNS_QUERY_TIMEOUT 1 /* seconds */ +#define DNS_RETRY_COUNT 8 + + +/* ********************************************************************* + * dns_dolookup(s,hostname,ipaddr) + * + * Look up a host name and return its IP address. + * + * Input parameters: + * s - udp socket + * server - name server to query + * hostname - host name to find + * ipaddr - buffer to place IP address + * + * Return value: + * 0 if no responses found + * >0 to indicate number of response records (usually 1) + * else error code + ********************************************************************* */ + +static int dns_dolookup(int s,uint8_t *server,char *hostname,uint8_t *ipaddr) +{ + ebuf_t *buf; + uint16_t id; + uint16_t tmp; + char *tok; + char *ptr; + int64_t timer; + int nres = 0; + uint16_t nqr,nar,nns,nxr; + uint8_t tmpb; + int expired; + + + /* + * Use the current time for our request ID + */ + + id = (uint16_t) cfe_ticks; + + /* + * Get a buffer and fill it in. + */ + + buf = udp_alloc(); + + ebuf_append_u16_be(buf,id); + ebuf_append_u16_be(buf,(DNS_FLG_QUERY | DNS_OPCODE_QUERY | DNS_FLG_RD)); + ebuf_append_u16_be(buf,1); /* one question */ + ebuf_append_u16_be(buf,0); /* no answers */ + ebuf_append_u16_be(buf,0); /* no server resource records */ + ebuf_append_u16_be(buf,0); /* no additional records */ + + /* + * Chop up the hostname into pieces, basically replacing + * the dots with length indicators. + */ + + ptr = hostname; + + while ((tok = strchr(ptr,'.'))) { + ebuf_append_u8(buf,(tok-ptr)); + ebuf_append_bytes(buf,ptr,(tok-ptr)); + ptr = tok + 1; + } + + ebuf_append_u8(buf,strlen(ptr)); + ebuf_append_bytes(buf,ptr,strlen(ptr)); + ebuf_append_u8(buf,0); + ebuf_append_u16_be(buf,QTYPE_HOSTADDR); + ebuf_append_u16_be(buf,QCLASS_INTERNET); + + /* + * Send the request to the name server + */ + + udp_send(s,buf,server); + + /* + * Set a timer for the response + */ + + TIMER_SET(timer,DNS_QUERY_TIMEOUT*CFE_HZ); + + /* + * Start waiting... + */ + + while (!(expired = TIMER_EXPIRED(timer))) { + POLL(); + + buf = udp_recv(s); + if (!buf) continue; + + /* IDs must match */ + + ebuf_get_u16_be(buf,tmp); + if (id != tmp) goto drop; + + /* It must be a response */ + + ebuf_get_u16_be(buf,tmp); + + if ((tmp & DNS_FLG_RESPONSE) == 0) goto drop; + + if ((tmp & DNS_RCODE_MASK) != DNS_RCODE_OK) { + udp_free(buf); + /* name error */ + break; + } + + ebuf_get_u16_be(buf,nqr); + ebuf_get_u16_be(buf,nar); + ebuf_get_u16_be(buf,nns); + ebuf_get_u16_be(buf,nxr); + + if (nar == 0) { + goto drop; + } + + while (nqr > 0) { + if (ebuf_length(buf) <= 0) { + goto drop; + } + for (;;) { + ebuf_get_u8(buf,tmpb); + if (tmpb == 0) break; + ebuf_skip(buf,tmpb); + if (ebuf_length(buf) <= 0) { + goto drop; + } + } + ebuf_skip(buf,2); /* skip QTYPE */ + ebuf_skip(buf,2); /* skip QCLASS */ + nqr--; /* next question record */ + } + + /* + * Loop through the answer records to find + * a HOSTADDR record. Ignore any other records + * we find. + */ + + while (nar > 0) { + uint16_t rname,rtype,rclass,dlen; + + ebuf_get_u16_be(buf,rname); /* resource name */ + + ebuf_get_u16_be(buf,rtype); /* resource type */ + + ebuf_get_u16_be(buf,rclass); /* resource class */ + + ebuf_skip(buf,4); /* time to live */ + + ebuf_get_u16_be(buf,dlen); /* length of data */ + + if (rtype != QTYPE_HOSTADDR) { + ebuf_skip(buf,dlen); + nar--; + continue; + } + if (rclass != QCLASS_INTERNET) { + ebuf_skip(buf,dlen); + nar--; + continue; + } + + if (dlen != IP_ADDR_LEN) { + ebuf_skip(buf,dlen); + nar--; + continue; + } + + ebuf_get_bytes(buf,ipaddr,IP_ADDR_LEN); + break; + } + + if (nar == 0) goto drop; + + udp_free(buf); + nres++; + break; + + drop: + udp_free(buf); + } + + if (expired) return CFE_ERR_TIMEOUT; + if (nres == 0) return CFE_ERR_HOSTUNKNOWN; + return nres; +} + + +/* ********************************************************************* + * dns_lookup(hostname,ipaddr) + * + * Look up a host name and return its IP address. + * + * Input parameters: + * hostname - host name to find + * ipaddr - buffer to place IP address + * + * Return value: + * 0 if no responses found + * >0 to indicate number of response records (usually 1) + * else error code + ********************************************************************* */ + +int dns_lookup(char *hostname,uint8_t *ipaddr) +{ + int s; + int nres = 0; + int retries; + char temphostname[100]; + uint8_t *server; + uint8_t *tok; + + /* + * If it's a valid IP address, don't look it up. + */ + + if (parseipaddr(hostname,ipaddr) == 0) return 1; + + /* + * Add default domain if no domain was specified + */ + + if (strchr(hostname,'.') == NULL) { + tok = net_getparam(NET_DOMAIN); + if (tok) { + xsprintf(temphostname,"%s.%s",hostname,tok); + hostname = temphostname; + } + } + + /* + * Figure out who the name server is + */ + + server = net_getparam(NET_NAMESERVER); + + if (!server) return CFE_ERR_NETDOWN; + if (ip_addriszero(server)) return CFE_ERR_NONAMESERVER; + + /* + * Go do the name server lookup + */ + + s = udp_socket(UDP_PORT_DNS); + if (s < 0) return CFE_ERR_NOHANDLES; + + for (retries = 0; retries < DNS_RETRY_COUNT; retries++) { + nres = dns_dolookup(s,server,hostname,ipaddr); + if (nres == CFE_ERR_TIMEOUT) continue; + if (nres >= 0) break; + } + + udp_close(s); + + return nres; + +} diff --git a/cfe/cfe/net/net_ebuf.h b/cfe/cfe/net/net_ebuf.h new file mode 100644 index 0000000..11d83c1 --- /dev/null +++ b/cfe/cfe/net/net_ebuf.h @@ -0,0 +1,165 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * Network EBUF macros File: net_ebuf.h + * + * This file contains macros and function prototypes for + * messing with "ebuf" buffers. ebufs describe network + * packets. They're sort of like poor-man's mbufs :-) + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +/* ********************************************************************* + * Constants + ********************************************************************* */ + + +#define ENET_MAX_PKT 1514 +#define ENET_CRC_SIZE 4 +#define ENET_ADDR_LEN 6 +#define ENET_DIX_HEADER 14 + +/* ********************************************************************* + * Structures + ********************************************************************* */ + + +typedef struct ebuf_s { + queue_t eb_qblock; /* linkage */ + unsigned int eb_length; /* length of area at eb_ptr */ + unsigned int eb_status; /* rx/tx status */ + int eb_port; /* eth port that owns buffer */ + void *eb_device; /* underlying net device */ + int eb_usrdata; /* user-defined stuff */ + uint8_t *eb_usrptr; /* user-defined stuff */ + uint8_t *eb_ptr; /* current ptr within pkt */ + uint8_t eb_data[0x5F0]; /* data, must be > ENET_MAX_PKT */ + /* and divisible by sizeof(uint) */ +} ebuf_t; + +/* + * Macros to initialize ebufs + */ + +#define ebuf_init_rx(eb) (eb)->eb_ptr = (eb)->eb_data; \ + (eb)->eb_length = 0; \ + (eb)->eb_status = 0 + +#define ebuf_init_tx(eb) (eb)->eb_ptr = (eb)->eb_data; \ + (eb)->eb_length = 0; \ + (eb)->eb_status = 0 + +/* + * Macros to move the currens position within an ebuf + */ + +#define ebuf_prepend(eb,amt) (eb)->eb_ptr -= amt; (eb)->eb_length += (amt) +#define ebuf_adjust(eb,amt) (eb)->eb_ptr += (amt); (eb)->eb_length += (amt) +#define ebuf_seek(eb,amt) (eb)->eb_ptr += (amt) + +#define ebuf_remlen(eb) ((eb)->eb_length) +#define ebuf_length(eb) ((eb)->eb_length) +#define ebuf_ptr(eb) ((eb)->eb_ptr) +#define ebuf_setlength(eb,amt) (eb)->eb_length = (amt) + +/* + * ebuf_append_xxx macros - these macros add data to the + * end of a packet, adjusting the length + */ + +#define ebuf_append_u8(eb,b) ((eb)->eb_ptr[(eb)->eb_length++]) = (b) ; +#define ebuf_append_u16_be(eb,w) ebuf_append_u8(eb,((w) >> 8) & 0xFF) ; \ + ebuf_append_u8(eb,((w) & 0xFF)) + +#define ebuf_append_u32_be(eb,w) ebuf_append_u8(eb,((w) >> 24) & 0xFF) ; \ + ebuf_append_u8(eb,((w) >> 16) & 0xFF) ; \ + ebuf_append_u8(eb,((w) >> 8) & 0xFF) ; \ + ebuf_append_u8(eb,((w) & 0xFF)) + +#define ebuf_append_bytes(eb,b,l) memcpy(&((eb)->eb_ptr[(eb)->eb_length]),(b),(l)); \ + (eb)->eb_length += (l) + +/* + * ebuf_put_xxx macros - these macros modify data in the middle + * of a packet, typically in an area already allocated with + * ebuf_adjust. The length is not updated, but the pointer is. + */ + +#define ebuf_put_u8(eb,b) *((eb)->eb_ptr++) = (b) +#define ebuf_put_u16_be(eb,w) ebuf_put_u8(eb,((w) >> 8) & 0xFF) ; \ + ebuf_put_u8(eb,((w) & 0xFF)) + +#define ebuf_put_u32_be(eb,w) ebuf_put_u8(eb,((w) >> 24) & 0xFF) ; \ + ebuf_put_u8(eb,((w) >> 16) & 0xFF) ; \ + ebuf_put_u8(eb,((w) >> 8) & 0xFF) ; \ + ebuf_put_u8(eb,((w) & 0xFF)) + +#define ebuf_put_bytes(eb,b,l) memcpy((buf)->eb_ptr,(b),(l)) ; (buf)->eb_ptr += (l) + +#define ebuf_srcaddr(eb) &((eb)->eb_data[6]) +#define ebuf_destaddr(eb) &((eb)->eb_data[0]) + +/* return next byte from the ebuf, and advance pointer */ + +#define ebuf_skip(eb,amt) (eb)->eb_length -= (amt); (eb)->eb_ptr += (amt) + +#define ebuf_get_u8(eb,v) (eb)->eb_length--; \ + v = (*((eb)->eb_ptr++)) + +/* return next u16 from the ebuf, big-endian */ + +#define ebuf_get_u16_be(eb,v) (eb)->eb_ptr+=2; \ + (eb)->eb_length-=2; \ + v = ( ((uint16_t)*((eb)->eb_ptr-1)) | \ + (((uint16_t)*((eb)->eb_ptr-2)) << 8) ) + +/* return next u32 from the ebuf, big-endian */ + +#define ebuf_get_u32_be(eb,v) (eb)->eb_ptr+=4; \ + (eb)->eb_length-=4; \ + v = ( ((uint32_t)*((eb)->eb_ptr-1)) | \ + (((uint32_t)*((eb)->eb_ptr-2)) << 8) | \ + (((uint32_t)*((eb)->eb_ptr-3)) << 16) | \ + (((uint32_t)*((eb)->eb_ptr-4)) << 24) ) + +#define ebuf_get_bytes(eb,b,l) memcpy((b),(buf)->eb_ptr,(l)) ; (eb)->eb_ptr += (l) ; (eb)->eb_length -= (l) + + diff --git a/cfe/cfe/net/net_ether.c b/cfe/cfe/net/net_ether.c new file mode 100644 index 0000000..0dec6e8 --- /dev/null +++ b/cfe/cfe/net/net_ether.c @@ -0,0 +1,705 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * Ethernet Datalink File: net_ether.c + * + * This module provides a simple datalink (LLC1) interface + * capable of demultiplexing standard DIX-style Ethernet packets. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + +#include "lib_types.h" +#include "lib_string.h" +#include "lib_queue.h" +#include "lib_malloc.h" +#include "lib_printf.h" + +#include "cfe_iocb.h" +#include "cfe_devfuncs.h" +#include "cfe_ioctl.h" +#include "cfe_error.h" + +#include "net_ebuf.h" +#include "net_ether.h" + + +/* ********************************************************************* + * Constants + ********************************************************************* */ + +#define ETH_MAX_PORTS 4 +#define ETH_MAX_BUFFERS 8 + +/* ********************************************************************* + * Types + ********************************************************************* */ + +typedef struct ether_port_s { + int ep_dev; + uint8_t ep_proto[8]; + int ep_ptype; + int ep_mtu; + int (*ep_rxcallback)(ebuf_t *buf,void *ref); + void *ep_ref; +} ether_port_t; + +struct ether_info_s { + ether_port_t *eth_ports; + queue_t eth_freelist; + uint8_t eth_hwaddr[6]; + int eth_devhandle; + ebuf_t *eth_bufpool; +}; + +/* ********************************************************************* + * Globals + ********************************************************************* */ + +const uint8_t eth_broadcast[ENET_ADDR_LEN] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; + + +/* ********************************************************************* + * eth_open(eth,ptye,pdata,cb) + * + * Open an Ethernet portal. + * + * Input parameters: + * eth - ethernet context + * ptype - protocol type (ETH_PTYPE_xxx) + * pdata - protocol data (two bytes for DIX protocols) + * cb - callback for receive packets + * + * Return value: + * portal number + * or <0 if error occured + ********************************************************************* */ + +int eth_open(ether_info_t *eth,int ptype,char *pdata, + int (*cb)(ebuf_t *buf,void *ref),void *ref) +{ + ether_port_t *p; + int portnum; + + p = eth->eth_ports; + + for (portnum = 0; portnum < ETH_MAX_PORTS; portnum++,p++) { + if (p->ep_rxcallback == NULL) break; + } + + if (portnum == ETH_MAX_PORTS) { + return CFE_ERR_NOHANDLES; /* no ports left */ + } + + switch (ptype) { + case ETH_PTYPE_DIX: + p->ep_proto[0] = pdata[0]; + p->ep_proto[1] = pdata[1]; + p->ep_mtu = ENET_MAX_PKT - ENET_DIX_HEADER; + break; + + case ETH_PTYPE_802SAP: + case ETH_PTYPE_802SNAP: + default: + /* + * we only support DIX etypes right now. If we ever want to support + * non-IP stacks (unlikely) this will need to change. + */ + return CFE_ERR_UNSUPPORTED; + } + + p->ep_ptype = ptype; + p->ep_rxcallback = cb; + p->ep_dev = eth->eth_devhandle; + p->ep_ref = ref; + + return portnum; +} + + +/* ********************************************************************* + * eth_close(eth,port) + * + * Close an Ethernet portal, freeing resources allocated to it. + * + * Input parameters: + * eth - ethernet context + * port - portal number + * + * Return value: + * nothing + ********************************************************************* */ + +void eth_close(ether_info_t *eth,int port) +{ + ether_port_t *p = &(eth->eth_ports[port]); + + p->ep_ptype = 0; + p->ep_rxcallback = NULL; + p->ep_dev = 0; + memset(&(p->ep_proto[0]),0,sizeof(p->ep_proto)); +} + + +/* ********************************************************************* + * eth_findport(eth,buf) + * + * Locate the portal associated with a particular Ethernet packet. + * Parse the packet enough to determine if it's addressed + * correctly and to a valid protocol, and then look up the + * corresponding portal. + * + * Input parameters: + * eth - ethernet context + * buf - ethernet buffer to check + * + * Return value: + * eth_port_t structure or NULL if packet should be dropped + ********************************************************************* */ + +static ether_port_t *eth_findport(ether_info_t *eth,ebuf_t *buf) +{ + int idx; + ether_port_t *p; + + /* + * A few pre-flight checks: packets *from* multicast addresses + * are not allowed. + */ + + if (buf->eb_ptr[6] & 1) return NULL; + + /* + * Packets smaller than minimum size are not allowed. + */ + + if (buf->eb_length < 60) return NULL; + + /* + * Packets with bad status are not allowed + */ + + /* XXX if (buf->eb_status != 0) return NULL; */ + + /* + * Okay, scan the port list and find the matching portal. + */ + + for (idx = 0, p = eth->eth_ports; idx < ETH_MAX_PORTS; idx++,p++) { + if (!p->ep_rxcallback) continue; /* port not in use */ + + switch (p->ep_ptype) { + case ETH_PTYPE_DIX: + if ((p->ep_proto[0] == buf->eb_ptr[12]) && + (p->ep_proto[1] == buf->eb_ptr[13])) { + ebuf_skip(buf,ENET_DIX_HEADER); + return p; + } + break; + case ETH_PTYPE_802SAP: + case ETH_PTYPE_802SNAP: + default: + break; + } + } + + return NULL; +} + +/* ********************************************************************* + * eth_poll(eth) + * + * Poll devices and process inbound packets. If new packets arrive, + * call the appropriate callback routine. + * + * Input parameters: + * eth - ethernet context + * + * Return value: + * nothing + ********************************************************************* */ + +void eth_poll(ether_info_t *eth) +{ + ebuf_t *buf; + ether_port_t *p; + int res; + + /* XXX should this loop until all packets are processed? */ + + /* + * If no packets, just get out now + */ + + if (cfe_inpstat(eth->eth_devhandle) == 0) return; + + /* + * get a packet from the free list + */ + + buf = (ebuf_t *) q_deqnext(&(eth->eth_freelist)); + if (!buf) return; + + /* + * Receive network data into the packet buffer + */ + + ebuf_init_rx(buf); + res = cfe_read(eth->eth_devhandle,buf->eb_ptr,ENET_MAX_PKT); + + /* + * if receive error, get out now. + */ + + if (res <= 0) { + q_enqueue(&(eth->eth_freelist),(queue_t *) buf); + return; + } + + /* + * init the rest of the fields in the ebuf + */ + + buf->eb_length = res; + buf->eb_status = 0; + buf->eb_device = eth; + buf->eb_usrdata = 0; + + /* + * Look up the portal to receive the new packet + */ + + p = eth_findport(eth,buf); + + /* + * Call the callback routine if we want to keep this + * buffer. Otherwise, drop it on the floor + */ + + if (p) { + buf->eb_port = p - eth->eth_ports; + res = (*(p->ep_rxcallback))(buf,p->ep_ref); + if (res == ETH_DROP) eth_free(buf); + } + else { + eth_free(buf); + } +} + + +/* ********************************************************************* + * eth_gethwaddr(eth,hwaddr) + * + * Obtain the hardware address of the Ethernet interface. + * + * Input parameters: + * eth - ethernet context + * hwaddr - place to put hardware address - 6 bytes + * + * Return value: + * nothing + ********************************************************************* */ + +void eth_gethwaddr(ether_info_t *eth,uint8_t *hwaddr) +{ + memcpy(hwaddr,eth->eth_hwaddr,ENET_ADDR_LEN); +} + +/* ********************************************************************* + * eth_sethwaddr(eth,hwaddr) + * + * Set the hardware address of the Ethernet interface. + * + * Input parameters: + * eth - ethernet context + * hwaddr - new hardware address - 6 bytes + * + * Return value: + * nothing + ********************************************************************* */ + +void eth_sethwaddr(ether_info_t *eth,uint8_t *hwaddr) +{ + int retlen; + + memcpy(eth->eth_hwaddr,hwaddr,ENET_ADDR_LEN); + cfe_ioctl(eth->eth_devhandle,IOCTL_ETHER_SETHWADDR,&(eth->eth_hwaddr[0]), + sizeof(eth->eth_hwaddr),&retlen,0); + +} + + + +/* ********************************************************************* + * eth_setspeed(eth,speed) + * + * Set the speed of the Ethernet interface. + * + * Input parameters: + * eth - ethernet context + * speed - target speed (or auto for automatic) + * + * Return value: + * nothing + ********************************************************************* */ + +int eth_setspeed(ether_info_t *eth,int speed) +{ + int retlen; + + return cfe_ioctl(eth->eth_devhandle,IOCTL_ETHER_SETSPEED, + (uint8_t *) &speed,sizeof(speed),&retlen,0); + +} + +/* ********************************************************************* + * eth_setloopback(eth,loop) + * + * Configure loopback mode options for the Ethernet + * + * Input parameters: + * eth - ethernet context + * loop - loopback mode to set + * + * Return value: + * nothing + ********************************************************************* */ + +int eth_setloopback(ether_info_t *eth,int loop) +{ + int retlen; + + return cfe_ioctl(eth->eth_devhandle,IOCTL_ETHER_SETLOOPBACK, + (uint8_t *) &loop,sizeof(loop),&retlen,0); + +} + +/* ********************************************************************* + * eth_getspeed(eth,speed) + * + * Get the current setting for the Ethernet speed (note that this + * is the speed we want to achieve, not the current speed) + * + * Input parameters: + * eth - ethernet context + * speed - pointer to int to receive speed + * + * Return value: + * nothing + ********************************************************************* */ + +int eth_getspeed(ether_info_t *eth,int *speed) +{ + int retlen; + + return cfe_ioctl(eth->eth_devhandle,IOCTL_ETHER_GETSPEED, + (uint8_t *) speed,sizeof(*speed),&retlen,0); + +} + +/* ********************************************************************* + * eth_getloopback(eth,loop) + * + * Read the loopback state of the Ethernet interface + * + * Input parameters: + * eth - ethernet context + * loop - pointer to int to receive loopback state + * + * Return value: + * nothing + ********************************************************************* */ + +int eth_getloopback(ether_info_t *eth,int *loop) +{ + int retlen; + + return cfe_ioctl(eth->eth_devhandle,IOCTL_ETHER_GETLOOPBACK, + (uint8_t *) loop,sizeof(*loop),&retlen,0); + +} + +/* ********************************************************************* + * eth_send(buf,dest) + * + * Transmit a packet. + * + * Input parameters: + * buf - ebuf structure describing packet + * dest - destination hardware address + * + * Return value: + * 0 - no error + * else error code + ********************************************************************* */ + +int eth_send(ebuf_t *buf,uint8_t *dest) +{ + ether_info_t *eth = buf->eb_device; + ether_port_t *p = &(eth->eth_ports[buf->eb_port]); + int res; + + switch (p->ep_ptype) { + case ETH_PTYPE_DIX: + ebuf_seek(buf,-ENET_DIX_HEADER); + ebuf_put_bytes(buf,dest,ENET_ADDR_LEN); + ebuf_put_bytes(buf,eth->eth_hwaddr,ENET_ADDR_LEN); + ebuf_put_bytes(buf,p->ep_proto,2); + /* adjust pointer and add in DIX header length */ + ebuf_prepend(buf,ENET_DIX_HEADER); + break; + case ETH_PTYPE_802SAP: + case ETH_PTYPE_802SNAP: + default: + eth_free(buf); /* should not happen */ + return CFE_ERR_UNSUPPORTED; + } + + res = cfe_write(p->ep_dev,ebuf_ptr(buf),ebuf_length(buf)); + + /* XXX - should we free buffers here? */ + + return res; +} + +/* ********************************************************************* + * eth_alloc(eth,port) + * + * Allocate an Ethernet buffer. Ethernet buffers know what + * ports they are associated with, since we need to reserve + * space for the EThernet header, which might vary in size + * for DIX, 802, etc. + * + * Input parameters: + * eth - ethernet context + * port - portal ID + * + * Return value: + * ebuf, or NULL if no ebufs left + ********************************************************************* */ + +ebuf_t *eth_alloc(ether_info_t *eth,int port) +{ + ebuf_t *buf; + ether_port_t *p = &(eth->eth_ports[port]); + + buf = (ebuf_t *) q_deqnext(&(eth->eth_freelist)); + if (buf == NULL) return NULL; + + buf->eb_status = 0; + buf->eb_port = port; + buf->eb_device = eth; + ebuf_init_tx(buf); + + switch (p->ep_ptype) { + case ETH_PTYPE_NONE: + break; + case ETH_PTYPE_DIX: + ebuf_seek(buf,ENET_DIX_HEADER); + break; + case ETH_PTYPE_802SAP: + case ETH_PTYPE_802SNAP: + default: + /* XXX Other ether types here */ + break; + } + + /* + * 'eb_ptr' points at new data, length is cleared. + * We will add the length back in at send time when the + * ethernet header is filled in. + */ + buf->eb_length = 0; + + return buf; +} + +/* ********************************************************************* + * eth_free(buf) + * + * Free an ebuf. + * + * Input parameters: + * buf - ebuf to free + * + * Return value: + * nothing + ********************************************************************* */ + +void eth_free(ebuf_t *buf) +{ + ether_info_t *eth = buf->eb_device; + + q_enqueue(&(eth->eth_freelist),(queue_t *) buf); +} + + +/* ********************************************************************* + * eth_getmtu(eth,port) + * + * Return the mtu of the specified Ethernet port. The mtu + * is the maximum number of bytes you can put in the buffer, + * excluding the Ethernet header. + * + * Input parameters: + * eth - ethernet context + * port - portal ID + * + * Return value: + * number of bytes + ********************************************************************* */ + + +int eth_getmtu(ether_info_t *eth,int port) +{ + ether_port_t *p = &(eth->eth_ports[port]); + + return p->ep_mtu; +} + + +/* ********************************************************************* + * eth_init(devname) + * + * Create an Ethernet context for a particular Ethernet device. + * + * Input parameters: + * devname - device name for underlying Ethernet driver + * + * Return value: + * ethernet context, or NULL of it could not be created. + ********************************************************************* */ + +ether_info_t *eth_init(char *devname) +{ + int idx; + ebuf_t *buf; + ether_info_t *eth; + int devhandle; + int retlen; + + /* + * Open the device driver + */ + + devhandle = cfe_open(devname); + if (devhandle < 0) return NULL; + + eth = KMALLOC(sizeof(ether_info_t),0); + if (!eth) { + cfe_close(devhandle); + return NULL; + } + + memset(eth,0,sizeof(ether_info_t)); + + /* + * Obtain hardware address + */ + + cfe_ioctl(devhandle,IOCTL_ETHER_GETHWADDR,&(eth->eth_hwaddr[0]), + sizeof(eth->eth_hwaddr),&retlen,0); + + /* + * Allocate portal table + */ + + eth->eth_ports = KMALLOC(ETH_MAX_PORTS*sizeof(ether_port_t),0); + if (!eth->eth_ports) { + cfe_close(devhandle); + KFREE(eth); + return NULL; + } + + memset(eth->eth_ports,0,ETH_MAX_PORTS*sizeof(ether_port_t)); + + /* + * Allocate buffer pool + */ + + eth->eth_bufpool = (ebuf_t *) KMALLOC(sizeof(ebuf_t)*ETH_MAX_BUFFERS,0); + if (!eth->eth_bufpool) { + cfe_close(devhandle); + KFREE(eth->eth_ports); + KFREE(eth); + return NULL; + } + + /* + * Chain buffers onto the free list + */ + + q_init(&(eth->eth_freelist)); + buf = eth->eth_bufpool; + for (idx = 0; idx < ETH_MAX_BUFFERS; idx++) { + q_enqueue(&(eth->eth_freelist),(queue_t *) buf); + buf++; + } + + /* + * Remember the device handle + */ + + eth->eth_devhandle = devhandle; + + return eth; +} + + +/* ********************************************************************* + * eth_uninit(eth) + * + * Close and free up an Ethernet context + * + * Input parameters: + * eth - ethernet context + * + * Return value: + * nothing + ********************************************************************* */ + +void eth_uninit(ether_info_t *eth) +{ + cfe_close(eth->eth_devhandle); + KFREE(eth->eth_bufpool); + KFREE(eth->eth_ports); + KFREE(eth); +} + + diff --git a/cfe/cfe/net/net_ether.h b/cfe/cfe/net/net_ether.h new file mode 100644 index 0000000..a94130b --- /dev/null +++ b/cfe/cfe/net/net_ether.h @@ -0,0 +1,89 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * Ethernet protocol demux defns File: net_ether.h + * + * constants and prototypes for the Ethernet datalink + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + + +/* ********************************************************************* + * Constants + ********************************************************************* */ + +#define ETH_PTYPE_NONE 0 +#define ETH_PTYPE_DIX 1 +#define ETH_PTYPE_802SAP 2 +#define ETH_PTYPE_802SNAP 3 + +#define ETH_KEEP 1 +#define ETH_DROP 0 + +/* ********************************************************************* + * types + ********************************************************************* */ + +typedef struct ether_info_s ether_info_t; + +/* ********************************************************************* + * Prototypes + ********************************************************************* */ + +int eth_open(ether_info_t *eth,int ptype,char *pdata,int (*cb)(ebuf_t *buf,void *ref),void *ref); +void eth_close(ether_info_t *eth,int port); +void eth_poll(ether_info_t *eth); +int eth_send(ebuf_t *buf,uint8_t *dest); +ebuf_t *eth_alloc(ether_info_t *eth,int port); +void eth_free(ebuf_t *buf); +ether_info_t *eth_init(char *devname); +void eth_uninit(ether_info_t *eth); +int eth_getmtu(ether_info_t *,int port); +void eth_gethwaddr(ether_info_t *,uint8_t *hwaddr); +void eth_sethwaddr(ether_info_t *,uint8_t *hwaddr); +int eth_getspeed(ether_info_t *,int *speed); +int eth_setspeed(ether_info_t *,int speed); +int eth_getloopback(ether_info_t *,int *loop); +int eth_setloopback(ether_info_t *,int loop); +extern const uint8_t eth_broadcast[ENET_ADDR_LEN]; + + + diff --git a/cfe/cfe/net/net_icmp.c b/cfe/cfe/net/net_icmp.c new file mode 100644 index 0000000..e98bc87 --- /dev/null +++ b/cfe/cfe/net/net_icmp.c @@ -0,0 +1,312 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * ICMP Protocol File: net_icmp.c + * + * This module implements portions of the ICMP protocol. Note + * that it is not a complete implementation, just enough to + * generate and respond to ICMP echo requests. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +#include "lib_types.h" +#include "lib_string.h" +#include "lib_queue.h" +#include "lib_malloc.h" +#include "lib_printf.h" + +#include "cfe_timer.h" + +#include "net_ebuf.h" +#include "net_ether.h" + +#include "net_ip.h" + +/* ********************************************************************* + * Constants + ********************************************************************* */ + +#define ICMP_CODE_ECHO 0 +#define ICMP_TYPE_ECHOREPLY 0 +#define ICMP_TYPE_ECHOREQ 8 + +#define ICMPMSG(type,code) (((type)<<8)|(code)) + +/* ********************************************************************* + * Structures + ********************************************************************* */ + +struct icmp_info_s { + ip_info_t *icmp_ipinfo; + queue_t icmp_echoreplies; + int icmp_maxreplies; +}; + +/* ********************************************************************* + * ICMP_RX_CALLBACK(ref,buf,dst,src) + * + * This routine is called by the IP layer when we receive + * ICMP protocol messages. + * + * Input parameters: + * ref - reference data (an icmp_info_t) + * buf - the ebuf containing the buffer + * dst - destination IP address (us, usually) + * src - source IP address + * + * Return value: + * ETH_KEEP to keep packet, ETH_DROP to cause packet to be freed + ********************************************************************* */ + +static int icmp_rx_callback(void *ref,ebuf_t *buf,uint8_t *dst,uint8_t *src) +{ + icmp_info_t *icmp = (icmp_info_t *)ref; + ip_info_t *ipi = icmp->icmp_ipinfo; + uint16_t imsg; + uint16_t cksum; + ebuf_t *txbuf; + uint8_t *icmphdr; + int res; + + imsg = ICMPMSG(buf->eb_ptr[0],buf->eb_ptr[1]); + + res = ETH_DROP; /* assume we're dropping the pkt */ + + switch (imsg) { + case ICMPMSG(ICMP_TYPE_ECHOREQ,ICMP_CODE_ECHO): + txbuf = _ip_alloc(ipi); + if (txbuf) { + /* Construct reply from the original packet. */ + icmphdr = txbuf->eb_ptr; + ebuf_append_bytes(txbuf,buf->eb_ptr,buf->eb_length); + icmphdr[0] = ICMP_TYPE_ECHOREPLY; + icmphdr[1] = ICMP_CODE_ECHO; + icmphdr[2] = 0; icmphdr[3] = 0; + cksum = ~ip_chksum(0,icmphdr,ebuf_length(txbuf)); + icmphdr[2] = (cksum >> 8) & 0xFF; + icmphdr[3] = (cksum & 0xFF); + if (_ip_send(ipi,txbuf,src,IPPROTO_ICMP) < 0) { + _ip_free(ipi,txbuf); + } + } + break; + + case ICMPMSG(ICMP_TYPE_ECHOREPLY,ICMP_CODE_ECHO): + if (q_count(&(icmp->icmp_echoreplies)) < icmp->icmp_maxreplies) { + /* We're keeping this packet, put it on the queue and don't + free it in the driver. */ + q_enqueue(&(icmp->icmp_echoreplies),(queue_t *) buf); + res = ETH_KEEP; + } + break; + + default: + res = ETH_DROP; + } + + return res; +} + + +/* ********************************************************************* + * _ICMP_INIT(ipi) + * + * Initialize the ICMP layer. + * + * Input parameters: + * ipi - ipinfo structure of IP layer to attach to + * + * Return value: + * icmp_info_t structure or NULL if error occurs + ********************************************************************* */ + +icmp_info_t *_icmp_init(ip_info_t *ipi) +{ + icmp_info_t *icmp; + + icmp = (icmp_info_t *) KMALLOC(sizeof(icmp_info_t),0); + if (!icmp) return NULL; + + icmp->icmp_ipinfo = ipi; + q_init(&(icmp->icmp_echoreplies)); + icmp->icmp_maxreplies = 0; + + _ip_register(ipi,IPPROTO_ICMP,icmp_rx_callback,icmp); + + return icmp; +} + +/* ********************************************************************* + * _ICMP_UNINIT(icmp) + * + * Un-initialize the ICMP layer. + * + * Input parameters: + * icmp - icmp_info_t structure + * + * Return value: + * nothing + ********************************************************************* */ + +void _icmp_uninit(icmp_info_t *icmp) +{ + _ip_deregister(icmp->icmp_ipinfo,IPPROTO_ICMP); + + KFREE(icmp); +} + + +/* ********************************************************************* + * _ICMP_PING(icmp,dest,seq,len) + * + * Transmit an ICMP echo request and wait for a reply. + * + * Input parameters: + * icmp - icmp_info_t structure + * dest - destination IP address + * seq - sequence number for ICMP packet + * len - length of data portion of ICMP packet + * + * Return value: + * <0 = error + * 0 = timeout + * >0 = reply received + ********************************************************************* */ + +int _icmp_ping(icmp_info_t *icmp,uint8_t *dest,int seq,int len) +{ + ebuf_t *buf; + int64_t timer; + uint16_t cksum; + uint8_t *icmphdr; + uint16_t id; + int idx; + int result = 0; + + /* + * Get an ebuf + */ + + buf = _ip_alloc(icmp->icmp_ipinfo); + if (buf == NULL) return -1; + + /* + * Remember where the ICMP header is going to be so we can + * calculate the checksum later. + */ + + icmphdr = buf->eb_ptr; + + id = (uint16_t) cfe_ticks; + + /* + * Construct the ICMP header and data portion. + */ + + ebuf_append_u8(buf,8); /* echo message */ + ebuf_append_u8(buf,0); /* code = 0 */ + ebuf_append_u16_be(buf,0); /* empty checksum for now */ + ebuf_append_u16_be(buf,id); /* packet ID */ + ebuf_append_u16_be(buf,((uint16_t)seq)); /* sequence # */ + + for (idx = 0; idx < len; idx++) { + ebuf_append_u8(buf,((idx+0x40)&0xFF)); /* data */ + } + + /* + * Calculate and install the checksum + */ + + cksum = ~ip_chksum(0,icmphdr,ebuf_length(buf)); + icmphdr[2] = (cksum >> 8) & 0xFF; + icmphdr[3] = (cksum & 0xFF); + + /* + * Transmit the ICMP echo + */ + + icmp->icmp_maxreplies = 1; /* allow ICMP replies */ + _ip_send(icmp->icmp_ipinfo,buf,dest,IPPROTO_ICMP); + buf = NULL; + + /* + * Wait for a reply + */ + + TIMER_SET(timer,2*CFE_HZ); + + while (!TIMER_EXPIRED(timer)) { + + POLL(); + buf = (ebuf_t *) q_deqnext(&(icmp->icmp_echoreplies)); + + /* If we get a packet, make sure it matches. */ + + if (buf) { + uint16_t rxid,rxseq; + + cksum = ip_chksum(0,buf->eb_ptr,ebuf_length(buf)); + if (cksum == 0xFFFF) { + ebuf_skip(buf,2); + ebuf_skip(buf,2); /* skip checksum */ + ebuf_get_u16_be(buf,rxid); + ebuf_get_u16_be(buf,rxseq); + + if ((id == rxid) && ((uint16_t) seq == rxseq)) { + result = 1; + break; + } + } + _ip_free(icmp->icmp_ipinfo,buf); + } + } + + /* + * Don't accept any more replies. + */ + + icmp->icmp_maxreplies = 0; /* allow ICMP replies */ + + if (buf) _ip_free(icmp->icmp_ipinfo,buf); + + return result; + +} diff --git a/cfe/cfe/net/net_ip.c b/cfe/cfe/net/net_ip.c new file mode 100755 index 0000000..86303e2 --- /dev/null +++ b/cfe/cfe/net/net_ip.c @@ -0,0 +1,722 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * Internet Protocol File: net_ip.c + * + * This module implements the IP layer (RFC791) + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +#include "lib_types.h" +#include "lib_string.h" +#include "lib_queue.h" +#include "lib_malloc.h" +#include "lib_printf.h" + +#include "net_ebuf.h" +#include "net_ether.h" + +#include "net_ip.h" +#include "net_ip_internal.h" + +#include "cfe_error.h" + +/* ********************************************************************* + * Forward declarations + ********************************************************************* */ + +static int ip_rx_callback(ebuf_t *buf,void *ref); + +/* ********************************************************************* + * _ip_alloc(ipi) + * + * Allocate an ebuf and reserve space for the IP header in it. + * + * Input parameters: + * ipi - IP stack information + * + * Return value: + * ebuf - an ebuf, or NULL if there are none left + ********************************************************************* */ + +ebuf_t *_ip_alloc(ip_info_t *ipi) +{ + ebuf_t *buf; + + buf = eth_alloc(ipi->eth_info,ipi->ip_port); + + if (buf == NULL) return buf; + + ebuf_seek(buf,IPHDR_LENGTH); + ebuf_setlength(buf,0); + + return buf; +} + + +/* ********************************************************************* + * ip_chksum(initcksum,ptr,len) + * + * Do an IP checksum for the specified buffer. You can pass + * an initial checksum if you're continuing a previous checksum + * calculation, such as for UDP headers and pseudoheaders. + * + * Input parameters: + * initcksum - initial checksum (usually zero) + * ptr - pointer to buffer to checksum + * len - length of data in bytes + * + * Return value: + * checksum (16 bits) + ********************************************************************* */ + +uint16_t ip_chksum(uint16_t initcksum,uint8_t *ptr,int len) +{ + unsigned int cksum; + int idx; + int odd; + + cksum = (unsigned int) initcksum; + + odd = len & 1; + len -= odd; + + for (idx = 0; idx < len; idx += 2) { + cksum += ((unsigned long) ptr[idx] << 8) + ((unsigned long) ptr[idx+1]); + } + + if (odd) { /* buffer is odd length */ + cksum += ((unsigned long) ptr[idx] << 8); + } + + /* + * Fold in the carries + */ + + while (cksum >> 16) { + cksum = (cksum & 0xFFFF) + (cksum >> 16); + } + + return cksum; +} + + +/* ********************************************************************* + * _ip_send(ipi,buf,destaddr,proto) + * + * Send an IP datagram. We only support non-fragmented datagrams + * at this time. + * + * Input parameters: + * ipi - IP stack information + * buf - an ebuf + * destaddr - destination IP address + * proto - IP protocol number + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int _ip_send(ip_info_t *ipi,ebuf_t *buf,uint8_t *destaddr,uint8_t proto) +{ + uint16_t cksum; + uint8_t masksrc[IP_ADDR_LEN]; + uint8_t maskdest[IP_ADDR_LEN]; + int pktlen; + uint8_t *ptr; + + /* Move to the beginning of the IP hdeader */ + + ebuf_seek(buf,-IPHDR_LENGTH); + + pktlen = ebuf_length(buf) + IPHDR_LENGTH; + + ipi->ip_id++; + + /* Insert the IP header */ + + ebuf_put_u8(buf,IPHDR_VER_4 | IPHDR_LEN_20); + ebuf_put_u8(buf,IPHDR_TOS_DEFAULT); + ebuf_put_u16_be(buf,pktlen); + ebuf_put_u16_be(buf,ipi->ip_id); + ebuf_put_u16_be(buf,0); + ebuf_put_u8(buf,IPHDR_TTL_DEFAULT); + ebuf_put_u8(buf,proto); + ebuf_put_u16_be(buf,0); /* checksum */ + ebuf_put_bytes(buf,ipi->net_info.ip_addr,IP_ADDR_LEN); + ebuf_put_bytes(buf,destaddr,IP_ADDR_LEN); + + /* adjust pointer and add in the header length */ + + ebuf_prepend(buf,IPHDR_LENGTH); + + /* Checksum the header */ + + ptr = ebuf_ptr(buf); + cksum = ip_chksum(0,ptr,IPHDR_LENGTH); + cksum = ~cksum; + ptr[10] = (cksum >> 8) & 0xFF; + ptr[11] = (cksum >> 0) & 0xFF; + + /* + * If sending to the IP broadcast address, + * send to local broadcast. + */ + + if (ip_addrisbcast(destaddr)) { + eth_send(buf,(uint8_t *) eth_broadcast); + eth_free(buf); + return 0; + } + + /* + * If the mask has not been set, don't try to + * determine if we should use the gateway or not. + */ + + if (ip_addriszero(ipi->net_info.ip_netmask)) { + return _arp_lookup_and_send(ipi,buf,destaddr); + } + + /* + * Compute (dest-addr & netmask) and (my-addr & netmask) + */ + + ip_mask(masksrc,destaddr,ipi->net_info.ip_netmask); + ip_mask(maskdest,ipi->net_info.ip_addr,ipi->net_info.ip_netmask); + + /* + * if destination and my address are on the same subnet, + * send the packet directly. Otherwise, send via + * the gateway. + */ + + if (ip_compareaddr(masksrc,maskdest) == 0) { + return _arp_lookup_and_send(ipi,buf,destaddr); + } + else { + /* if no gw configured, drop packet */ + if (ip_addriszero(ipi->net_info.ip_gateway)) { + eth_free(buf); /* silently drop */ + return 0; + } + else { + return _arp_lookup_and_send(ipi,buf,ipi->net_info.ip_gateway); + } + } + +} + + +/* ********************************************************************* + * ip_rx_callback(buf,ref) + * + * Receive callback for IP packets. This routine is called + * by the Ethernet datalink. We look up a suitable protocol + * handler and pass the packet off. + * + * Input parameters: + * buf - ebuf we received + * ref - reference data from the ethernet datalink + * + * Return value: + * ETH_KEEP to keep the packet + * ETH_DROP to drop the packet + ********************************************************************* */ + +static int ip_rx_callback(ebuf_t *buf,void *ref) +{ + ip_info_t *ipi = ref; + uint8_t tmp; + int hdrlen; + uint8_t *hdr; + uint16_t origchksum; + uint16_t calcchksum; + uint16_t length; + uint16_t tmp16; + uint8_t proto; + uint8_t srcip[IP_ADDR_LEN]; + uint8_t dstip[IP_ADDR_LEN]; + ip_protodisp_t *pdisp; + int res; + int idx; + + hdr = ebuf_ptr(buf); /* save current posn */ + + ebuf_get_u8(buf,tmp); /* version and header length */ + + /* + * Check IP version + */ + + if ((tmp & 0xF0) != IPHDR_VER_4) { + goto drop; /* not IPV4 */ + } + hdrlen = (tmp & 0x0F) * 4; + + /* + * Check header size + */ + + if (hdrlen < IPHDR_LENGTH) { + goto drop; /* header < 20 bytes */ + } + + /* + * Check the checksum + */ + origchksum = ((uint16_t) hdr[10] << 8) | (uint16_t) hdr[11]; + hdr[10] = hdr[11] = 0; + calcchksum = ~ip_chksum(0,hdr,hdrlen); + if (calcchksum != origchksum) { + goto drop; + } + + /* + * Okay, now go back and check other fields. + */ + + ebuf_skip(buf,1); /* skip TOS field */ + + ebuf_get_u16_be(buf,length); + ebuf_skip(buf,2); /* skip ID field */ + + ebuf_get_u16_be(buf,tmp16); /* get Fragment Offset field */ + + /* + * If the fragment offset field is nonzero, or the + * "more fragments" bit is set, then this is a packet + * fragment. Our trivial IP implementation does not + * deal with fragments, so drop the packets. + */ + if ((tmp16 & (IPHDR_FRAGOFFSET | IPHDR_MOREFRAGMENTS)) != 0) { + goto drop; /* packet is fragmented */ + } + + ebuf_skip(buf,1); /* skip TTL */ + ebuf_get_u8(buf,proto); /* get protocol */ + ebuf_skip(buf,2); /* skip checksum */ + + ebuf_get_bytes(buf,srcip,IP_ADDR_LEN); + ebuf_get_bytes(buf,dstip,IP_ADDR_LEN); + + ebuf_skip(buf,hdrlen-IPHDR_LENGTH); /* skip rest of header */ + + ebuf_setlength(buf,length-hdrlen); /* set length to just data portion */ + + /* + * If our address is not set, let anybody in. We need this to + * properly pass up DHCP replies that get forwarde through routers. + * Otherwise, only let in matching addresses or broadcasts. + */ + + if (!ip_addriszero(ipi->net_info.ip_addr)) { + if ((ip_compareaddr(dstip,ipi->net_info.ip_addr) != 0) && + !(ip_addrisbcast(dstip))) { + goto drop; /* not for us */ + } + } + + /* + * ebuf's pointer now starts at beginning of protocol data + */ + + /* + * Find matching protocol dispatch + */ + + pdisp = ipi->ip_protocols; + res = ETH_DROP; + for (idx = 0; idx < IP_MAX_PROTOCOLS; idx++) { + if (pdisp->cb && (pdisp->protocol == proto)) { + res = (*(pdisp->cb))(pdisp->ref,buf,dstip,srcip); + break; + } + pdisp++; + } + + + return res; + +drop: + return ETH_DROP; +} + + +/* ********************************************************************* + * _ip_init(eth) + * + * Initialize the IP layer, attaching it to an underlying Ethernet + * datalink interface. + * + * Input parameters: + * eth - Ethernet datalink information + * + * Return value: + * ip_info pointer (IP stack information) or NULL if error + ********************************************************************* */ + +ip_info_t *_ip_init(ether_info_t *eth) +{ + ip_info_t *ipi; + int8_t ipproto[2]; + + /* + * Allocate IP stack info + */ + + ipi = KMALLOC(sizeof(ip_info_t),0); + if (ipi == NULL) return NULL; + + memset(ipi,0,sizeof(ip_info_t)); + + ipi->eth_info = eth; + + /* + * Initialize ARP + */ + + if (_arp_init(ipi) < 0) { + KFREE(ipi); + return NULL; + } + + /* + * Open the Ethernet portal for IP packets + */ + + ipproto[0] = (PROTOSPACE_IP >> 8) & 0xFF; ipproto[1] = PROTOSPACE_IP & 0xFF; + ipi->ip_port = eth_open(ipi->eth_info,ETH_PTYPE_DIX,ipproto,ip_rx_callback,ipi); + + if (ipi->ip_port < 0) { + _arp_uninit(ipi); + KFREE(ipi); + return NULL; + } + + return ipi; +} + + +/* ********************************************************************* + * _ip_uninit(ipi) + * + * Un-initialize the IP layer, freeing resources + * + * Input parameters: + * ipi - IP stack information + * + * Return value: + * nothing + ********************************************************************* */ + +void _ip_uninit(ip_info_t *ipi) +{ + /* + * Close the IP portal + */ + + eth_close(ipi->eth_info,ipi->ip_port); + + /* + * Turn off the ARP layer. + */ + + _arp_uninit(ipi); + + + /* + * free strings containing the domain and host names + */ + + if (ipi->net_info.ip_domain) { + KFREE(ipi->net_info.ip_domain); + } + + if (ipi->net_info.ip_hostname) { + KFREE(ipi->net_info.ip_hostname); + } + + /* + * Free the stack information + */ + + KFREE(ipi); +} + + +/* ********************************************************************* + * _ip_timer_tick(ipi) + * + * Called once per second while the IP stack is active. + * + * Input parameters: + * ipi - ip stack information + * + * Return value: + * nothing + ********************************************************************* */ + + +void _ip_timer_tick(ip_info_t *ipi) +{ + _arp_timer_tick(ipi); +} + + +/* ********************************************************************* + * _ip_free(ipi,buf) + * + * Free an ebuf allocated via _ip_alloc + * + * Input parameters: + * ipi - IP stack information + * buf - ebuf to free + * + * Return value: + * nothing + ********************************************************************* */ + +void _ip_free(ip_info_t *ipi,ebuf_t *buf) +{ + eth_free(buf); +} + +/* ********************************************************************* + * _ip_getaddr(ipi,buf) + * + * Return our IP address (is this used?) + * + * Input parameters: + * ipi - IP stack information + * buf - pointer to 4-byte buffer to receive IP address + * + * Return value: + * nothing + ********************************************************************* */ + +void _ip_getaddr(ip_info_t *ipi,uint8_t *buf) +{ + memcpy(buf,ipi->net_info.ip_addr,IP_ADDR_LEN); +} + +/* ********************************************************************* + * _ip_getparam(ipi,param) + * + * Return the value of an IP parameter (address, netmask, etc.). + * The return value may need to be coerced if it's not normally + * a uint8_t* pointer. + * + * Input parameters: + * ipi - IP stack information + * param - parameter number + * + * Return value: + * parameter value, or NULL if the parameter is invalid or + * not set. + ********************************************************************* */ + +uint8_t *_ip_getparam(ip_info_t *ipinfo,int param) +{ + uint8_t *ret = NULL; + + switch (param) { + case NET_IPADDR: + ret = ipinfo->net_info.ip_addr; + break; + case NET_NETMASK: + ret = ipinfo->net_info.ip_netmask; + break; + case NET_GATEWAY: + ret = ipinfo->net_info.ip_gateway; + break; + case NET_NAMESERVER: + ret = ipinfo->net_info.ip_nameserver; + break; + case NET_HWADDR: + ret = ipinfo->arp_hwaddr; + break; + case NET_DOMAIN: + ret = ipinfo->net_info.ip_domain; + break; + case NET_HOSTNAME: + ret = ipinfo->net_info.ip_hostname; + break; + case NET_SPEED: + return NULL; + break; + case NET_LOOPBACK: + return NULL; + break; + } + + return ret; +} + +/* ********************************************************************* + * _ip_getparam(ipi,param,value) + * + * Set the value of an IP parameter (address, netmask, etc.). + * The value may need to be coerced if it's not normally + * a uint8_t* pointer. + * + * Input parameters: + * ipi - IP stack information + * param - parameter number + * value - parameter's new value + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +int _ip_setparam(ip_info_t *ipinfo,int param,uint8_t *ptr) +{ + int res = -1; + + switch (param) { + case NET_IPADDR: + memcpy(ipinfo->net_info.ip_addr,ptr,IP_ADDR_LEN); + _arp_send_gratuitous(ipinfo); + res = 0; + break; + case NET_NETMASK: + memcpy(ipinfo->net_info.ip_netmask,ptr,IP_ADDR_LEN); + res = 0; + break; + case NET_GATEWAY: + memcpy(ipinfo->net_info.ip_gateway,ptr,IP_ADDR_LEN); + res = 0; + break; + case NET_NAMESERVER: + memcpy(ipinfo->net_info.ip_nameserver,ptr,IP_ADDR_LEN); + res = 0; + break; + case NET_DOMAIN: + if (ipinfo->net_info.ip_domain) { + KFREE(ipinfo->net_info.ip_domain); + ipinfo->net_info.ip_domain = NULL; + } + if (ptr) ipinfo->net_info.ip_domain = (uint8_t*)strdup((char *) ptr); + break; + case NET_HOSTNAME: + if (ipinfo->net_info.ip_hostname) { + KFREE(ipinfo->net_info.ip_hostname); + ipinfo->net_info.ip_hostname = NULL; + } + if (ptr) ipinfo->net_info.ip_hostname = (uint8_t*)strdup((char *) ptr); + break; + case NET_HWADDR: + memcpy(ipinfo->arp_hwaddr,ptr,ENET_ADDR_LEN); + eth_sethwaddr(ipinfo->eth_info,ptr); + res = 0; + break; + case NET_SPEED: + res = eth_setspeed(ipinfo->eth_info,*(int *)ptr); + break; + case NET_LOOPBACK: + res = eth_setloopback(ipinfo->eth_info,*(int *)ptr); + break; + } + + return res; +} + +/* ********************************************************************* + * _ip_register(ipinfo,proto,cb) + * + * Register a protocol handler with the IP layer. IP client + * protocols such as UDP, ICMP, etc. call this to register their + * callbacks. + * + * Input parameters: + * ipinfo - IP stack information + * proto - IP protocol number + * cb - callback routine to register + * + * Return value: + * nothing + ********************************************************************* */ + +void _ip_register(ip_info_t *ipinfo, + int proto, + int (*cb)(void *ref,ebuf_t *buf,uint8_t *dst,uint8_t *src),void *ref) +{ + int idx; + + for (idx = 0; idx < IP_MAX_PROTOCOLS; idx++) { + if (ipinfo->ip_protocols[idx].cb == NULL) break; + } + + if (idx == IP_MAX_PROTOCOLS) return; + + ipinfo->ip_protocols[idx].protocol = (uint8_t) proto; + ipinfo->ip_protocols[idx].cb = cb; + ipinfo->ip_protocols[idx].ref = ref; +} + + +/* ********************************************************************* + * _ip_deregister(ipinfo,proto) + * + * Deregister an IP protocol. + * + * Input parameters: + * ipinfo - IP stack information + * proto - protocol number + * + * Return value: + * nothing + ********************************************************************* */ + +void _ip_deregister(ip_info_t *ipinfo,int proto) +{ + int idx; + + for (idx = 0; idx < IP_MAX_PROTOCOLS; idx++) { + if (ipinfo->ip_protocols[idx].protocol == (uint8_t) proto) { + ipinfo->ip_protocols[idx].protocol = 0; + ipinfo->ip_protocols[idx].ref = 0; + ipinfo->ip_protocols[idx].cb = NULL; + } + } +} + diff --git a/cfe/cfe/net/net_ip.h b/cfe/cfe/net/net_ip.h new file mode 100755 index 0000000..7b1b4be --- /dev/null +++ b/cfe/cfe/net/net_ip.h @@ -0,0 +1,159 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * IP Protocol Definitions File: net_ip.h + * + * This is the main include file for the CFE network stack. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + + +/* ********************************************************************* + * Global info + ********************************************************************* */ + +#ifndef IP_ADDR_LEN +#define IP_ADDR_LEN 4 +#endif + +typedef struct ip_info_s ip_info_t; + +typedef struct net_info_s { + /* Configuration info for IP interface */ + uint8_t ip_addr[IP_ADDR_LEN]; + uint8_t ip_netmask[IP_ADDR_LEN]; + uint8_t ip_gateway[IP_ADDR_LEN]; + uint8_t ip_nameserver[IP_ADDR_LEN]; + uint8_t *ip_domain; + uint8_t *ip_hostname; +} net_info_t; + +/* ********************************************************************* + * ARP Information + ********************************************************************* */ + +int _arp_lookup_and_send(ip_info_t *ipi,ebuf_t *buf,uint8_t *dest); +void _arp_timer_tick(ip_info_t *ipi); +int _arp_init(ip_info_t *ipi); +void _arp_uninit(ip_info_t *ipi); +void _arp_add(ip_info_t *ipi,uint8_t *destip,uint8_t *desthw); +void _arp_send_gratuitous(ip_info_t *ipi); +uint8_t *_arp_lookup(ip_info_t *ipi,uint8_t *destip); +int _arp_enumerate(ip_info_t *ipi,int entrynum,uint8_t *ipaddr,uint8_t *hwaddr); +int _arp_delete(ip_info_t *ipi,uint8_t *ipaddr); + + +/* ********************************************************************* + * IP protocol + ********************************************************************* */ + + +#define IPPROTO_TCP 6 +#define IPPROTO_UDP 17 +#define IPPROTO_ICMP 1 + +int _ip_send(ip_info_t *ipi,ebuf_t *buf,uint8_t *destaddr,uint8_t proto); +ip_info_t *_ip_init(ether_info_t *); +void _ip_timer_tick(ip_info_t *ipi); +void _ip_uninit(ip_info_t *ipi); +ebuf_t *_ip_alloc(ip_info_t *ipi); +void _ip_free(ip_info_t *ipi,ebuf_t *buf); +void _ip_getaddr(ip_info_t *ipi,uint8_t *buf); +uint8_t *_ip_getparam(ip_info_t *ipinfo,int param); +int _ip_setparam(ip_info_t *ipinfo,int param,uint8_t *ptr); +uint16_t ip_chksum(uint16_t initcksum,uint8_t *ptr,int len); +void _ip_deregister(ip_info_t *ipinfo,int proto); +void _ip_register(ip_info_t *ipinfo, + int proto, + int (*cb)(void *ref,ebuf_t *buf,uint8_t *dst,uint8_t *src),void *ref); + + +#define ip_mask(dest,a,b) (dest)[0] = (a)[0] & (b)[0] ; \ + (dest)[1] = (a)[1] & (b)[1] ; \ + (dest)[2] = (a)[2] & (b)[2] ; \ + (dest)[3] = (a)[3] & (b)[3] + +#define ip_compareaddr(a,b) memcmp(a,b,IP_ADDR_LEN) + +#define ip_addriszero(a) (((a)[0]|(a)[1]|(a)[2]|(a)[3]) == 0) +#define ip_addrisbcast(a) ((a[0] == 0xFF) && (a[1] == 0xFF) && (a[2] == 0xFF) && (a[3] == 0xFF)) + +#ifndef NET_IPADDR +#define NET_IPADDR 0 +#define NET_NETMASK 1 +#define NET_GATEWAY 2 +#define NET_NAMESERVER 3 +#define NET_HWADDR 4 +#define NET_DOMAIN 5 +#define NET_HOSTNAME 6 +#define NET_SPEED 7 +#define NET_LOOPBACK 8 +#endif + +/* ********************************************************************* + * UDP Protocol + ********************************************************************* */ + +typedef struct udp_info_s udp_info_t; + +int _udp_socket(udp_info_t *info,uint16_t port); +void _udp_close(udp_info_t *info,int s); +int _udp_send(udp_info_t *info,int s,ebuf_t *buf,uint8_t *dest); +udp_info_t *_udp_init(ip_info_t *ipi,void *ref); +void _udp_uninit(udp_info_t *info); +int _udp_bind(udp_info_t *info,int s,uint16_t port); +int _udp_connect(udp_info_t *info,int s,uint16_t port); +ebuf_t *_udp_recv(udp_info_t *info,int s); +ebuf_t *_udp_alloc(udp_info_t *info); +void _udp_free(udp_info_t *info,ebuf_t *buf); + +/* ********************************************************************* + * ICMP protocol + ********************************************************************* */ + +typedef struct icmp_info_s icmp_info_t; + +icmp_info_t *_icmp_init(ip_info_t *ipi); +void _icmp_uninit(icmp_info_t *icmp); + +int _icmp_ping(icmp_info_t *icmp,uint8_t *ipaddr,int seq,int len); + + diff --git a/cfe/cfe/net/net_ip_internal.h b/cfe/cfe/net/net_ip_internal.h new file mode 100644 index 0000000..94401e2 --- /dev/null +++ b/cfe/cfe/net/net_ip_internal.h @@ -0,0 +1,128 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * Internal IP structures File: net_ip_internal.h + * + * This module contains non-public IP stack constants, + * structures, and function prototypes. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +/* ********************************************************************* + * ARP Protocol + ********************************************************************* */ + +#define ARP_OPCODE_REQUEST 1 +#define ARP_OPCODE_REPLY 2 + +#define ARP_HWADDRSPACE_ETHERNET 1 + +#define PROTOSPACE_IP 0x0800 +#define PROTOSPACE_ARP 0x0806 + +/* Foxconn add start by Cliff Wang, 03/23/2010 */ +#define PROTOSPACE_NMRP 0x0912 +/* Foxconn add end by Cliff Wang, 03/23/2010 */ + +#define ARP_KEEP_TIMER 60 +#define ARP_QUERY_TIMER 1 +#define ARP_QUERY_RETRIES 4 +#define ARP_TXWAIT_MAX 2 +#define ARP_TABLE_SIZE 8 + +typedef enum { ae_unused, ae_arping, ae_established } arpstate_t; + +typedef struct arpentry_s { + arpstate_t ae_state; + int ae_usage; + int ae_timer; + int ae_retries; + int ae_permanent; + uint8_t ae_ipaddr[IP_ADDR_LEN]; + uint8_t ae_ethaddr[ENET_ADDR_LEN]; + queue_t ae_txqueue; +} arpentry_t; + + +/* ********************************************************************* + * IP Protocol + ********************************************************************* */ + +#define IPHDR_VER_4 0x40 +#define IPHDR_LEN_20 0x05 +#define IPHDR_LENGTH 20 +#define IPHDR_TOS_DEFAULT 0x00 +#define IPHDR_TTL_DEFAULT 100 + +#define IPHDR_RESERVED 0x8000 +#define IPHDR_DONTFRAGMENT 0x4000 +#define IPHDR_MOREFRAGMENTS 0x2000 +#define IPHDR_FRAGOFFSET 0x01FFF + +typedef struct ip_protodisp_s { + uint8_t protocol; + int (*cb)(void *ref,ebuf_t *buf,uint8_t *dst,uint8_t *src); + void *ref; +} ip_protodisp_t; + +#define IP_MAX_PROTOCOLS 4 + + +struct ip_info_s { + net_info_t net_info; + + /* Ethernet info */ + ether_info_t *eth_info; + + /* Info specific to IP */ + uint16_t ip_id; + int ip_port; + + /* IP protocol dispatch table */ + ip_protodisp_t ip_protocols[IP_MAX_PROTOCOLS]; + + /* Info specific to ARP */ + arpentry_t *arp_table; + int arp_port; + uint8_t arp_hwaddr[ENET_ADDR_LEN]; +}; + + diff --git a/cfe/cfe/net/net_nmrp.c b/cfe/cfe/net/net_nmrp.c new file mode 100755 index 0000000..696d04d --- /dev/null +++ b/cfe/cfe/net/net_nmrp.c @@ -0,0 +1,656 @@ +#include "bsp_config.h" + +#include "lib_types.h" +#include "lib_string.h" +#include "lib_queue.h" +#include "lib_malloc.h" +#include "lib_printf.h" + +#include "cfe_iocb.h" +#include "cfe_devfuncs.h" +#include "cfe_ioctl.h" +#include "cfe_timer.h" + +#include "cfe_error.h" + +#include "net_ebuf.h" +#include "net_ether.h" + +#include "cfe_timer.h" + +#include "net_ip.h" +#include "net_ip_internal.h" +#include "net_api.h" + +#include "env_subr.h" + +#if CFG_TCP +#include "net_tcp.h" +#endif + +#include "ui_command.h" + +typedef struct net_ctx_s { + /* Global info */ + int64_t timer; + + /* device name */ + char *devname; + + /* Run-time info for IP interface */ + ip_info_t *ipinfo; + + /* Info for Ethernet interface */ + ether_info_t *ethinfo; + + /* Info specific to UDP */ + udp_info_t *udpinfo; + + /* Info specific to ICMP */ + icmp_info_t *icmpinfo; + +#if CFG_TCP + /* Info specific to TCP */ + tcp_info_t *tcpinfo; +#endif +} net_ctx_t; + + +#pragma pack(1) + +typedef uint8_t BYTE; +typedef uint16_t WORD; +typedef uint32_t DWORD; + +/* NMRP timeouts */ +enum _nmrp_timeouts_ { + NMRP_TIMEOUT_REQ = 1/2, /* 0.5 sec */ + NMRP_TIMEOUT_LISTEN = 3, /* 3 sec */ + NMRP_TIMEOUT_ACTIVE = 60000, /* 1 minute */ + NMRP_TIMEOUT_CLOSE = 6000, /* 6 sec */ + NMRP_TIMEOUT_ADVERTISE = 500 /* 0.5 sec */ +}; + +enum _nmrp_errors_ { + NMRP_ERR_NONE = 0, + NMRP_ERR_MSG_INVALID_LEN, + NMRP_ERR_MSG_TOO_LONG, + NMRP_ERR_MSG_TOO_MANY_OPT, + NMRP_ERR_MSG_UNKNOWN_OPT, + NMRP_ERR_MSG_INVALID_OPT, + NMRP_ERR_NO_BUF, + NMRP_ERR_GENERAL +}; + +/* NMRP codes */ +enum _nmrp_codes_ { + NMRP_CODE_ADVERTISE = 0x01, + NMRP_CODE_CONF_REQ = 0x02, + NMRP_CODE_CONF_ACK = 0x03, + NMRP_CODE_CLOSE_REQ = 0x04, + NMRP_CODE_CLOSE_ACK = 0x05, + NMRP_CODE_KEEP_ALIVE_REQ = 0x06, + NMRP_CODE_KEEP_ALIVE_ACK = 0x07, + NMRP_CODE_TFTP_UL_REQ = 0x10 +}; + +/* NMRP option types */ +enum _nmrp_option_types_ { + NMRP_OPT_MAGIC_NO = 0x0001, + NMRP_OPT_DEV_IP = 0x0002, + NMRP_OPT_FW_UP = 0x0101 +}; + +typedef enum { + NMRPC_L2_NO_ERROR = 0, + NMRPC_L2_TX_PKT_TOO_LONG, + NMRPC_L2_TX_ETHER_PREPEND_FAIL, + NMRPC_L2_TX_ERROR, + NMRPC_L2_GEN_ERR +} NMRPC_L2_RET_CODE; + +/* NMRP REQ max retries */ +enum _nmrp_req_max_retries_ { + NMRP_MAX_RETRY_CONF = 5, + NMRP_MAX_RETRY_CLOSE = 9999, + NMRP_MAX_RETRY_TFTP_UL = 4 +}; + + +enum _nmrp_state_ { + NMRP_STATE_LISTEN = 0, + NMRP_STATE_CONFIGURE, + NMRP_STATE_TRANSFER, + NMRP_STATE_CLOSE +}; + +#define NMRP_MAX_OPT_PER_MSG (6) +#define ETHER_NMRP (0x0912) + +#define NMRP_HDR_LEN (sizeof(NMRP_MSG) - sizeof(NMRP_OPT)) +#define NMRP_MIN_OPT_LEN (sizeof(NMRP_OPT) - 1) + +#define NMRP_MAGIC_NO "\x4E\x54\x47\x52" +#define NMRP_MAGIC_NO_LEN (sizeof(NMRP_MAGIC_NO) - 1) + +typedef struct { + uint8_t destMAC[6]; + uint8_t srcMAC[6]; + uint16_t etherType; +} ETHER_HDR; + +typedef struct { + uint16_t type; + uint16_t len; + uint8_t value; +} NMRP_OPT; + +typedef struct { + uint16_t reserved; + uint8_t code; + uint8_t id; + uint16_t length; + + NMRP_OPT opt; +} NMRP_MSG; + +typedef struct { + ETHER_HDR etherHdr; + NMRP_MSG nmrpMsg; +} ETHER_NMRP_PKT; + +typedef struct { + uint16_t type; + uint16_t len; + + union { + uint8_t byteVal; + uint16_t wordVal; + uint32_t dwordVal; + const uint8_t *streamVal; + } value; +} NMRP_PARSED_OPT; + +typedef struct { + uint8_t code; + uint8_t id; + uint16_t length; + + int numOptions; + NMRP_PARSED_OPT options[NMRP_MAX_OPT_PER_MSG]; +} NMRP_PARSED_MSG; + +typedef struct { + int reqCode; + const char reqName[32], respName[32]; + + int (* RespParser) (const void *pkt, int pktLen, const BYTE *serverMac, void *userData); +} NMRP_REQ_CUSTOM; + +typedef struct { + + BYTE peerMac[6]; + BYTE myMac[6]; + + DWORD peerIp; + DWORD myIp; + WORD ipId; + + WORD peerPort; + WORD myPort; +} NET_INFO; + +#pragma pack() + +#define MIN_ETHER_NMRP_LEN (sizeof(ETHER_HDR) + NMRP_HDR_LEN) + +#if 0 +#define HTONCS(constWord) ((((constWord) & 0xff00) >> 8) | (((constWord) & 0xff) << 8)) +#define NTOHCS(constWord) HTONCS(constWord) +#define HTONCL(constDword) ((((constDword) & 0xff000000) >> 24) | (((constDword) & 0xff0000) >> 8) | (((constDword) & 0xff00) << 8) | (((constDword) & 0xff) << 24)) +#define NTOHCL(constDword) HTONCL(constDword) +#endif + +#define HTONCS(constWord) constWord +#define NTOHCS(constWord) constWord +#define HTONCL(constDword) constDword +#define NTOHCL(constDword) constDword +/* +#define IS_NMRP_PKT(etherNmrpPkt, pktLen, parsedNmrpMsg, sourceMAC) \ + ((pktLen) > MIN_ETHER_NMRP_LEN && (etherNmrpPkt)->etherHdr.etherType == HTONCS(ETHER_NMRP) && \ + ((sourceMAC) == NULL || memcmp((sourceMAC), (etherNmrpPkt)->etherHdr.srcMAC, 6) == 0) && \ + NMRP_MsgParsing(&(etherNmrpPkt)->nmrpMsg, (pktLen) - sizeof(ETHER_HDR), (parsedNmrpMsg)) == NMRP_ERR_NONE) +*/ + +#define IS_NMRP_PKT(etherNmrpPkt, pktLen, parsedNmrpMsg, sourceMAC) \ + ((pktLen) > MIN_ETHER_NMRP_LEN && (etherNmrpPkt)->etherHdr.etherType == HTONCS(ETHER_NMRP) && \ + ((sourceMAC) == NULL || memcmp((sourceMAC), (etherNmrpPkt)->etherHdr.srcMAC, 6) == 0)) + +static int nmrp_rx_callback(ebuf_t *buf,void *ref); +int NMRP_MsgParsing(const NMRP_MSG *pkt, int pktLen, NMRP_PARSED_MSG *msg); +NMRP_PARSED_OPT *NMRP_MsgGetOpt(NMRP_PARSED_MSG *msg, uint16_t optType); +//int NMRPRequest( const BYTE *serverMac, const BYTE *clientMac, NMRP_REQ_CUSTOM *custom, void *userData); +static int NMRPConfiguring( const BYTE *serverMac, const BYTE *clientMac, DWORD *ipAddr, DWORD *ipSubnetMask, int *fwUpgrade); +static int NMRPConfAckParser(const void *pkt, int pktLen, const BYTE *serverMac, void *userData); +//int NMRPTFTPWaiting(const NET_INFO *netInfo, DWORD *peerIp, WORD *peerPort, WORD *ipId); +int NMRPTFTPWaiting(void); +int NMRPKeepAlive(void); +static int NMRPClosing(void); +static int NMRPSend(NMRP_REQ_CUSTOM *custom); + +int nmrp_server_detected; +int g_nmrp_config_acked; +int g_nmrp_close_acked; +//int g_Listening; +int g_NMRP_State; +extern net_ctx_t *netctx; + +NET_INFO g_NetInfo; +//ebuf_t *g_nmrp_txbuf = NULL; +int g_portal_number; +NMRP_PARSED_MSG g_nmrpMsg; +int g_tftp_upgrade_success; +int g_nmrp_keepalive;//jenny add for fixed NMRP server timeout issue +unsigned char g_received_packet[1600]; + +int gpio_control(int gpio, int value); +extern int32_t _getticks(void); /* return value of CP0 COUNT */ +static unsigned long seed = 1; + +void srand(unsigned long local_seed); +unsigned long rand(void); +int _start_nmrp(void); + +void srand(unsigned long local_seed) +{ + seed = local_seed; +} + +unsigned long rand(void) +{ + + long x, hi, lo, t; + + x = seed; + hi = x / 127773; + lo = x % 127773; + t = 16807 * lo - 2836 * hi; + if (t <= 0) t += 0x7fffffff; + seed = t; + return t; +} + +int _start_nmrp (void) +{ + uint8_t nmrpproto[2]; + + int ret = 0; + int ui_cmd_status; + int fwUpgrade; + int64_t timer; + DWORD ipSubnetMask; + char buf[512]; + unsigned char *pIP, *pMask; + + srand( (unsigned long)_getticks()); + + //net_init("eth0"); + nmrpproto[0] = (PROTOSPACE_NMRP >> 8) & 0xFF; + nmrpproto[1] = (PROTOSPACE_NMRP & 0xFF); + g_tftp_upgrade_success = 0; + g_nmrp_keepalive = 0;//jenny add for timeout + + g_portal_number = eth_open(netctx->ethinfo, ETH_PTYPE_DIX, (char*)nmrpproto, nmrp_rx_callback, NULL); + if(g_portal_number < 0) + { + ret = -1; + goto _clean_and_exit; + } + + eth_gethwaddr(netctx->ethinfo, g_NetInfo.myMac); +//_retry: + g_NMRP_State = NMRP_STATE_LISTEN; + nmrp_server_detected = 0; + TIMER_SET(timer, NMRP_TIMEOUT_LISTEN*CFE_HZ); + while (!TIMER_EXPIRED(timer)) + { + POLL(); + if(nmrp_server_detected) + { + xprintf ("find server.\n"); + break; + } + } + + if(!nmrp_server_detected) + { + ret = -1; + goto _clean_and_exit; + } + + g_nmrp_config_acked = 0; + + if (NMRPConfiguring( g_NetInfo.peerMac, g_NetInfo.myMac, &g_NetInfo.myIp, &ipSubnetMask, &fwUpgrade) == -1) + { + ret = -1; + goto _clean_and_exit; + } + + pIP = (unsigned char *)&g_NetInfo.myIp; + pMask = (unsigned char *)&ipSubnetMask; + + sprintf(buf, "ifconfig eth0 -addr=%u.%u.%u.%u -mask=%u.%u.%u.%u", *(pIP), *(pIP+1), *(pIP+2), *(pIP+3), + *(pMask), *(pMask+1), *(pMask+2), *(pMask+3) ); + ui_docommand(buf); + + +_retry_tftp: + + NMRPTFTPWaiting(); + //eth_close(netctx->ethinfo, g_portal_number); + //g_portal_number = 0; + //net_init("eth0"); + ui_cmd_status = ui_docommand("tftpd nmrp"); + + + sprintf(buf, "ifconfig eth0 -addr=%u.%u.%u.%u -mask=%u.%u.%u.%u", *(pIP), *(pIP+1), *(pIP+2), *(pIP+3), + *(pMask), *(pMask+1), *(pMask+2), *(pMask+3) ); + + ui_docommand(buf); + + //net_init("eth0"); + g_portal_number = eth_open(netctx->ethinfo, ETH_PTYPE_DIX, (char*)nmrpproto, nmrp_rx_callback, NULL); + + if(ui_cmd_status != 0) + { + goto _retry_tftp; + } + + g_nmrp_close_acked = 0; + /* Foxconn modified start, zacker,04/07/2008 */ + /* ui_docommand("nvram erase"); */ + // nvram_unset("restore_defaults"); + // nvram_commit(); + /* Foxconn modified end, zacker,04/07/2008 */ + + NMRPClosing(); + g_tftp_upgrade_success = 1; + // gpio_control(7, 1); //turn off power led + +_clean_and_exit: + + if(g_portal_number > 0) + eth_close(netctx->ethinfo, g_portal_number); + + return ret; +} + +extern void setPowerOnLedOn(void); +static int nmrp_rx_callback(ebuf_t *buf,void *ref) +{ + int pktLen = 0; + NMRP_PARSED_MSG nmrpMsg; + const ETHER_NMRP_PKT *pkt; + unsigned char *p; + + pkt = (const ETHER_NMRP_PKT *)buf->eb_data; + pktLen = buf->eb_length + sizeof(ETHER_HDR) - 4; + + p=(unsigned char *)pkt; + + if(g_NMRP_State == NMRP_STATE_LISTEN) + { + if (IS_NMRP_PKT(pkt, pktLen, &nmrpMsg, NULL) && (*(p+16) == NMRP_CODE_ADVERTISE ) ) + { + if (NMRP_MsgParsing(&(pkt)->nmrpMsg, (pktLen) - sizeof(ETHER_HDR), &nmrpMsg) == NMRP_ERR_NONE) + { + if(NMRP_MsgGetOpt(&nmrpMsg, NMRP_OPT_MAGIC_NO) != NULL) + { + memcpy(g_NetInfo.peerMac, buf->eb_data+6, 6); + g_NMRP_State = NMRP_STATE_CONFIGURE; + nmrp_server_detected = 1; + setPowerOnLedOn(); /* Foxconn added for U12H154, turn on TEST LED */ + } + } + } + } + else if(g_NMRP_State == NMRP_STATE_CONFIGURE) + { + if (IS_NMRP_PKT(pkt, pktLen, &nmrpMsg, NULL) && (*(p+16) == NMRP_CODE_CONF_ACK ) ) + { + memcpy(g_received_packet, pkt, pktLen); + pkt = (const ETHER_NMRP_PKT *)g_received_packet; + if (NMRP_MsgParsing(&(pkt)->nmrpMsg, (pktLen) - sizeof(ETHER_HDR), &nmrpMsg) == NMRP_ERR_NONE) + { + if(NMRPConfAckParser(pkt, pktLen, g_NetInfo.peerMac, &nmrpMsg)) + { + g_nmrpMsg = nmrpMsg; + g_nmrp_config_acked = 1; + g_NMRP_State = NMRP_STATE_TRANSFER; + } + } + } + } + else if(g_NMRP_State == NMRP_STATE_TRANSFER) + { + if (IS_NMRP_PKT(pkt, pktLen, &nmrpMsg, NULL) && (*(p+16) == NMRP_CODE_CLOSE_ACK ) ) + { + if (NMRP_MsgParsing(&(pkt)->nmrpMsg, (pktLen) - sizeof(ETHER_HDR), &nmrpMsg) == NMRP_ERR_NONE) + { + g_NMRP_State =NMRP_STATE_CLOSE; + g_nmrp_close_acked = 1; + } + } + } + + return ETH_DROP; +} + +static int NMRP_OptValParsing(NMRP_PARSED_OPT *optParsed, const uint8_t *value) +{ + int retVal = NMRP_ERR_NONE; + + switch (optParsed->type) { + case NMRP_OPT_MAGIC_NO: /* We require the lenght MUST be correct for the MAGIC-NO option */ + if (optParsed->len != (NMRP_MIN_OPT_LEN + NMRP_MAGIC_NO_LEN) || memcmp(value, NMRP_MAGIC_NO, NMRP_MAGIC_NO_LEN)) + { + retVal = NMRP_ERR_MSG_INVALID_OPT; + } + else + { + optParsed->value.streamVal = value; + } + break; + + case NMRP_OPT_DEV_IP: /* save the value of IP in the byte stream format, the actual IP is streamVal[0].streamVal[1].streamVal[2].streamVal[3]. */ + //if ( optParsed->len != (NMRP_MIN_OPT_LEN + 8) || !IsIPAddrValid(ntohl(*(DWORD *) value), ntohl(*(DWORD *) (value + 4))) ) + if ( optParsed->len != (NMRP_MIN_OPT_LEN + 8) ) + retVal = NMRP_ERR_MSG_INVALID_OPT; + else + optParsed->value.streamVal = value; + break; + + case NMRP_OPT_FW_UP: /* don't check the length of FW-UP option for future compatibility */ + break; + + default: + retVal = NMRP_ERR_MSG_UNKNOWN_OPT; + break; + } + + return retVal; +} + +int NMRP_MsgParsing(const NMRP_MSG *pkt, int pktLen, NMRP_PARSED_MSG *msg) +{ + if (pktLen < NMRP_HDR_LEN) + return NMRP_ERR_MSG_INVALID_LEN; + + msg->code = pkt->code; + msg->id = pkt->id; + + if ((msg->length = NTOHCS(pkt->length)) <= pktLen && msg->length >= NMRP_HDR_LEN) { + int retVal; + uint16_t optLen; + NMRP_OPT *opt; + NMRP_PARSED_OPT *optParsed; + + opt = (NMRP_OPT *)(((uint8_t *) pkt) + NMRP_HDR_LEN); + optParsed = msg->options; + msg->numOptions = 0; + + /* use the length indicated in the NMRP header */ + for (pktLen = msg->length - NMRP_HDR_LEN; pktLen >= NMRP_MIN_OPT_LEN; ) + { + optParsed->type = NTOHCS(opt->type); + optParsed->len = optLen = NTOHCS(opt->len); + + if (optParsed->len > pktLen) + return NMRP_ERR_MSG_INVALID_OPT; + + if ((retVal = NMRP_OptValParsing(optParsed, &opt->value)) == NMRP_ERR_NONE) + { + optParsed++; + + if (++msg->numOptions >= NMRP_MAX_OPT_PER_MSG) + return NMRP_ERR_MSG_TOO_MANY_OPT; + } + else if (retVal != NMRP_ERR_MSG_UNKNOWN_OPT) + { + return NMRP_ERR_MSG_INVALID_OPT; + } + + pktLen -= optLen; + opt = (NMRP_OPT *)(((uint8_t *) opt) + optLen); + } + } /* end if the msg->length is larger than the received packet length */ + + return pktLen == 0 ? NMRP_ERR_NONE : NMRP_ERR_MSG_INVALID_LEN; +} + +NMRP_PARSED_OPT *NMRP_MsgGetOpt(NMRP_PARSED_MSG *msg, uint16_t optType) +{ + NMRP_PARSED_OPT *opt, *optEnd; + + optEnd = &msg->options[msg->numOptions]; + + for (opt = msg->options; opt != optEnd; opt++) + if (opt->type == optType) + break; + + return msg->numOptions == 0 ? NULL : (opt == optEnd ? NULL : opt); +} + +static int NMRPClosing(void) +{ + int ret, retries=0; + static NMRP_REQ_CUSTOM reqClose = { NMRP_CODE_CLOSE_REQ, "CLOSE-REQ", "CLOSE-ACK", NULL }; + + do { + ret = NMRPSend(&reqClose); + if( !ret) + { + cfe_sleep(CFE_HZ/2); + if(g_nmrp_close_acked) + { + break; + } + } + } while(++retries <= NMRP_MAX_RETRY_CLOSE); + + return 0; +} + +int NMRPTFTPWaiting(void) +{ + static NMRP_REQ_CUSTOM reqTftpUl = { NMRP_CODE_TFTP_UL_REQ, "TFTP-UL-REQ", "TFTP-WRQ", NULL }; + + return NMRPSend(&reqTftpUl); +} + +/* Foxconn modify start by Jenny Zhao, 08/07/2008, for fixed timeout issue*/ +int NMRPKeepAlive(void) +{ + static NMRP_REQ_CUSTOM reqkeepalive = { NMRP_CODE_KEEP_ALIVE_REQ, "KEEPALIVE_REQ", "KEEPALIVE-ACK", NULL }; + + return NMRPSend(&reqkeepalive); +} +/* Foxconn modify end by Jenny Zhao, 08/07/2008*/ + +static int NMRPConfAckParser(const void *pkt, int pktLen, const BYTE *serverMac, void *userData) +{ + NMRP_PARSED_MSG *nmrpMsg = (NMRP_PARSED_MSG *) userData; + + return NMRP_MsgGetOpt(nmrpMsg, NMRP_OPT_DEV_IP) != NULL; + /* + return IS_NMRP_PKT((const ETHER_NMRP_PKT *)pkt, pktLen, nmrpMsg, serverMac) && + nmrpMsg->code == NMRP_CODE_CONF_ACK && + NMRP_MsgGetOpt(nmrpMsg, NMRP_OPT_DEV_IP) != NULL; + */ +} + + +static int NMRPConfiguring( const BYTE *serverMac, + const BYTE *clientMac, + DWORD *ipAddr, DWORD *ipSubnetMask, + int *fwUpgrade) +{ + //int ret, retries=0; + int ret; + NMRP_PARSED_OPT *devIp; + static NMRP_REQ_CUSTOM reqConf = { NMRP_CODE_CONF_REQ, "CONF-REQ", "CONF-ACK", NMRPConfAckParser }; + + do { + + ret = NMRPSend(&reqConf); + if( !ret) + { + cfe_sleep(CFE_HZ/2); + if(g_nmrp_config_acked) + { + break; + } + } + + //}while(++retries <= NMRP_MAX_RETRY_CLOSE); + } while(1); + + if(g_nmrp_config_acked) + { + devIp = NMRP_MsgGetOpt(&g_nmrpMsg, NMRP_OPT_DEV_IP); + *ipAddr = (* (DWORD *) devIp->value.streamVal); + *ipSubnetMask = (* (DWORD *) (devIp->value.streamVal + 4)); + *fwUpgrade = NMRP_MsgGetOpt(&g_nmrpMsg, NMRP_OPT_FW_UP) != NULL; + return 0; + } + return -1; +} + +static int NMRPSend(NMRP_REQ_CUSTOM *custom) +{ + ebuf_t *txbuf; + NMRP_MSG *msg; + unsigned long random_backoff; + + txbuf = eth_alloc(netctx->ethinfo, g_portal_number); + if (!txbuf) + { + return -1; + } + msg = (NMRP_MSG *)(txbuf->eb_data + 14); + msg->reserved = 0; + msg->code = custom->reqCode; + msg->id = 0; + msg->length = HTONCS(NMRP_HDR_LEN); /* header only, no option */ + txbuf->eb_length = NMRP_HDR_LEN; + + random_backoff = rand(); + random_backoff = random_backoff % 30; + xprintf("%s sent...Waiting for %s...\n", custom->reqName, custom->respName); + cfe_sleep(random_backoff); + eth_send(txbuf, (uint8_t *)g_NetInfo.peerMac); + eth_free(txbuf); + + return 0; +} + diff --git a/cfe/cfe/net/net_nmrp.h b/cfe/cfe/net/net_nmrp.h new file mode 100755 index 0000000..052ff19 --- /dev/null +++ b/cfe/cfe/net/net_nmrp.h @@ -0,0 +1 @@ +int _start_nmrp(void);
diff --git a/cfe/cfe/net/net_tcp.c b/cfe/cfe/net/net_tcp.c new file mode 100755 index 0000000..377c3c8 --- /dev/null +++ b/cfe/cfe/net/net_tcp.c @@ -0,0 +1,2215 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * TCP Protocol File: net_tcp.c + * + * This file contains a very simple TCP. The basic goals of this + * tcp are to be "good enough for firmware." We try to be + * correct in our protocol implementation, but not very fancy. + * In particular, we don't deal with out-of-order segments, + * we don't hesitate to copy data more then necessary, etc. + * We strive to implement important protocol features + * like slow start, nagle, etc., but even these things are + * subsetted and simplified as much as possible. + * + * Current "todo" list: + * slow start + * good testing of retransmissions, + * round-trip delay calculations + * Process received TCP options, particularly segment size + * Ignore urgent data (remove from datastream) + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +#include "lib_types.h" +#include "lib_string.h" +#include "lib_queue.h" +#include "lib_malloc.h" +#include "lib_printf.h" + +#include "net_ebuf.h" +#include "net_ether.h" + +#include "net_ip.h" +#include "net_ip_internal.h" + +#include "cfe_timer.h" + +#include "cfe_error.h" + +#include "net_tcpbuf.h" +#include "net_tcp_internal.h" +#include "net_tcp.h" + +/* ********************************************************************* + * Config + ********************************************************************* */ + +//#define _TCP_DEBUG_ + +/* ********************************************************************* + * Structures + ********************************************************************* */ + +struct tcp_info_s { + void *ti_ref; /* ref data for IP layer */ + ip_info_t *ti_ipinfo; /* IP layer handle */ + cfe_timer_t ti_fasttimer; /* 200ms timer */ + queue_t ti_tcblist; /* list of known TCBs */ + int ti_onqueue; /* number of TCBs on queue */ + uint32_t ti_iss; /* initial sequence number */ + tcb_t *ti_ports[TCP_MAX_PORTS]; /* table of active sockets */ +}; + +/* ********************************************************************* + * Forward Declarations + ********************************************************************* */ + +static void _tcp_protosend(tcp_info_t *ti,tcb_t *tcb); +static int _tcp_rx_callback(void *ref,ebuf_t *buf,uint8_t *destaddr,uint8_t *srcaddr); +static void _tcp_output(tcp_info_t *ti,tcb_t *tcb); +static void _tcp_aborttcb(tcp_info_t *ti,tcb_t *tcb); +static void _tcp_closetcb(tcp_info_t *ti,tcb_t *tcb); +static tcb_t *_tcp_find_lclport(tcp_info_t *ti,uint16_t port); +static void _tcp_freetcb(tcp_info_t *ti,tcb_t *tcb); +static void _tcp_sendctlmsg(tcp_info_t *ti,tcb_t *tcb,uint16_t flags,int timeout); + + +/* ********************************************************************* + * Macros + ********************************************************************* */ + +#ifdef _TCP_DEBUG_ +#define _tcp_setstate(tcb,state) (tcb)->tcb_state = (state); \ + printf("tcp state = " #state "\n"); +#define DEBUGMSG(x) printf x +#else +#define _tcp_setstate(tcb,state) (tcb)->tcb_state = (state); +#define DEBUGMSG(x) +#endif + +#define _tcp_preparectlmsg(tcb,flags) (tcb)->tcb_txflags = (flags) ; \ + (tcb)->tcb_flags |= TCB_FLG_SENDMSG; + +#define _tcp_canceltimers(tcb) \ + TIMER_CLEAR((tcb)->tcb_timer_retx); \ + TIMER_CLEAR((tcb)->tcb_timer_keep); \ + TIMER_CLEAR((tcb)->tcb_timer_2msl); \ + TIMER_CLEAR((tcb)->tcb_timer_pers); + + +/* ********************************************************************* + * Globals + ********************************************************************* */ + +#ifdef _TCP_DEBUG_ +int _tcp_dumpflags = 1; +#else +int _tcp_dumpflags = 0; +#endif + +/* ********************************************************************* + * _tcp_init(ipi,ref) + * + * Initialize the TCP module. We set up our data structures + * and register ourselves with the IP layer. + * + * Input parameters: + * ipi - IP information + * ref - will be passed back to IP as needed + * + * Return value: + * tcp_info_t structure, or NULL if problems + ********************************************************************* */ + +tcp_info_t *_tcp_init(ip_info_t *ipi,void *ref) +{ + tcp_info_t *ti; + int idx; + + ti = (tcp_info_t *) KMALLOC(sizeof(tcp_info_t),0); + if (!ti) return NULL; + + ti->ti_ref = ref; + ti->ti_ipinfo = ipi; + + /* + * Start the "fast" timer + */ + + TIMER_SET(ti->ti_fasttimer,TCP_FAST_TIMER); + + /* + * Initialize the TCB list + */ + + q_init(&(ti->ti_tcblist)); + + for (idx = 0; idx < TCP_MAX_PORTS; idx++) { + ti->ti_ports[idx] = NULL; + } + + ti->ti_onqueue = 0; + + /* + * Set up the initial sequence number + */ + + extern int32_t _getticks(void); /* return value of CP0 COUNT */ + ti->ti_iss = (uint32_t) _getticks(); /* XXX nonportable */ + + /* + * Register our protocol with IP + */ + + _ip_register(ipi,IPPROTO_TCP,_tcp_rx_callback,ti); + + return ti; +} + + +/* ********************************************************************* + * _tcp_uninit(info) + * + * De-initialize the TCP layer, unregistering from the IP layer. + * + * Input parameters: + * info - our tcp_info_t, from _tcp_init() + * + * Return value: + * nothing + ********************************************************************* */ + +void _tcp_uninit(tcp_info_t *info) +{ + tcb_t *tcb; + + /* + * Destroy all allocated TCBs, forcefully. + */ + + while (!q_isempty(&(info->ti_tcblist))) { + tcb = (tcb_t *) q_getfirst(&(info->ti_tcblist)); + /* tcp_freetcb removes tcb from the queue */ + _tcp_freetcb(info,tcb); + } + + /* + * Deregister with IP + */ + + _ip_deregister(info->ti_ipinfo,IPPROTO_TCP); + + /* + * Free up the info structure + */ + + KFREE(info); +} + + +/* ********************************************************************* + * _tcp_freetcb(ti,tcb) + * + * Called when the TIME_WAIT timer expires, we use this to + * free up the TCB for good. + * + * Input parameters: + * ti - tcp information + * tcb - tcb to free + * + * Return value: + * nothing + ********************************************************************* */ + +static void _tcp_freetcb(tcp_info_t *ti,tcb_t *tcb) +{ + /* + * Undo socket number + */ + + if (tcb->tcb_socknum >= 0) ti->ti_ports[tcb->tcb_socknum] = NULL; + tcb->tcb_socknum = -1; + + /* + * Remove from queue + */ + + ti->ti_onqueue--; + q_dequeue(&(tcb->tcb_qb)); + + /* + * Free buffers (could probably be done in tcb_destroy) + */ + + tmb_free(&(tcb->tcb_txbuf)); + tmb_free(&(tcb->tcb_rxbuf)); + + /* + * Free the TCB + */ + + KFREE(tcb); + +} + +/* ********************************************************************* + * _tcp_socket(info) + * + * Create a new tcp socket (a new tcb structure is allocated + * and entered into the socket table) + * + * Input parameters: + * info - tcp information + * + * Return value: + * new socket number, or <0 if an error occured + ********************************************************************* */ + +int _tcp_socket(tcp_info_t *info) +{ + int idx; + tcb_t *tcb; + + /* + * Find an empty slot. + */ + + for (idx = 0; idx < TCP_MAX_PORTS; idx++) { + if (!info->ti_ports[idx]) break; + } + + if (idx == TCP_MAX_PORTS) { + return CFE_ERR_NOHANDLES; + } + + /* + * See if we can create another TCB + */ + + if (info->ti_onqueue >= TCP_MAX_TCBS) return CFE_ERR_NOMEM; + + /* + * Allocate data structures + */ + + tcb = KMALLOC(sizeof(tcb_t),0); + if (!tcb) return CFE_ERR_NOMEM; + + memset(tcb,0,sizeof(tcb_t)); + + if (tmb_alloc(&tcb->tcb_txbuf,TCP_BUF_SIZE) < 0) goto error; + if (tmb_alloc(&tcb->tcb_rxbuf,TCP_BUF_SIZE) < 0) goto error; + + /* + * XXX Temp: our MTU is always 1400. We could/should + * XXX get this from the lower layer. + */ + + tcb->tcb_mtu = 1400; + + /* + * Default socket flags + */ + + tcb->tcb_sockflags = TCPFLG_NBIO; + + /* + * Set up initial state. Find an empty port number. + * note that the way we do this is pretty gruesome, but it will + * work for our small TCP, where the number of TCBs outstanding + * will be very small compared to the port number space. + * + * Try to look up the port number we want - if we find it, increment + * it and try again until we find an unused one. + * Stay away from ports 0..1023. + */ + + _tcp_setstate(tcb,TCPSTATE_CLOSED); + tcb->tcb_lclport = (uint16_t) (((uint16_t) cfe_ticks) + 1024); + + while (_tcp_find_lclport(info,tcb->tcb_lclport) != NULL) { + tcb->tcb_lclport++; + if (tcb->tcb_lclport == 0) tcb->tcb_lclport = 1024; + } + + /* + * Remember this socket in the table + */ + + info->ti_ports[idx] = tcb; + tcb->tcb_socknum = idx; + + info->ti_onqueue++; + q_enqueue(&(info->ti_tcblist),&(tcb->tcb_qb)); + + return idx; + +error: + tmb_free(&(tcb->tcb_txbuf)); + tmb_free(&(tcb->tcb_rxbuf)); + KFREE(tcb); + return CFE_ERR_NOMEM; +} + + +/* ********************************************************************* + * _tcp_connect(ti,s,dest,port) + * + * Connect a socket to a remote port. + * + * Input parameters: + * ti - TCP information + * s - socket number, allocated via _tcp_create + * dest - destination IP address + * port - destination port number + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int _tcp_connect(tcp_info_t *ti,int s,uint8_t *dest,uint16_t port) +{ + tcb_t *tcb; + + tcb = ti->ti_ports[s]; + if (!tcb) return CFE_ERR_INV_PARAM; + + memcpy(tcb->tcb_peeraddr,dest,IP_ADDR_LEN); + tcb->tcb_peerport = port; + + tcb->tcb_rcvnext = 0; + tcb->tcb_rcvack = 0; + + tcb->tcb_sendnext = ti->ti_iss; + tcb->tcb_sendunack = ti->ti_iss; + tcb->tcb_sendwindow = 0; + + tmb_init(&tcb->tcb_txbuf); + tmb_init(&tcb->tcb_rxbuf); + + TIMER_SET(tcb->tcb_timer_keep,TCP_KEEPALIVE_TIMER); + _tcp_setstate(tcb,TCPSTATE_SYN_SENT); + + _tcp_sendctlmsg(ti,tcb,TCPFLG_SYN,TCP_RETX_TIMER); + + return 0; +} + + +/* ********************************************************************* + * _tcp_close(ti,s) + * + * Disconnect a TCP socket nicely. Sends a FIN packet to get + * us into the disconnect state. + * + * Input parameters: + * ti - tcp information + * s - socket number + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int _tcp_close(tcp_info_t *ti,int s) +{ + tcb_t *tcb; + + tcb = ti->ti_ports[s]; + if (!tcb) return CFE_ERR_INV_PARAM; + +#if defined(CONFIG_MIPS_BRCM) + /* + * Flush output data. + */ + + _tcp_output(ti,tcb); +#endif + + /* + * Reclaim this socket number for future use + */ + + ti->ti_ports[s] = NULL; + tcb->tcb_socknum = -1; + + /* + * Decide what action to take based on current state + */ + + switch (tcb->tcb_state) { + case TCPSTATE_SYN_RECEIVED: + case TCPSTATE_ESTABLISHED: + /* + * Transmit the FIN/ACK and wait for a FIN/ACK. + * + * XXX probably want to wait till all unacked data is + * acked before sending the fin?? This would be sort + * of like the "linger" option + */ + _tcp_setstate(tcb,TCPSTATE_FINWAIT_1); + _tcp_sendctlmsg(ti,tcb,TCPFLG_ACK | TCPFLG_FIN,TCP_RETX_TIMER); + break; + + case TCPSTATE_CLOSED: + case TCPSTATE_LISTEN: + case TCPSTATE_SYN_SENT: + /* + * Disconnect during our attempt, or from some + * idle state that does not require sending anything. + * Go back to CLOSED. + */ + _tcp_closetcb(ti,tcb); + _tcp_freetcb(ti,tcb); + break; + + case TCPSTATE_CLOSE_WAIT: + _tcp_setstate(tcb,TCPSTATE_LAST_ACK); + _tcp_sendctlmsg(ti,tcb,TCPFLG_ACK | TCPFLG_FIN,TCP_RETX_TIMER); + break; + + case TCPSTATE_TIME_WAIT: + case TCPSTATE_FINWAIT_1: + case TCPSTATE_FINWAIT_2: + case TCPSTATE_CLOSING: + case TCPSTATE_LAST_ACK: + default: + break; + } + + return 0; +} + + + +/* ********************************************************************* + * _tcp_aborttcb(ti,tcb) + * + * Forcefully terminate a TCP connection. Sends an RST packet + * to nuke the other end. The socket is forced into the CLOSED + * state. + * + * Input parameters: + * ti - tcp information + * tcb -tcb to abort + * + * Return value: + * nothing + ********************************************************************* */ + +static void _tcp_aborttcb(tcp_info_t *ti,tcb_t *tcb) +{ + DEBUGMSG(("tcp_abort from state %d\n",tcb->tcb_state)); + + /* + * Decide what action to take based on current state + * If we're in SYN_SENT, RECEIVED, ESTABLISHED, FINWAIT_1, + * FINWAIT_2, CLOSING, LAST_ACK, or CLOSE_WAIT we've sent + * some traffic on this TCB, so send an RST to kill off + * the remote TCB. + */ + + if (TCPSTATE_IN_SET(tcb->tcb_state,M_TCPSTATE_ABORTSTATES)) { + /* Send RST with no timeout, don't retransmit it. */ + _tcp_canceltimers(tcb); + _tcp_sendctlmsg(ti,tcb,TCPFLG_ACK | TCPFLG_RST,0); + } + + /* + * No matter what, it's now CLOSED. + */ + + _tcp_closetcb(ti,tcb); + +} + + +/* ********************************************************************* + * _tcp_closetcb(ti,tcb) + * + * Close a TCB, switching the state to "closed" and resetting + * internal variables. The TCB is *not* freed. + * + * Input parameters: + * ti - tcp information + * tcb - tcb to close + * + * Return value: + * nothing + ********************************************************************* */ + +static void _tcp_closetcb(tcp_info_t *ti,tcb_t *tcb) +{ + /* + * Set state to "closed" and reset timers + */ + + _tcp_setstate(tcb,TCPSTATE_CLOSED); + tcb->tcb_flags = 0; + _tcp_canceltimers(tcb); + + /* + * Reinitialize the buffers to waste the stored send data and + * clear out any receive data + */ + + tmb_init(&tcb->tcb_txbuf); + tmb_init(&tcb->tcb_rxbuf); +} + +/* ********************************************************************* + * _tcp_send(ti,s,buf,len) + * + * Queue some data to be transmitted via a TCP socket + * + * Input parameters: + * ti - TCP information + * s - socket number + * buf - buffer of data to send + * len - size of buffer to send + * + * Return value: + * number of bytes queued + * <0 if an error occured + ********************************************************************* */ + +int _tcp_send(tcp_info_t *ti,int s,uint8_t *buf,int len) +{ + tcb_t *tcb; + int retlen; + int curlen; + + tcb = ti->ti_ports[s]; + if (!tcb) return CFE_ERR_INV_PARAM; + + /* + * State must be ESTABLISHED or CLOSE_WAIT. CLOSE_WAIT + * means we've received a fin, but we can still send in + * the outbound direction. + */ + + if (!TCPSTATE_IN_SET(tcb->tcb_state,M_TCPSTATE_SEND_OK)) { + return CFE_ERR_NOTCONN; + } + + /* + * Copy the user data into the transmit buffer. + */ + + curlen = tmb_curlen(&(tcb->tcb_txbuf)); + retlen = tmb_copyin(&(tcb->tcb_txbuf),buf,len,TRUE); + + /* + * Cause some output on the device. + */ + + /* + * Nagle: Call _tcp_output only if there is no outstanding + * unacknowledged data. The way our transmit buffer + * works, it only holds unacknowledged data, so this + * test is easy. It isn't really 100% correct to + * do it this way, but the effect is the same; we will + * not transmit tinygrams. + */ + + if ((curlen == 0) || (tcb->tcb_sockflags & TCPFLG_NODELAY)) { + _tcp_output(ti,tcb); + } + + return retlen; +} + +/* ********************************************************************* + * _tcp_recv(ti,s,buf,len) + * + * Get buffered receive data from the TCP socket + * + * Input parameters: + * ti - tcp information + * s - socket number + * buf - pointer to receive buffer area + * len - size of receive buffer area + * + * Return value: + * number of bytes received + * <0 if an error occured + * ==0 if no data available (or tcp session is closed) + ********************************************************************* */ + +int _tcp_recv(tcp_info_t *ti,int s,uint8_t *buf,int len) +{ + tcb_t *tcb; + int retlen; + + tcb = ti->ti_ports[s]; + if (!tcb) return CFE_ERR_INV_PARAM; + + if (!TCPSTATE_IN_SET(tcb->tcb_state,M_TCPSTATE_RECV_OK)) { + return CFE_ERR_NOTCONN; + } + + retlen = tmb_copyout(&(tcb->tcb_rxbuf),buf,len,TRUE); + + /* + * If we've drained all of the data out of the buffer + * send an ack. This isn't ideal, but it will + * prevent us from keeping the window closed. + */ + +// if (retlen && (tmb_curlen(&(tcb->tcb_rxbuf)) == 0)) { +// _tcp_preparectlmsg(tcb,TCPFLG_ACK); +// _tcp_protosend(ti,tcb); +// tcb->tcb_flags &= ~TCB_FLG_DLYACK; +// } + + return retlen; +} + + +/* ********************************************************************* + * _tcp_bind(ti,s,port) + * + * "bind" a TCP socket to a particular port - this sets the + * outbound local (source) port number. + * + * Input parameters: + * ti - tcp information + * s - socket number + * port - port number + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int _tcp_bind(tcp_info_t *ti,int s,uint16_t port) +{ + tcb_t *tcb; + + tcb = ti->ti_ports[s]; + if (!tcb) return CFE_ERR_INV_PARAM; + + /* XXX test state - must be in 'closed' */ + + if (_tcp_find_lclport(ti,port)) return CFE_ERR_ADDRINUSE; + + tcb->tcb_lclport = port; + + return 0; +} + +/* ********************************************************************* + * _tcp_setflags(ti,s,flags) + * + * Set per-socket flags. + * + * Input parameters: + * ti - tcp information + * s - socket number + * flags - flags to set + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int _tcp_setflags(tcp_info_t *ti,int s,unsigned int flags) +{ + tcb_t *tcb; + + tcb = ti->ti_ports[s]; + if (!tcb) return CFE_ERR_INV_PARAM; + + tcb->tcb_sockflags = flags; + + return 0; +} + +/* ********************************************************************* + * _tcp_getflags(ti,s,flags) + * + * Get per-socket flags. + * + * Input parameters: + * ti - tcp information + * s - socket number + * flags - pointer to int to receive flags + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int _tcp_getflags(tcp_info_t *ti,int s,unsigned int *flags) +{ + tcb_t *tcb; + + tcb = ti->ti_ports[s]; + if (!tcb) return CFE_ERR_INV_PARAM; + + *flags = tcb->tcb_sockflags; + + return 0; +} + + +/* ********************************************************************* + * _tcp_peeraddr(ti,s,addr,port) + * + * Return the address of the computer on the other end of this + * connection. + * + * Input parameters: + * ti - tcp information + * s - socket number + * addr - receives IP address of remote + * port - port number + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int _tcp_peeraddr(tcp_info_t *ti,int s,uint8_t *addr,uint16_t *port) +{ + tcb_t *tcb; + + tcb = ti->ti_ports[s]; + if (!tcb) return CFE_ERR_INV_PARAM; + + /* + * Test for any of the states where we believe the peeraddr in the tcb + * is valid. + */ + + if (!TCPSTATE_IN_SET(tcb->tcb_state,M_TCPSTATE_PEERADDR_OK)) { + return CFE_ERR_NOTCONN; + } + + if (addr) memcpy(addr,tcb->tcb_peeraddr,IP_ADDR_LEN); + if (port) *port = tcb->tcb_peerport; + + return 0; +} + +/* ********************************************************************* + * _tcp_listen(ti,s,port) + * + * Set a socket for listening mode, allowing inbound connections + * to occur. + * + * Input parameters: + * ti - tcp information + * s - socket nunber + * port - port number to listen for (implicit tcp_bind) + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int _tcp_listen(tcp_info_t *ti,int s,uint16_t port) +{ + tcb_t *tcb; + queue_t *qb; + + /* + * See if another TCB is listening to this port + */ + + for (qb = ti->ti_tcblist.q_next; qb != &(ti->ti_tcblist); qb = qb->q_next) { + tcb = (tcb_t *) qb; + + if ((tcb->tcb_lclport == port) && + (tcb->tcb_state == TCPSTATE_LISTEN) && + (tcb->tcb_peerport == 0)) return CFE_ERR_ADDRINUSE; + + } + + /* + * Otherwise, we're good to go. + */ + + tcb = ti->ti_ports[s]; + if (!tcb) return CFE_ERR_INV_PARAM; + + tcb->tcb_lclport = port; + _tcp_setstate(tcb,TCPSTATE_LISTEN); + + tcb->tcb_sendnext = 0; + tcb->tcb_sendunack = 0; + tcb->tcb_sendwindow = 0; + _tcp_canceltimers(tcb); + + tcb->tcb_rcvnext = 0; + tcb->tcb_rcvack = 0; + + tmb_init(&tcb->tcb_txbuf); + tmb_init(&tcb->tcb_rxbuf); + + tcb->tcb_txflags = 0; + + return 0; +} + +/* ********************************************************************* + * _tcp_status(ti,s,connflag,rxready,rxeof) + * + * Get status information about the TCP session + * + * Input parameters: + * ti - tcp information + * s - socket nunber + * connflag - points to an int to receive connection status + * (1=connected,0=not connected) + * rxready - number of bytes in receive queue + * rxeof - returns 1 if we've been FINed. + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int _tcp_status(tcp_info_t *ti,int s,unsigned int *connflag,int *rxready,int *rxeof) +{ + tcb_t *tcb; + int rxlen; + + tcb = ti->ti_ports[s]; + if (!tcb) return CFE_ERR_INV_PARAM; + + rxlen = tmb_curlen(&(tcb->tcb_rxbuf)); + + /* + * We consider the connection "connected" if it's established + * or it's in CLOSE_WAIT (FIN received) but we have not drained + * all of the receive data. + * + * Otherwise: If it's not in one of the connection establishment states, + * it's not connected. + */ + + if (connflag) { + if ((tcb->tcb_state == TCPSTATE_ESTABLISHED) || + ((rxlen != 0) && (tcb->tcb_state == TCPSTATE_CLOSE_WAIT))) { + *connflag = TCPSTATUS_CONNECTED; + } + else if (TCPSTATE_IN_SET(tcb->tcb_state,M_TCPSTATE_CONNINPROGRESS)) { + *connflag = TCPSTATUS_CONNECTING; + } + else { + *connflag = TCPSTATUS_NOTCONN; + } + } + + if (rxready) { + *rxready = rxlen; + } + + if (rxeof) { + *rxeof = 0; + if ((tcb->tcb_state == TCPSTATE_CLOSE_WAIT) || + (tcb->tcb_state == TCPSTATE_LAST_ACK)) { + *rxeof = 1; + } + } + + return 0; +} + +/* ********************************************************************* + * _tcp_debug(ti,s,arg) + * + * Perform debug functions on a socket + * + * Input parameters: + * ti - tcp information + * s - socket number + * arg - argument + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int _tcp_debug(tcp_info_t *ti,int s,int arg) +{ + tcb_t *tcb; + + tcb = ti->ti_ports[s]; + if (!tcb) return CFE_ERR_INV_PARAM; + + printf("State=%d SendNext=%u SendUnack=%u ", + tcb->tcb_state,tcb->tcb_sendnext,tcb->tcb_sendunack); + printf("SendWin=%d Ack=%u Rxlen=%d\n", + tcb->tcb_sendwindow, + tcb->tcb_rcvack,tmb_curlen(&(tcb->tcb_rxbuf))); + + return 0; +} + + +#ifdef _TCP_DEBUG_ +/* ********************************************************************* + * _tcp_dumppktstate(label,flags,ack,seq,len) + * + * A debug function. + * + * Input parameters: + * label - printed before packet state + * flags - transmit flags for packet + * ack,seq,len - from the packet + * + * Return value: + * nothing + ********************************************************************* */ +static void _tcp_dumppktstate(char *label,uint16_t flags, + uint32_t ack,uint32_t seq,int len) +{ + printf("%s: ",label); + if (flags & TCPFLG_FIN) printf("FIN "); + if (flags & TCPFLG_SYN) printf("SYN "); + if (flags & TCPFLG_RST) printf("RST "); + if (flags & TCPFLG_PSH) printf("PSH "); + if (flags & TCPFLG_ACK) printf("ACK "); + if (flags & TCPFLG_URG) printf("URG "); + printf("Ack=%u Seq=%u Data=%d\n",ack,seq,len); +} +#endif + + +/* ********************************************************************* + * _tcp_output(ti,tcb) + * + * Try to perform some output on this TCB. If there is + * data to send and we can transmit it (i.e., the remote's + * receive window will allow it), segment the data from the + * buffer and transmit it, updating local state variables. + * + * Input parameters: + * ti - tcp information + * tcb - the tcb to send data on + * + * Return value: + * nothing + ********************************************************************* */ + +static void _tcp_output(tcp_info_t *ti,tcb_t *tcb) +{ + ebuf_t *b; + int tcplen; + int window; + uint16_t flags; + uint8_t *cksumptr; + uint8_t *tcphdr; + uint16_t cksum; + int hdrlen; + uint8_t pseudoheader[12]; + int offset; + uint32_t sendmax; + uint32_t windowmax; + uint32_t cwndmax; + + /* + * sendmax is (one plus) the highest sequence number we have + * data for. + * + * windowmax is (one plus) the highest sequence number in the + * send window. + * + * cwndmax is (one plus) the highest sequence number in the + * congestion window. + */ + + sendmax = tcb->tcb_sendunack + tmb_curlen(&(tcb->tcb_txbuf)); + windowmax = tcb->tcb_sendunack + tcb->tcb_sendwindow; + cwndmax = tcb->tcb_sendunack + 5*1400; + + /* + * We'll send up to 'sendmax', 'cwndmax', or 'windowmax' bytes, whichever + * is sooner. Set 'sendmax' to the earliest sequence number. + */ + + if (TCPSEQ_GT(sendmax,windowmax)) sendmax = windowmax; + if (TCPSEQ_GT(sendmax,cwndmax)) sendmax = cwndmax; + + /* + * The (receive) window is whatever it takes to fill the buffer. + */ + + window = tmb_remlen(&(tcb->tcb_rxbuf)); + if (window > 65000) window = 65000; + + /* + * Spit out packets until "sendnext" either catches up + * or exceeds the remote window + */ + + while (TCPSEQ_LT(tcb->tcb_sendnext,sendmax)) { + + /* + * This is the offset into the transmit buffer to start with. + * Offset 0 in the transmit buffer corresponds to "send unack" + * received acks move this pointer. + */ + + offset = tcb->tcb_sendnext - tcb->tcb_sendunack; + + /* + * Allocate a buffer and remember the pointer to where + * the header starts. + */ + + b = _ip_alloc(ti->ti_ipinfo); + if (!b) break; + + tcphdr = ebuf_ptr(b) + ebuf_length(b); + + flags = TCPFLG_ACK | TCPHDRFLG(TCP_HDR_LENGTH); + hdrlen = TCP_HDR_LENGTH; + + /* + * Fill in the fields according to the RFC. + */ + + tcb->tcb_rcvack = tcb->tcb_rcvnext; /* Update our "most recent ack" */ + + ebuf_append_u16_be(b,tcb->tcb_lclport); /* Local and remote ports */ + ebuf_append_u16_be(b,tcb->tcb_peerport); + ebuf_append_u32_be(b,tcb->tcb_sendnext); /* sequence and ack numbers */ + ebuf_append_u32_be(b,tcb->tcb_rcvack); + ebuf_append_u16_be(b,flags); /* Flags */ + ebuf_append_u16_be(b,window); /* Window size */ + cksumptr = ebuf_ptr(b) + ebuf_length(b); + ebuf_append_u16_be(b,0); /* dummy checksum for calculation */ + ebuf_append_u16_be(b,0); /* Urgent Pointer (not used) */ + + /* + * Append the data, copying pieces out of the transmit buffer + * without adjusting its pointers. + */ + + tcplen = tmb_copyout2(&(tcb->tcb_txbuf), + b->eb_ptr + b->eb_length, + offset, + tcb->tcb_mtu); + + b->eb_length += tcplen; + +#ifdef _TCP_DEBUG_ + if (_tcp_dumpflags) _tcp_dumppktstate("TX_DATA",flags, + tcb->tcb_sendnext, + tcb->tcb_rcvack,tcplen); +#endif + + /* + * Adjust the "send next" sequence number to account for what + * we're about to send. + */ + + tcb->tcb_sendnext += tcplen; + + + /* + * Build the pseudoheader, which is part of the checksum calculation + */ + + _ip_getaddr(ti->ti_ipinfo,&pseudoheader[0]); + memcpy(&pseudoheader[4],tcb->tcb_peeraddr,IP_ADDR_LEN); + pseudoheader[8] = 0; + pseudoheader[9] = IPPROTO_TCP; + pseudoheader[10] = ((tcplen+hdrlen) >> 8) & 0xFF; + pseudoheader[11] = ((tcplen+hdrlen) & 0xFF); + + /* + * Checksum the packet and insert the checksum into the header + */ + + cksum = ip_chksum(0,pseudoheader,sizeof(pseudoheader)); + cksum = ip_chksum(cksum,tcphdr,tcplen + hdrlen); + cksum = ~cksum; + cksumptr[0] = (cksum >> 8) & 0xFF; + cksumptr[1] = (cksum & 0xFF); + + /* + * Transmit the packet. The layer below us will free it. + */ + + _ip_send(ti->ti_ipinfo,b,tcb->tcb_peeraddr,IPPROTO_TCP); + + /* + * Set the timer that we'll use to wait for an acknowledgement. + * If this timer goes off, we'll rewind "sendnext" back to + * "sendunack" and transmit stuff again. + */ + + TIMER_SET(tcb->tcb_timer_retx,TCP_RETX_TIMER); + + /* + * We just sent an ack, so cancel the delayed ack timer. + */ + + tcb->tcb_flags &= ~TCB_FLG_DLYACK; + + } + +} + +/* ********************************************************************* + * _tcp_protosend(ti,tcb) + * + * Transmit "protocol messages" on the tcb. This is used for + * sending SYN, FIN, ACK, and other control packets. + * + * Input parameters: + * ti - tcp infomration + * tcb - tcb we're interested in + * + * Return value: + * nothing + ********************************************************************* */ + +static void _tcp_protosend(tcp_info_t *ti,tcb_t *tcb) +{ + ebuf_t *b; + int tcplen; + int window; + uint16_t flags; + uint8_t *cksumptr; + uint8_t *tcphdr; + uint16_t cksum; + int hdrlen; + uint8_t pseudoheader[12]; + + /* + * Allocate a buffer and remember the pointer to where + * the header starts. + */ + + b = _ip_alloc(ti->ti_ipinfo); + if (!b) return; + + tcphdr = ebuf_ptr(b) + ebuf_length(b); + + /* + * We're going to send something, so we can clear the flag. + * Also clear the delay-ack flag since everything's an ack. + */ + + tcb->tcb_flags &= ~(TCB_FLG_SENDMSG | TCB_FLG_DLYACK); + + /* + * Build the TCP header + */ + + /* The flags are the current flags + the header length */ + if (tcb->tcb_txflags & TCPFLG_SYN) { + flags = tcb->tcb_txflags | (TCPHDRFLG(TCP_HDR_LENGTH + 4)); + hdrlen = TCP_HDR_LENGTH + 4; + } + else { + flags = tcb->tcb_txflags | (TCPHDRFLG(TCP_HDR_LENGTH)); + hdrlen = TCP_HDR_LENGTH; + } + + +#ifdef _TCP_DEBUG_ + if (_tcp_dumpflags) _tcp_dumppktstate("TX_CTL",flags, + tcb->tcb_sendnext, + tcb->tcb_rcvack,0); +#endif + + /* + * The (receive) window is whatever it takes to fill the buffer. + */ + + window = tmb_remlen(&(tcb->tcb_rxbuf)); + if (window > 65000) window = 65000; + + /* + * Fill in the fields according to the RFC. + */ + + tcb->tcb_rcvack = tcb->tcb_rcvnext; /* update last tx ack */ + + ebuf_append_u16_be(b,tcb->tcb_lclport); /* Local and remote ports */ + ebuf_append_u16_be(b,tcb->tcb_peerport); + ebuf_append_u32_be(b,tcb->tcb_sendnext); /* sequence and ack numbers */ + ebuf_append_u32_be(b,tcb->tcb_rcvack); + ebuf_append_u16_be(b,flags); /* Flags */ + ebuf_append_u16_be(b,window); /* Window size */ + cksumptr = ebuf_ptr(b) + ebuf_length(b); + ebuf_append_u16_be(b,0); /* dummy checksum for calculation */ + ebuf_append_u16_be(b,0); /* Urgent Pointer (not used) */ + + /* + * Append TCP options on SYN packet + */ + + if (flags & TCPFLG_SYN) { + ebuf_append_u16_be(b,TCP_MAX_SEG_OPT); + ebuf_append_u16_be(b,TCP_MAX_SEG_SIZE); + } + + tcplen = 0; /* don't transmit data here */ + + /* + * SYN and FIN packets consume a sequence number, so + * increment the "sendnext" variable. If we need to retransmit + * these segments, we'll wind "sendnext" back to "sendunack" + * + * XXX: Can you send a SYN and FIN at the same time? + */ + + if (flags & (TCPFLG_SYN | TCPFLG_FIN)) tcb->tcb_sendnext++; + + /* + * Build the pseudoheader, which is part of the checksum calculation + */ + + _ip_getaddr(ti->ti_ipinfo,&pseudoheader[0]); + memcpy(&pseudoheader[4],tcb->tcb_peeraddr,IP_ADDR_LEN); + pseudoheader[8] = 0; + pseudoheader[9] = IPPROTO_TCP; + pseudoheader[10] = ((tcplen+hdrlen) >> 8) & 0xFF; + pseudoheader[11] = ((tcplen+hdrlen) & 0xFF); + + /* + * Checksum the packet and insert the checksum into the header + */ + + cksum = ip_chksum(0,pseudoheader,sizeof(pseudoheader)); + cksum = ip_chksum(cksum,tcphdr,tcplen + hdrlen); + cksum = ~cksum; + cksumptr[0] = (cksum >> 8) & 0xFF; + cksumptr[1] = (cksum & 0xFF); + + /* + * Transmit the packet. The layer below us will free it. + */ + + _ip_send(ti->ti_ipinfo,b,tcb->tcb_peeraddr,IPPROTO_TCP); +} + +/* ********************************************************************* + * _tcp_sendreset(ti,dstipaddr,ack,srcport,dstport) + * + * Transmit "protocol messages" on the tcb. This is used for + * sending SYN, FIN, ACK, and other control packets. + * + * Input parameters: + * ti - tcp infomration + * tcb - tcb we're interested in + * + * Return value: + * nothing + ********************************************************************* */ + +static void _tcp_sendreset(tcp_info_t *ti,uint8_t *dstipaddr,uint32_t ack, + uint16_t srcport,uint16_t dstport) +{ + tcb_t tcb; /* fake TCB so we can use _tcp_protosend */ + + memset(&tcb,0,sizeof(tcb)); + memcpy(tcb.tcb_peeraddr,dstipaddr,IP_ADDR_LEN); + tcb.tcb_peerport = dstport; + tcb.tcb_lclport = srcport; + tcb.tcb_txflags = TCPFLG_RST|TCPFLG_ACK; + tcb.tcb_rcvnext = ack; + + _tcp_protosend(ti,&tcb); +} + + +/* ********************************************************************* + * _tcp_sendctlmsg(ti,tcb,flags,timeout) + * + * Set up for and transmit a control message. This is usually + * called on a state transition where we need to send a control + * message like a SYN or FIN, with a timeout. We reset the + * retry counter, set the retransmit timer, and fire off the message + * right here. + * + * Input parameters: + * ti - tcp information + * tcb - the tcb for this TCP session + * flags - packet flags (TCPFLG_xxx) + * timeout - timeout value , in ticks + * + * Return value: + * nothing + ********************************************************************* */ + +static void _tcp_sendctlmsg(tcp_info_t *ti,tcb_t *tcb,uint16_t flags,int timeout) +{ + tcb->tcb_txflags = flags; + tcb->tcb_retrycnt = 0; + + if (timeout >= 0) { + if (timeout) { + TIMER_SET(tcb->tcb_timer_retx,timeout); + } + else { + TIMER_CLEAR(tcb->tcb_timer_retx); + } + } + + _tcp_protosend(ti,tcb); +} + + +/* ********************************************************************* + * _tcp_find_lclport(ti,port) + * + * Find the specified local port - mostly to see if it is + * already in use. + * + * Input parameters: + * ti - tcp information + * port - local port + * + * Return value: + * tcb owning this port, or NULL if none found + ********************************************************************* */ +static tcb_t *_tcp_find_lclport(tcp_info_t *ti,uint16_t port) +{ + tcb_t *tcb = NULL; + queue_t *qb; + + for (qb = ti->ti_tcblist.q_next; qb != &(ti->ti_tcblist); qb = qb->q_next) { + tcb = (tcb_t *) qb; + + if (tcb->tcb_lclport == port) return tcb; + + } + + return NULL; + +} + +/* ********************************************************************* + * _tcp_find_tcb(ti,srcport,dstport,saddr) + * + * Locate the TCB in the active TCBs. This is used to match + * an inbound TCP packet to a TCB. + * + * Input parameters: + * ti - tcp information + * srcport,dstport - source and dest ports from TCP header + * saddr - source IP address of sending host + * + * Return value: + * tcb pointer or NULL if no TCB was found + ********************************************************************* */ + +static tcb_t *_tcp_find_tcb(tcp_info_t *ti,uint16_t srcport,uint16_t dstport,uint8_t *saddr) +{ + tcb_t *tcb = NULL; + queue_t *qb; + + for (qb = ti->ti_tcblist.q_next; qb != &(ti->ti_tcblist); qb = qb->q_next) { + tcb = (tcb_t *) qb; + + /* Try active TCBs */ + if ((tcb->tcb_peerport != 0) && + (tcb->tcb_lclport == dstport) && + (tcb->tcb_peerport == srcport) && + (memcmp(saddr,tcb->tcb_peeraddr,IP_ADDR_LEN) == 0)) break; + } + + /* Found it on our active list. */ + if (qb != &(ti->ti_tcblist)) return tcb; + + /* no dice, try listening ports */ + for (qb = ti->ti_tcblist.q_next; qb != &(ti->ti_tcblist); qb = qb->q_next) { + tcb = (tcb_t *) qb; + + /* Try active TCBs */ + if ((tcb->tcb_peerport == 0) && + (tcb->tcb_lclport == dstport)) break; + } + + /* Found it on our passive list. */ + if (qb != &(ti->ti_tcblist)) return tcb; + + return NULL; +} + +/* ********************************************************************* + * _tcp_procack(ti,tcb,ack,flags) + * + * Process a received acknowledgement. + * + * Input parameters: + * ti - tcp information + * tcb - tcb for this tcb session + * ack - acknum from received packet + * flags - flags from received packet + * + * Return value: + * nothing + ********************************************************************* */ + +static void _tcp_procack(tcp_info_t *ti,tcb_t *tcb,uint32_t ack,uint16_t flags) +{ + int ackamt; + int unacklen; + + /* This is the number of unacknowledged bytes we have */ + unacklen = tmb_curlen(&(tcb->tcb_txbuf)); + + switch (tcb->tcb_state) { + + case TCPSTATE_ESTABLISHED: + /* + * The ack is valid if it's in the range of + * unacknowledged data we're holding onto. + * + * sendunack < ack <= (sendunack+datalen) + * + * Do the first comparison as "<=" instead of just "<" + * so we can catch duplicate acks. + */ + + if (!(TCPSEQ_LEQ(tcb->tcb_sendunack,ack) && + TCPSEQ_LEQ(ack,(tcb->tcb_sendunack+unacklen)))) { + DEBUGMSG(("Ack is out of range: %u\n",ack)); + return; + } + + /* + * Compute the # of bytes spanned by this ack + */ + + ackamt = TCPSEQ_DIFF(ack,tcb->tcb_sendunack); + + /* + * If we actually acked something, adjust the tx buffer + * to remove the bytes covered by the ack, then update + * the 'next' sequence number so we'll start there next + * time. + */ + + if (ackamt > 0) { + tmb_adjust(&tcb->tcb_txbuf,ackamt); + tcb->tcb_txflags = TCPFLG_ACK; + tcb->tcb_sendunack = ack; + tcb->tcb_dup_ackcnt = 0; /* not a duplicate */ + } + else { + tcb->tcb_dup_ackcnt++; + //DEBUGMSG(("Duplicate ack received\n")); + /* + * Duplicate ack received + * XXX This is where we'd be doing stuff for + * XXX slow-start, fast retransmit, etc. + */ + } + + /* + * If we're all caught up, we can cancel the + * retransmit timer. Otherwise, try to do + * some output on the interface. This will + * reset the retransmit timer if we did anything. + */ + + if (tmb_curlen(&(tcb->tcb_txbuf)) != 0) { + tcb->tcb_flags |= TCB_FLG_OUTPUT; + } + else { + TIMER_CLEAR(tcb->tcb_timer_retx); + } + + break; + + case TCPSTATE_FINWAIT_1: + ackamt = TCPSEQ_DIFF(ack,tcb->tcb_sendunack); + if (ackamt == 1) { + tcb->tcb_sendunack = ack; + if (flags & TCPFLG_FIN) { + _tcp_setstate(tcb,TCPSTATE_TIME_WAIT); + _tcp_canceltimers(tcb); + _tcp_preparectlmsg(tcb,TCPFLG_ACK); + TIMER_SET(tcb->tcb_timer_2msl,TCP_TIMEWAIT_TIMER); + } + else { + _tcp_setstate(tcb,TCPSTATE_FINWAIT_2); + } + } + break; + + } +} + +/* ********************************************************************* + * _tcp_procdata(ti,tcb,seqnum,flags,buf) + * + * Process data in a received TCP packet - we'll put the + * data into the receive ring buffer, process the seqnum + * field, and prepare to send an ack. + * + * Input parameters: + * ti - tcp information + * tcb - tcb describing TCP session + * seqnum,flags - from the header of the received TCP packet + * buf - ebuf contianing data + * + * Return value: + * nothing + ********************************************************************* */ + +static void _tcp_procdata(tcp_info_t *ti,tcb_t *tcb,uint32_t seqnum, + uint16_t flags,ebuf_t *buf) +{ + int datalen; + uint8_t *bp; + int rxwin,rwin; + uint32_t endseqnum; + uint32_t endrxwindow; + int reallen; + + /* + * Figure out how much we're going to take. This should not + * exceed our buffer size because we advertised a window + * only big enough to fill our buffer. + */ + + bp = ebuf_ptr(buf); /* pointer to TCP data */ + datalen = ebuf_remlen(buf); /* Size of TCP data */ + + /* + * If there's no data in the buffer, we can skip the data-related + * stuff and check for a possible FIN. + */ + + if (datalen != 0) { + + /* + * Figure out if the data fits within the window. We don't deal + * with out-of-order segments, so the test is relatively + * simple: The received segment must contain (or match) + * the sequence number from "rcvnext" to the end of the receive + * window. + * + * Therefore: seqnum <= rcvnext < seqnum+datalen + */ + + rxwin = tmb_remlen(&(tcb->tcb_rxbuf)); /* receive window size */ + endseqnum = (seqnum + datalen); /* seq # of end of segment */ + endrxwindow = tcb->tcb_rcvack + rxwin; /* end of receive window */ + + /* + * The segment might include data outside the receive window + * (it's not supposed to, but it can). Crop the 'endseqnum' + * value at the rx window if necessary. Keep the earlier seqnum. + */ +// +// XXX This is just plain broken - "endrxwindow" is the wrong thing to test. +// if (TCPSEQ_LT(endrxwindow,endseqnum)) { +// endseqnum = endrxwindow; +// } + + /* + * Test to see if the data is in sequence. + */ + + rwin = (TCPSEQ_LEQ(seqnum,tcb->tcb_rcvnext) && + TCPSEQ_LT(tcb->tcb_rcvnext,endseqnum)); + + if (!rwin) { + DEBUGMSG(("Dropping out-of-order packet %u %u %u\n",seqnum, + tcb->tcb_rcvnext,endseqnum)); + return; + } + + /* + * The actual amount of new data is the distance from + * the "rcvnext" pointer to the end sequence number. + * typically this will be the entire packet, but we + * handle the case of receiving a partial duplicate segment. + * Do this by shortening the data length we're going to + * copy and adjusting the buffer pointer to point past the + * duplicate data. Normally this won't do anything. + */ + + reallen = endseqnum - tcb->tcb_rcvnext; + bp += (tcb->tcb_rcvnext - seqnum); + + if (reallen != datalen) { + DEBUGMSG(("newdata(%d) does not match real length(%d)\n",reallen,datalen)); + } + + if (reallen > 0) { + + /* + * Copy the data into the receive buffer. In the + * unlikely event that it doesn't fit, update the + * length so we'll only ack what we took. + */ + + reallen = tmb_copyin(&(tcb->tcb_rxbuf),bp,reallen,TRUE); + + tcb->tcb_rcvnext += reallen; + + /* + * Set the delayed-ack flag. If it's already set, + * then we've already received some data without acking + * it, so send the ack now, to encourage us to + * ack every other segment at least. + */ + + if (tcb->tcb_flags & TCB_FLG_DLYACK) { + _tcp_preparectlmsg(tcb,TCPFLG_ACK); + } + else { + tcb->tcb_flags |= TCB_FLG_DLYACK; + } + } + } + + /* + * Handle the case of data in a FIN packet. + */ + + if (flags & TCPFLG_FIN) { + + tcb->tcb_rcvnext++; /* Consume the FIN */ + + DEBUGMSG(("FIN rcvd in %d ack %u\n",tcb->tcb_state,tcb->tcb_rcvnext)); + + switch (tcb->tcb_state) { + case TCPSTATE_ESTABLISHED: + + /* + * If a FIN is received in the ESTABLISHED state, + * go to CLOSE_WAIT. + */ + + tcb->tcb_flags &= ~TCB_FLG_DLYACK; + _tcp_setstate(tcb,TCPSTATE_CLOSE_WAIT); + _tcp_sendctlmsg(ti,tcb,TCPFLG_ACK,-1); + break; + + case TCPSTATE_FINWAIT_1: + + /* + * Two choices: Either we got a FIN or a FIN ACK. + * In either case, send an ack. The difference + * is what state we end up in. + */ + + if (flags & TCPFLG_ACK) { + /* Received: FIN ACK - go directly to TIME_WAIT */ + _tcp_canceltimers(tcb); + _tcp_preparectlmsg(tcb,TCPFLG_ACK); + _tcp_setstate(tcb,TCPSTATE_TIME_WAIT); +#if defined(CONFIG_MIPS_BRCM) + TIMER_SET(tcb->tcb_timer_2msl,TCP_SEND_TIMER); +#else + TIMER_SET(tcb->tcb_timer_2msl,TCP_TIMEWAIT_TIMER); +#endif + } + else { + /* Received: FIN - simultaneous close, go to CLOSING */ + _tcp_setstate(tcb,TCPSTATE_CLOSING); + _tcp_preparectlmsg(tcb,TCPFLG_ACK); + } + break; + + case TCPSTATE_FINWAIT_2: + + /* + * Received a FIN in FINWAIT_2 - just transmit + * an ack and go to TIME_WAIT. + */ + + _tcp_canceltimers(tcb); + _tcp_preparectlmsg(tcb,TCPFLG_ACK); + _tcp_setstate(tcb,TCPSTATE_TIME_WAIT); + TIMER_SET(tcb->tcb_timer_2msl,TCP_TIMEWAIT_TIMER); + break; + } + } + +} + +/* ********************************************************************* + * _tcp_rx_callback(ref,buf,destaddr,srcaddr) + * + * The IP layer calls this routine when a TCP packet is received. + * We dispatch the packet to appropriate handlers from here. + * + * Input parameters: + * ref - our tcp information (held by the ip stack for us) + * buf - the ebuf that we received + * destaddr,srcaddr - destination and source IP addresses + * + * Return value: + * ETH_DROP or ETH_KEEP, depending if we keep the packet or not + ********************************************************************* */ + +static int _tcp_rx_callback(void *ref,ebuf_t *buf,uint8_t *destaddr,uint8_t *srcaddr) +{ + uint8_t pseudoheader[12]; + int tcplen; + uint16_t calccksum; + uint16_t origcksum; + uint8_t *tcphdr; + uint16_t srcport; + uint16_t dstport; + uint32_t seqnum; + uint32_t acknum; + uint16_t window; + uint16_t flags; + tcb_t *tcb; + tcp_info_t *ti = (tcp_info_t *) ref; + + /* + * get a pointer to the TCP header + */ + + tcplen = ebuf_length(buf); + tcphdr = ebuf_ptr(buf); + + /* + * construct the pseudoheader for the cksum calculation + */ + + memcpy(&pseudoheader[0],srcaddr,IP_ADDR_LEN); + memcpy(&pseudoheader[4],destaddr,IP_ADDR_LEN); + pseudoheader[8] = 0; + pseudoheader[9] = IPPROTO_TCP; + pseudoheader[10] = (tcplen >> 8) & 0xFF; + pseudoheader[11] = (tcplen & 0xFF); + + origcksum = ((uint16_t) tcphdr[16] << 8) | (uint16_t) tcphdr[17]; + tcphdr[16] = tcphdr[17] = 0; + + calccksum = ip_chksum(0,pseudoheader,sizeof(pseudoheader)); + calccksum = ip_chksum(calccksum,tcphdr,tcplen); + calccksum = ~calccksum; + + if (calccksum != origcksum) { + return ETH_DROP; + } + + /* Read the other TCP header fields from the packet */ + + ebuf_get_u16_be(buf,srcport); + ebuf_get_u16_be(buf,dstport); + ebuf_get_u32_be(buf,seqnum); + ebuf_get_u32_be(buf,acknum); + ebuf_get_u16_be(buf,flags); + ebuf_get_u16_be(buf,window); + ebuf_skip(buf,4); /* skip checksum and urgent pointer */ + + /* Skip options in header */ + if (TCPHDRSIZE(flags) < TCP_HDR_LENGTH) { + return ETH_DROP; + } + ebuf_skip(buf,(TCPHDRSIZE(flags) - TCP_HDR_LENGTH)); + + /* + * Okay, start looking for a matching TCB. If no matching TCB, + * send a RST. + * XXX Extra credit: rate-limit the RSTs. + */ + + tcb = _tcp_find_tcb(ti,srcport,dstport,srcaddr); + if (!tcb) { + DEBUGMSG(("Invalid TCB from %I, srcport=%u dstport=%u\n",srcaddr,srcport,dstport)); + _tcp_sendreset(ti,srcaddr,seqnum+1,dstport,srcport); + return ETH_DROP; /* drop packet if no matching port */ + } + + /* + * Any activity on a TCB is enough to reset the keepalive timer + */ + + if (TCPSTATE_IN_SET(tcb->tcb_state,M_TCPSTATE_RESETKEEPALIVE)) { + TIMER_SET(tcb->tcb_timer_keep,TCP_KEEPALIVE_TIMER); + } + + /* + * Some debugging + */ + +#ifdef _TCP_DEBUG_ + if (_tcp_dumpflags) _tcp_dumppktstate("Received",flags, + acknum,seqnum,ebuf_length(buf)); +#endif + + /* + * If someone tries to reset us, just kill off the port. + * (except: if we were in SYN_RECEIVED, go back to LISTEN) + */ + + if (flags & TCPFLG_RST) { + if (tcb->tcb_state == TCPSTATE_SYN_RECEIVED) { + _tcp_setstate(tcb,TCPSTATE_LISTEN); + } + else { + _tcp_closetcb(ti,tcb); + } + return ETH_DROP; + } + + /* + * Remember the window we got. + */ + + tcb->tcb_sendwindow = window; + + /* + * What we do here depends on the current connection state + * Most of this is just from the connection state diagram we've + * all see way too often. + */ + + switch ( tcb->tcb_state ) { + + case TCPSTATE_LISTEN: + if (flags & TCPFLG_SYN) { + tcb->tcb_rcvnext = seqnum + 1; + tcb->tcb_peerport = srcport; + memcpy(tcb->tcb_peeraddr,srcaddr,IP_ADDR_LEN); + TIMER_SET(tcb->tcb_timer_keep,TCP_KEEPALIVE_TIMER); + _tcp_setstate(tcb,TCPSTATE_SYN_RECEIVED); + _tcp_sendctlmsg(ti,tcb,TCPFLG_SYN | TCPFLG_ACK,TCP_RETX_TIMER); + } + return ETH_DROP; /* we ignore everything else */ + break; + + case TCPSTATE_SYN_SENT: + + /* + * The only packets we pay attention to in SYN_SENT + * are packets with SYN flags. + */ + if (flags & TCPFLG_SYN) { + + /* + * Two choices: Either we got a SYN ACK (normal) + * or just a SYN (simultaneous open, rare) + */ + + if ((flags & TCPFLG_ACK) && + (acknum == (tcb->tcb_sendunack + 1))) { + /* + * If we received a SYN ACK and the acknum + * matches our SYN's seqnum + 1, we want + * to transition from SYN_SENT to ESTABLISHED. + */ + TIMER_SET(tcb->tcb_timer_keep,TCP_KEEPALIVE_TIMER); + _tcp_setstate(tcb,TCPSTATE_ESTABLISHED); + tcb->tcb_sendunack = acknum; + tcb->tcb_sendnext = tcb->tcb_sendunack; + tcb->tcb_rcvnext = seqnum + 1; + /* + * Send an ack, but don't bother with the timer. + * If the remote does not get our ack, it will + * retransmit the SYN ACK and we'll ack it from + * the "ESTABLISHED" state. + * Actually, is that really true? + */ + _tcp_sendctlmsg(ti,tcb,TCPFLG_ACK,0); + } + else { + DEBUGMSG(("Simultaneous open: SYN received in SYN_SENT state\n")); + tcb->tcb_rcvnext++; /* consume the SYN */ + _tcp_setstate(tcb,TCPSTATE_SYN_RECEIVED); + _tcp_sendctlmsg(ti,tcb,TCPFLG_SYN|TCPFLG_ACK,TCP_RETX_TIMER); + } + } + + return ETH_DROP; + break; + + case TCPSTATE_SYN_RECEIVED: + /* + * If we've received a SYN and someone sends us a SYN, + * and the sequence numbers don't match, + * send back a SYN ACK. (this doesn't sound right, + * does it? It's sort of what we do from LISTEN) + */ + + if (flags & TCPFLG_SYN) { + _tcp_sendctlmsg(ti,tcb,TCPFLG_SYN | TCPFLG_ACK,TCP_RETX_TIMER); + } + + /* + * If we got an ack and the acknum is correct, + * switch the state to 'ESTABLISHED' and cancel + * the timer. We're ready to rock. + */ + + if ((flags & TCPFLG_ACK) && + (acknum == (tcb->tcb_sendunack + 1))) { + _tcp_sendctlmsg(ti,tcb,TCPFLG_ACK,0); + tcb->tcb_sendunack = acknum; + tcb->tcb_sendnext = tcb->tcb_sendunack; + TIMER_SET(tcb->tcb_timer_keep,TCP_KEEPALIVE_TIMER); + _tcp_setstate(tcb,TCPSTATE_ESTABLISHED); + } + + return ETH_DROP; + break; + + case TCPSTATE_ESTABLISHED: + case TCPSTATE_FINWAIT_1: + case TCPSTATE_FINWAIT_2: + + /* + * ESTABLISHED, FINWAIT_1, and FINWAIT_2 can all + * carry data. Process the data here. If the + * segment also contains a FIN, these routines + * will handle that. + */ + + if (flags & TCPFLG_ACK) { + _tcp_procack(ti,tcb,acknum,flags); + } + _tcp_procdata(ti,tcb,seqnum,flags,buf); + break; + + case TCPSTATE_CLOSING: + if (acknum == (tcb->tcb_sendunack + 1)) { + _tcp_setstate(tcb,TCPSTATE_TIME_WAIT); + _tcp_canceltimers(tcb); + TIMER_SET(tcb->tcb_timer_2msl,TCP_TIMEWAIT_TIMER); + } + break; + + case TCPSTATE_LAST_ACK: + if (acknum == (tcb->tcb_sendunack + 1)) { + /* Ack matches, just close the TCB here. */ + _tcp_closetcb(ti,tcb); + /* and free it. */ + _tcp_freetcb(ti,tcb); + } + else { + _tcp_sendctlmsg(ti,tcb,TCPFLG_ACK | TCPFLG_FIN,TCP_RETX_TIMER); + } + break; + + case TCPSTATE_TIME_WAIT: + if (!(flags & TCPFLG_RST)) { + tcb->tcb_txflags = TCPFLG_ACK; + TIMER_SET(tcb->tcb_timer_2msl,TCP_TIMEWAIT_TIMER); + _tcp_protosend(ti,tcb); + } + break; + } + + /* + * If we're expected to transmit something, do it now. + */ + + if (tcb->tcb_flags & TCB_FLG_OUTPUT) { + _tcp_output(ti,tcb); + tcb->tcb_flags &= ~(TCB_FLG_OUTPUT | TCB_FLG_SENDMSG); + } + + if (tcb->tcb_flags & TCB_FLG_SENDMSG) { + _tcp_protosend(ti,tcb); + } + + /* We always make a copy of the data, so drop the packet. */ + return ETH_DROP; +} + +/* ********************************************************************* + * _tcp_fasttimo(ti) + * + * Handle "fast timeout" for active TCP sockets. + * + * Input parameters: + * ti - tcp_info structure + * + * Return value: + * nothing + ********************************************************************* */ +static void _tcp_fasttimo(tcp_info_t *ti) +{ + tcb_t *tcb; + queue_t *qb; + + /* + * First, reset the timer so we'll end up here + * again in another 200ms + */ + + TIMER_SET(ti->ti_fasttimer,TCP_FAST_TIMER); /* 200ms */ + + /* + * Now, walk down the list of TCBs and handle + * all the timers. + */ + + for (qb = ti->ti_tcblist.q_next; qb != &(ti->ti_tcblist); qb = qb->q_next) { + tcb = (tcb_t *) qb; + if (tcb->tcb_flags & TCB_FLG_DLYACK) { + tcb->tcb_flags &= ~TCB_FLG_DLYACK; + _tcp_sendctlmsg(ti,tcb,TCPFLG_ACK,-1); + } + } + + /* + * While we're here, increment TCP's initial sequence number. + * BSD suggests 128000 every second, so we'll add 25600 every 200ms. + */ + + ti->ti_iss += 25600; + +} + + +/* ********************************************************************* + * _tcp_poll(arg) + * + * CFE calls this routine periodically to allow us to check + * and update timers, etc. + * + * Input parameters: + * arg - our tcp information (held by the timer module for us) + * + * Return value: + * nothing + ********************************************************************* */ + +void _tcp_poll(void *arg) +{ + tcb_t *tcb; + queue_t *qb; + tcp_info_t *ti = (tcp_info_t *) arg; + + /* + * Handle the "fast" timer. We do this every 200ms + */ + + if (TIMER_EXPIRED(ti->ti_fasttimer)) { + _tcp_fasttimo(ti); + /* timer will be reset by the _tcp_fasttimo routine */ + } + + /* + * Check the TCBs for the "slow" timers. + */ + + for (qb = ti->ti_tcblist.q_next; qb != &(ti->ti_tcblist); qb = qb->q_next) { + tcb = (tcb_t *) qb; + + /* + * Check the retransmit timer. This is used during connection + * attempts to retransmit control messages, or also during + * regular data transfer to retransmit messages that are not + * acknowledged. + * + * XXX use the computed round-trip timer here + */ + + if (TIMER_EXPIRED(tcb->tcb_timer_retx)) { + TIMER_CLEAR(tcb->tcb_timer_retx); /* unless it is reset */ + + DEBUGMSG(("Retransmit timer expired, retrycnt=%d\n",tcb->tcb_retrycnt)); + + switch (tcb->tcb_state) { + case TCPSTATE_ESTABLISHED: + /* + * wind the send seqnum back to the last unacknowledged data, + * and send it out again. + */ + TIMER_SET(tcb->tcb_timer_retx,TCP_RETX_TIMER); + tcb->tcb_retrycnt++; + tcb->tcb_sendnext = tcb->tcb_sendunack; + _tcp_output(ti,tcb); + break; + + case TCPSTATE_SYN_SENT: + TIMER_SET(tcb->tcb_timer_retx,(TCP_RETX_TIMER << tcb->tcb_retrycnt)); + tcb->tcb_retrycnt++; + tcb->tcb_sendnext = tcb->tcb_sendunack; + _tcp_protosend(ti,tcb); + break; + + } + } + + /* + * Check the keepalive timer. This is used during connection + * attempts to time out the connection, and _can_ be used for + * keepalives during established sessions. + * + * Our TCP does not bother with keepalive messages. + */ + + if (TIMER_EXPIRED(tcb->tcb_timer_keep)) { + TIMER_CLEAR(tcb->tcb_timer_keep); /* unless it is reset */ + DEBUGMSG(("Keepalive timer expired in state %d\n",tcb->tcb_state)); + if (TCPSTATE_IN_SET(tcb->tcb_state,M_TCPSTATE_CONNINPROGRESS)) { + DEBUGMSG(("Canceling pending connection\n")); + _tcp_aborttcb(ti,tcb); + } + } + + /* + * Check the 2MSL timer. This is used in the TIME_WAIT state + * to return the tcb to the free list after the time wait + * period elapses. + */ + + if (TIMER_EXPIRED(tcb->tcb_timer_2msl)) { + TIMER_CLEAR(tcb->tcb_timer_2msl); /* will not be reset */ + DEBUGMSG(("2MSL timer expired in state %d\n",tcb->tcb_state)); + if (tcb->tcb_state == TCPSTATE_TIME_WAIT) { + DEBUGMSG(("Freeing TCB\n")); + _tcp_closetcb(ti,tcb); + _tcp_freetcb(ti,tcb); + } + } + + } + +} + + diff --git a/cfe/cfe/net/net_tcp.h b/cfe/cfe/net/net_tcp.h new file mode 100755 index 0000000..fa772e6 --- /dev/null +++ b/cfe/cfe/net/net_tcp.h @@ -0,0 +1,83 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * TCP Protocol Definitions File: net_tcp.h + * + * This file contains TCP protocol-specific definitions + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +/* ********************************************************************* + * TCP Flags - keep in sync with net_api.h + ********************************************************************* */ + +#define TCPFLG_NODELAY 1 /* disable nagle */ +#define TCPFLG_NBIO 2 /* Non-blocking I/O */ + +#define TCPSTATUS_NOTCONN 0 +#define TCPSTATUS_CONNECTING 1 +#define TCPSTATUS_CONNECTED 2 + +/* ********************************************************************* + * TCP API + ********************************************************************* */ + +typedef struct tcp_info_s tcp_info_t; + +tcp_info_t *_tcp_init(ip_info_t *ipi,void *ref); +void _tcp_uninit(tcp_info_t *info); + +int _tcp_socket(tcp_info_t *info); +int _tcp_connect(tcp_info_t *ti,int s,uint8_t *dest,uint16_t port); +int _tcp_close(tcp_info_t *ti,int s); +int _tcp_send(tcp_info_t *ti,int s,uint8_t *buf,int len); +int _tcp_recv(tcp_info_t *ti,int s,uint8_t *buf,int len); +int _tcp_bind(tcp_info_t *ti,int s,uint16_t port); +int _tcp_peeraddr(tcp_info_t *ti,int s,uint8_t *addr,uint16_t *port); +int _tcp_listen(tcp_info_t *ti,int s,uint16_t port); +int _tcp_status(tcp_info_t *ti,int s,unsigned int *connflag,int *rxready,int *rxeof); +int _tcp_debug(tcp_info_t *ti,int s,int arg); +int _tcp_setflags(tcp_info_t *ti,int s,unsigned int flags); +int _tcp_getflags(tcp_info_t *ti,int s,unsigned int *flags); + +void _tcp_poll(void *arg); + + diff --git a/cfe/cfe/net/net_tcp_internal.h b/cfe/cfe/net/net_tcp_internal.h new file mode 100755 index 0000000..1e3fc6d --- /dev/null +++ b/cfe/cfe/net/net_tcp_internal.h @@ -0,0 +1,244 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * TCP Protocol Internal Definitions File: net_tcp_internal.h + * + * This file contains the structures and constants needed to + * maintain TCP connections. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + + +/* ********************************************************************* + * TCP Constants + ********************************************************************* */ + + +#define TCP_MAX_PORTS 8 +#define TCP_MAX_TCBS 16 +#if defined(CONFIG_MIPS_BRCM) +#define TCP_BUF_SIZE (20 * 1024) +#else +#define TCP_BUF_SIZE 65536 +#endif + +#define TCP_MAX_SEG_SIZE 1400 + +#define TCP_CONNECT_TIMER (30*CFE_HZ) +#define TCP_RETX_TIMER (1*CFE_HZ) +#define TCP_TIMEWAIT_TIMER (30*CFE_HZ) +#define TCP_SEND_TIMER (CFE_HZ) + +#define TCP_FAST_TIMER (CFE_HZ/5) /* 200ms */ +#define TCP_KEEPALIVE_TIMER (CFE_HZ*60) /* one minute */ + + +/* ********************************************************************* + * TCP Protocol + ********************************************************************* */ + +#define TCPFLG_FIN 0x0001 +#define TCPFLG_SYN 0x0002 +#define TCPFLG_RST 0x0004 +#define TCPFLG_PSH 0x0008 +#define TCPFLG_ACK 0x0010 +#define TCPFLG_URG 0x0020 +#define TCPHDRSIZE(flg) (((flg) >> 12)*4) +#define TCPHDRFLG(size) (((size)/4)<<12) + +#define TCP_HDR_LENGTH 20 + +#define TCP_MAX_SEG_OPT 0x0204 + +/* ********************************************************************* + * TCP State machine + ********************************************************************* */ + +/* + * TCB states, see RFC + */ + +#define TCPSTATE_CLOSED 0 +#define TCPSTATE_LISTEN 1 +#define TCPSTATE_SYN_SENT 2 +#define TCPSTATE_SYN_RECEIVED 3 +#define TCPSTATE_ESTABLISHED 4 +#define TCPSTATE_CLOSE_WAIT 5 +#define TCPSTATE_FINWAIT_1 6 +#define TCPSTATE_FINWAIT_2 7 +#define TCPSTATE_CLOSING 8 +#define TCPSTATE_LAST_ACK 9 +#define TCPSTATE_TIME_WAIT 10 + +/* + * Masks for TCP states - we use these so we can make + * bit vectors of states for easy tests. + */ + +#define M_TCPSTATE_CLOSED (1 << TCPSTATE_CLOSED) +#define M_TCPSTATE_LISTEN (1 << TCPSTATE_LISTEN) +#define M_TCPSTATE_SYN_SENT (1 << TCPSTATE_SYN_SENT) +#define M_TCPSTATE_SYN_RECEIVED (1 << TCPSTATE_SYN_RECEIVED) +#define M_TCPSTATE_ESTABLISHED (1 << TCPSTATE_ESTABLISHED) +#define M_TCPSTATE_CLOSE_WAIT (1 << TCPSTATE_CLOSE_WAIT) +#define M_TCPSTATE_FINWAIT_1 (1 << TCPSTATE_FINWAIT_1) +#define M_TCPSTATE_FINWAIT_2 (1 << TCPSTATE_FINWAIT_2) +#define M_TCPSTATE_CLOSING (1 << TCPSTATE_CLOSING) +#define M_TCPSTATE_LAST_ACK (1 << TCPSTATE_LAST_ACK) +#define M_TCPSTATE_TIME_WAIT (1 << TCPSTATE_TIME_WAIT) +#define M_TCPSTATE_CLOSED (1 << TCPSTATE_CLOSED) + +/* + * This macro returns true if a given state is in a + * set of states (defined below) + */ + +#define TCPSTATE_IN_SET(state,set) ((1 << (state)) & (set)) + +/* + * Intresting groups of TCP states + */ + +/* ABORTSTATES - tcp_abort will send a RST if our TCB is one of these. */ +#define M_TCPSTATE_ABORTSTATES \ + M_TCPSTATE_SYN_SENT | M_TCPSTATE_SYN_RECEIVED | M_TCPSTATE_ESTABLISHED | \ + M_TCPSTATE_FINWAIT_1 | M_TCPSTATE_FINWAIT_2 | M_TCPSTATE_CLOSING | \ + M_TCPSTATE_LAST_ACK | M_TCPSTATE_CLOSE_WAIT + +/* SEND_OK - tcp_send will send data if our TCB is one of these */ +#define M_TCPSTATE_SEND_OK \ + M_TCPSTATE_ESTABLISHED | M_TCPSTATE_CLOSE_WAIT + +/* RECV_OK - tcp_recv will pass up data if our TCB is in one of these */ +#define M_TCPSTATE_RECV_OK \ + M_TCPSTATE_ESTABLISHED | M_TCPSTATE_CLOSE_WAIT + +/* PEERADDR_OK - tcp_peeraddr will return a value if TCB is in one of these */ +#define M_TCPSTATE_PEERADDR_OK \ + M_TCPSTATE_SYN_SENT | M_TCPSTATE_SYN_RECEIVED | M_TCPSTATE_ESTABLISHED | \ + M_TCPSTATE_FINWAIT_1 | M_TCPSTATE_FINWAIT_2 | M_TCPSTATE_CLOSING | \ + M_TCPSTATE_LAST_ACK | M_TCPSTATE_CLOSE_WAIT + +/* RESETKEEPALIVE - reset keepalive timer in these states */ +#define M_TCPSTATE_RESETKEEPALIVE \ + M_TCPSTATE_ESTABLISHED | M_TCPSTATE_CLOSE_WAIT | M_TCPSTATE_FINWAIT_1 | \ + M_TCPSTATE_FINWAIT_2 | M_TCPSTATE_CLOSING | M_TCPSTATE_LAST_ACK + +#define CONNINPROGRESS - connection is in progress in these states */ +#define M_TCPSTATE_CONNINPROGRESS \ + M_TCPSTATE_LISTEN | M_TCPSTATE_SYN_SENT | M_TCPSTATE_SYN_RECEIVED + + +/* + * TCP flags for the control block + */ + +#define TCB_FLG_OUTPUT 1 /* need to call tcp_output */ +#define TCB_FLG_SENDMSG 2 +#define TCB_FLG_DLYACK 4 /* delayed ack is pending */ + +/* ********************************************************************* + * TCP Control Block + ********************************************************************* */ + +typedef struct tcb_s { + queue_t tcb_qb; /* next/previous in list */ + int tcb_socknum; /* socket number, index into table */ + + int tcb_state; /* current connection state */ + + uint8_t tcb_peeraddr[IP_ADDR_LEN]; /* Peer's IP Address */ + uint16_t tcb_peerport; /* Peer's port address */ + uint16_t tcb_lclport; /* My port address */ + + uint16_t tcb_txflags; /* packet flags for next tx packet */ + + cfe_timer_t tcb_timer_2msl; /* 2MSL timer, used in TIME_WAIT */ + cfe_timer_t tcb_timer_keep; /* Timer for keepalives and connections */ + cfe_timer_t tcb_timer_retx; /* send retransmission timer */ + cfe_timer_t tcb_timer_pers; /* Persist timer */ + + int tcb_retrycnt; /* Retry counter */ + + int tcb_mtu; /* MTU if underlying link */ + unsigned int tcb_flags; /* Misc protocol flags */ + unsigned int tcb_sockflags; /* flags set by user api */ + + /* + * Buffers + */ + + tcpmodbuf_t tcb_txbuf; /* Transmit buffer */ + tcpmodbuf_t tcb_rxbuf; /* Receive buffer */ + + /* + * Send sequence variables + */ + + uint32_t tcb_sendunack; /* oldest unacknowledged seqnum */ + uint32_t tcb_sendnext; /* Next seqnum to send */ + uint32_t tcb_sendwindow; /* Last advertised send window */ + + /* + * Receive sequence variables + */ + + uint32_t tcb_rcvnext; /* next in-order receive seq num */ + uint32_t tcb_rcvack; /* last transmitted acknowledgement */ + + /* + * Window management variables + */ + int tcb_dup_ackcnt; /* Duplicate ack counter */ + +} tcb_t; + +/* ********************************************************************* + * Macros to muck with sequence numbers + ********************************************************************* */ + +#define TCPSEQ_LT(a,b) ((int)((a)-(b)) < 0) +#define TCPSEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) +#define TCPSEQ_GT(a,b) ((int)((a)-(b)) > 0) +#define TCPSEQ_GEQ(a,b) ((int)((a)-(b)) >= 0) + +#define TCPSEQ_DIFF(a,b) ((int)((a)-(b))) + diff --git a/cfe/cfe/net/net_tcpbuf.c b/cfe/cfe/net/net_tcpbuf.c new file mode 100644 index 0000000..08e690b --- /dev/null +++ b/cfe/cfe/net/net_tcpbuf.c @@ -0,0 +1,298 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * TCP Protocol File: net_tcp.c + * + * This file contains a very simple TCP. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +#include "lib_types.h" +#include "lib_string.h" +#include "lib_queue.h" +#include "lib_malloc.h" +#include "lib_printf.h" + +#include "cfe_error.h" + +#include "net_tcpbuf.h" + + + +/* ********************************************************************* + * tmb_init(buf) + * + * Initialize a modulo buffer's pointers to the "empty" state + * + * Input parameters: + * buf - modulo buffer structure + * + * Return value: + * nothing + ********************************************************************* */ + +void tmb_init(tcpmodbuf_t *buf) +{ + buf->tmb_addptr = 0; + buf->tmb_remptr = 0; + buf->tmb_len = 0; +} + +/* ********************************************************************* + * tmb_adjust(buf,amt) + * + * Move the "remove" pointer ahead by 'amt' without retrieving the + * data. + * + * Input parameters: + * buf - modulo buffer structure + * amt - number of bytes to move + * + * Return value: + * nothing + ********************************************************************* */ + + +void tmb_adjust(tcpmodbuf_t *buf,int amt) +{ + /* XXX should we check for moving to far? */ + buf->tmb_len -= amt; + buf->tmb_remptr = (buf->tmb_remptr + amt) % buf->tmb_bufsize; +} + +/* ********************************************************************* + * tmb_alloc(buf,size) + * + * Allocate memory for the modulo buffer. + * + * Input parameters: + * buf - modulo buffer structure + * size - size of data in modulo buffer + * + * Return value: + * 0 if ok + * -1 if error + ********************************************************************* */ + +int tmb_alloc(tcpmodbuf_t *buf,int size) +{ + buf->tmb_buf = KMALLOC(size,0); + if (!buf->tmb_buf) return -1; + buf->tmb_bufsize = size; + + tmb_init(buf); + + return 0; +} + +/* ********************************************************************* + * tmb_free(buf) + * + * Free memory associated with the modulo buffer + * + * Input parameters: + * buf - modulo buffer + * + * Return value: + * nothing + ********************************************************************* */ + +void tmb_free(tcpmodbuf_t *buf) +{ + if (buf->tmb_buf) KFREE(buf->tmb_buf); + buf->tmb_buf = NULL; +} + + +/* ********************************************************************* + * tmb_copyin(tmb,buf,len,update) + * + * Copy data into the modulo buffer at the 'add' pointer + * + * Input parameters: + * tmb - modulo buffer structure + * buf,len - buffer and length of buffer to copy in + * update - true to advance 'add' pointer (usually true for copyin) + * + * Return value: + * number of bytes actually added to the buffer + ********************************************************************* */ + +int tmb_copyin(tcpmodbuf_t *tmb,uint8_t *buf,int len,int update) +{ + int maxlen; + int l; + int newptr; + int retlen; + + if (len == 0) return 0; + + /* Set 'maxlen' to the max # of bytes we will send now */ + maxlen = tmb->tmb_bufsize - tmb->tmb_len; + if (maxlen > len) maxlen = len; + + retlen = maxlen; /* we'll return this later. */ + + /* Copy the bytes into the buffer and deal with buffer wrap */ + l = tmb->tmb_bufsize - tmb->tmb_addptr; + if (l > maxlen) l = maxlen; + + memcpy(tmb->tmb_buf + tmb->tmb_addptr,buf,l); + maxlen -= l; + buf += l; + + if (maxlen) { + memcpy(tmb->tmb_buf,buf,maxlen); + newptr = maxlen; + } + else { + newptr = tmb->tmb_addptr + l; + } + + if (update) { + tmb->tmb_len += retlen; /* this many more in the buffer */ + tmb->tmb_addptr = newptr; + } + + return retlen; +} + +/* ********************************************************************* + * tmb_copyout(tmb,buf,len,update) + * + * Copy data out of the modulo buffer from the 'remove' pointer + * + * Input parameters: + * tmb - modulo buffer structure + * buf,len - buffer and length of buffer to copy out + * update - true to advance 'remove' pointer + * + * Return value: + * number of bytes actually removed from the buffer + ********************************************************************* */ + +int tmb_copyout(tcpmodbuf_t *tmb,uint8_t *buf,int len,int update) +{ + int maxlen; + int l; + int newptr; + int retlen; + + /* Compute how many bytes to return */ + maxlen = tmb->tmb_len; + if (maxlen > len) maxlen = len; + + retlen = maxlen; + + l = tmb->tmb_bufsize - tmb->tmb_remptr; + if (l > maxlen) l = maxlen; + + memcpy(buf,tmb->tmb_buf + tmb->tmb_remptr,l); + buf += l; + maxlen -= l; + + if (maxlen) { + memcpy(buf,tmb->tmb_buf,maxlen); + newptr = maxlen; + } + else { + newptr = tmb->tmb_remptr + l; + } + + if (update) { + tmb->tmb_len -= retlen; + tmb->tmb_remptr = newptr; + } + + return retlen; +} + +/* ********************************************************************* + * tmb_copyout2(tmb,buf,offset,len) + * + * Copy data out of the modulo buffer from a specific offset from + * the remove pointer. This is done without updating the + * remove pointer - we use this to dig data out when segmenting + * packets for transmission. + * + * Input parameters: + * tmb - modulo buffer structure + * buf,len - buffer and length of buffer to copy out + * offset - offset from remove pointer to start + * + * Return value: + * number of bytes actually copied out of the buffer + ********************************************************************* */ + +int tmb_copyout2(tcpmodbuf_t *tmb,uint8_t *buf,int offset,int len) +{ + int maxlen; + int l; + int retlen; + int remptr; + + /* Compute how many bytes to return */ + maxlen = tmb->tmb_len - offset; + + /* if offset is beyond length, get out now. */ + if (maxlen <= 0) return 0; + + /* otherwise choose min(max_can_copy,max_to_copy) */ + if (maxlen > len) maxlen = len; + retlen = maxlen; + + /* Adjust remove pointer for offset */ + remptr = (tmb->tmb_remptr + offset) % tmb->tmb_bufsize; + + l = tmb->tmb_bufsize - remptr; + if (l > maxlen) l = maxlen; + + memcpy(buf,tmb->tmb_buf + remptr,l); + buf += l; + maxlen -= l; + + if (maxlen) { + memcpy(buf,tmb->tmb_buf,maxlen); + } + + return retlen; +} + diff --git a/cfe/cfe/net/net_tcpbuf.h b/cfe/cfe/net/net_tcpbuf.h new file mode 100644 index 0000000..0c251e3 --- /dev/null +++ b/cfe/cfe/net/net_tcpbuf.h @@ -0,0 +1,83 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * TCP Protocol Definitions File: net_tcp.h + * + * This file contains TCP protocol-specific definitions + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + +/* ********************************************************************* + * Macros + ********************************************************************* */ + +#define tmb_remlen(tmb) ((tmb)->tmb_bufsize - (tmb)->tmb_len) +#define tmb_curlen(tmb) ((tmb)->tmb_len) + + +/* ********************************************************************* + * Modulo Buffer + ********************************************************************* */ + +typedef struct tcpmodbuf_s { + uint8_t *tmb_buf; /* Buffer */ + int tmb_bufsize; /* size of buffer */ + int tmb_addptr; /* current "add" pointer */ + int tmb_remptr; /* current "remove" pointer */ + int tmb_len; /* amount of data in the buffer */ +} tcpmodbuf_t; + + +/* ********************************************************************* + * Prototypes + ********************************************************************* */ + +void tmb_init(tcpmodbuf_t *buf); +void tmb_adjust(tcpmodbuf_t *buf,int amt); +int tmb_alloc(tcpmodbuf_t *buf,int size); +void tmb_free(tcpmodbuf_t *buf); +int tmb_copyin(tcpmodbuf_t *tmb,uint8_t *buf,int len,int update); +int tmb_copyout(tcpmodbuf_t *tmb,uint8_t *buf,int len,int update); +int tmb_copyout2(tcpmodbuf_t *tmb,uint8_t *buf,int offset,int len); + + + + + diff --git a/cfe/cfe/net/net_tftp.c b/cfe/cfe/net/net_tftp.c new file mode 100644 index 0000000..1c3e799 --- /dev/null +++ b/cfe/cfe/net/net_tftp.c @@ -0,0 +1,921 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * TFTP Client File: net_tftp.c + * + * This module contains a TFTP client. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +#include "lib_types.h" +#include "lib_string.h" +#include "lib_queue.h" +#include "lib_malloc.h" +#include "lib_printf.h" + +#include "cfe_error.h" +#include "cfe_fileops.h" + +/* Foxconn add start by Cliff Wang, 03/23/2010 */ +#include "cfe_console.h" +/* Foxconn add end by Cliff Wang, 03/23/2010 */ + +#include "net_ebuf.h" +#include "net_ether.h" + +#include "cfe.h" + +#include "cfe_loader.h" + +#include "net_api.h" + + + +/* ********************************************************************* + * TFTP protocol + ********************************************************************* */ + +#define UDP_PROTO_TFTP 69 + +#define TFTP_BLOCKSIZE 512 + +#define TFTP_OP_RRQ 1 +#define TFTP_OP_WRQ 2 +#define TFTP_OP_DATA 3 +#define TFTP_OP_ACK 4 +#define TFTP_OP_ERROR 5 + +#define TFTP_ERR_DISKFULL 3 + +#define TFTP_MAX_RETRIES 8 + +#define TFTP_RRQ_TIMEOUT 1 /* seconds */ +#define TFTP_RECV_TIMEOUT 1 /* seconds */ + +/* ********************************************************************* + * TFTP context + ********************************************************************* */ + +typedef struct tftp_fsctx_s { + int dummy; +} tftp_fsctx_t; + +typedef struct tftp_info_s { + int tftp_socket; + uint8_t tftp_data[TFTP_BLOCKSIZE]; + int tftp_blklen; + int tftp_blkoffset; + int tftp_fileoffset; + uint16_t tftp_blknum; + uint8_t tftp_ipaddr[IP_ADDR_LEN]; + int tftp_lastblock; + int tftp_error; + int tftp_filemode; + char *tftp_filename; +} tftp_info_t; + +/* ********************************************************************* + * Prototypes + ********************************************************************* */ + +static int tftp_fileop_init(void **fsctx,void *devicename); +static int tftp_fileop_open(void **ref,void *fsctx,char *filename,int mode); +static int tftp_fileop_read(void *ref,uint8_t *buf,int len); +static int tftp_fileop_write(void *ref,uint8_t *buf,int len); +static int tftp_fileop_seek(void *ref,int offset,int how); +static void tftp_fileop_close(void *ref); +static void tftp_fileop_uninit(void *fsctx); + +/* Foxconn add start by Cliff Wang, 03/23/2010 */ +int tftp_max_retries = TFTP_MAX_RETRIES; +int tftp_rrq_timeout = TFTP_RRQ_TIMEOUT; +int tftp_recv_timeout = TFTP_RECV_TIMEOUT; +/* Foxconn add end by Cliff Wang, 03/23/2010 */ + +/* ********************************************************************* + * TFTP fileio dispatch table + ********************************************************************* */ + +const fileio_dispatch_t tftp_fileops = { + "tftp", + LOADFLG_NOBB | FSYS_TYPE_NETWORK, + tftp_fileop_init, + tftp_fileop_open, + tftp_fileop_read, + tftp_fileop_write, + tftp_fileop_seek, + tftp_fileop_close, + tftp_fileop_uninit +}; + + +/* ********************************************************************* + * _tftp_open(info,hostname,filename,mode) + * + * Open a file on a remote host, using the TFTP protocol. + * + * Input parameters: + * info - TFTP information + * hostname - host name or IP address of remote host + * filename - name of file on remote system + * mode - file open mode, read or write + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int _tftp_open(tftp_info_t *info,char *hostname,char *filename,int mode) +{ + ebuf_t *buf = NULL; + const char *datamode = "octet"; + uint16_t type,error,block; + int res; + int retries; + + /* + * Look up the remote host's IP address + */ + + res = dns_lookup(hostname,info->tftp_ipaddr); + if (res < 0) return res; + + /* + * Open a UDP socket to the TFTP server + */ + + info->tftp_socket = udp_socket(UDP_PROTO_TFTP); + info->tftp_lastblock = 0; + info->tftp_error = 0; + info->tftp_filemode = mode; + + /* + * Try to send the RRQ packet to open the file + */ + + for (retries = 0; retries < TFTP_MAX_RETRIES; retries++) { + + buf = udp_alloc(); + if (!buf) break; + + if (info->tftp_filemode == FILE_MODE_READ) { + ebuf_append_u16_be(buf,TFTP_OP_RRQ); /* read file */ + } + else { + ebuf_append_u16_be(buf,TFTP_OP_WRQ); /* write file */ + } + ebuf_append_bytes(buf,filename,strlen(filename)+1); + ebuf_append_bytes(buf,datamode,strlen(datamode)+1); + + udp_send(info->tftp_socket,buf,info->tftp_ipaddr); + + buf = udp_recv_with_timeout(info->tftp_socket,TFTP_RRQ_TIMEOUT); + if (buf) break; + } + + /* + * If we got no response, bail now. + */ + + if (!buf) { + udp_close(info->tftp_socket); + info->tftp_socket = -1; + return CFE_ERR_TIMEOUT; + } + + /* + * Otherwise, process the response. + */ + + ebuf_get_u16_be(buf,type); + + switch (type) { + case TFTP_OP_ACK: + /* + * Acks are what we get back on a WRQ command, + * but are otherwise unexpected. + */ + if (info->tftp_filemode == FILE_MODE_WRITE) { + udp_connect(info->tftp_socket,(uint16_t) buf->eb_usrdata); + info->tftp_blknum = 1; + info->tftp_blklen = 0; + udp_free(buf); + return 0; + break; + } + /* fall through */ + case TFTP_OP_RRQ: + case TFTP_OP_WRQ: + default: + /* + * we aren't expecting any of these messages + */ + udp_free(buf); + udp_close(info->tftp_socket); + info->tftp_socket = -1; + return CFE_ERR_PROTOCOLERR; + + case TFTP_OP_ERROR: + /* + * Process the error return (XXX: remove xprintf here) + */ + ebuf_get_u16_be(buf,error); + xprintf("TFTP error %d: %s\n",error,ebuf_ptr(buf)); + udp_free(buf); + udp_close(info->tftp_socket); + info->tftp_socket = -1; + return CFE_ERR_PROTOCOLERR; + + case TFTP_OP_DATA: + /* + * Yay, we've got data! Store the first block. + */ + ebuf_get_u16_be(buf,block); + udp_connect(info->tftp_socket,(uint16_t) buf->eb_usrdata); + info->tftp_blknum = block; + info->tftp_blklen = ebuf_length(buf); + ebuf_get_bytes(buf,info->tftp_data,ebuf_length(buf)); + udp_free(buf); + if (info->tftp_blklen < TFTP_BLOCKSIZE) { + info->tftp_lastblock = 1; /* EOF */ + } + return 0; + break; + + } +} + + +/* ********************************************************************* + * _tftp_readmore(info) + * + * Read the next block of the file. We do this by acking the + * previous block. Once that block is acked, the TFTP server + * should send the next block to us. + * + * Input parameters: + * info - TFTP information + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int _tftp_readmore(tftp_info_t *info) +{ + ebuf_t *buf; + uint16_t cmd,block; + int retries; + + /* + * If we've already read the last block, there is no more + */ + + if (info->tftp_lastblock) return 1; + if (info->tftp_error) return CFE_ERR_TIMEOUT; + + /* + * Otherwise, ack the current block so another one will come + */ + + for (retries = 0; retries < TFTP_MAX_RETRIES; retries++) { + + buf = udp_alloc(); + if (!buf) return -1; + + /* + * Send the ack + */ + + ebuf_append_u16_be(buf,TFTP_OP_ACK); + ebuf_append_u16_be(buf,info->tftp_blknum); + udp_send(info->tftp_socket,buf,info->tftp_ipaddr); + + /* + * Wait for some response, retransmitting as necessary + */ + + /* Foxconn add start by Cliff Wang, 03/23/2010 */ + buf = udp_recv_with_timeout(info->tftp_socket,tftp_recv_timeout); + // buf = udp_recv_with_timeout(info->tftp_socket,TFTP_RECV_TIMEOUT); + /* Foxconn add end by Cliff Wang, 03/23/2010 */ + + if (buf == NULL) continue; + + /* + * Got a response, make sure it's right + */ + + ebuf_get_u16_be(buf,cmd); + if (cmd != TFTP_OP_DATA) { + udp_free(buf); + continue; + } + + ebuf_get_u16_be(buf,block); + if (block != (info->tftp_blknum+1)) { + udp_free(buf); + continue; + } + + /* + * It's the correct response. Copy the user data + */ + + info->tftp_blknum = block; + info->tftp_blklen = ebuf_length(buf); + ebuf_get_bytes(buf,info->tftp_data,ebuf_length(buf)); + udp_free(buf); + break; + } + + /* + * If the block is less than 512 bytes long, it's the EOF block. + */ + + if (retries == TFTP_MAX_RETRIES) { + info->tftp_error = 1; + return CFE_ERR_TIMEOUT; + } + + if (info->tftp_blklen < TFTP_BLOCKSIZE) { + info->tftp_lastblock = 1; /* EOF */ + } + + return 0; +} + +/* ********************************************************************* + * _tftp_writemore(info) + * + * Write the next block of the file, sending the data from our + * holding buffer. Note that the holding buffer must be full + * or else we consider this to be an EOF. + * + * Input parameters: + * info - TFTP information + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int _tftp_writemore(tftp_info_t *info) +{ + ebuf_t *buf; + uint16_t cmd,block,error; + int retries; + + /* + * If we've already written the last block, there is no more + */ + + if (info->tftp_lastblock) return 1; + if (info->tftp_error) return CFE_ERR_TIMEOUT; + + /* + * Otherwise, send a block + */ + + for (retries = 0; retries < TFTP_MAX_RETRIES; retries++) { + + buf = udp_alloc(); + if (!buf) return -1; + + /* + * Send the data + */ + + ebuf_append_u16_be(buf,TFTP_OP_DATA); + ebuf_append_u16_be(buf,info->tftp_blknum); + ebuf_append_bytes(buf,info->tftp_data,info->tftp_blklen); + udp_send(info->tftp_socket,buf,info->tftp_ipaddr); + + /* + * Wait for some response, retransmitting as necessary + */ + + /* Foxconn add start by Cliff Wang, 03/23/2010 */ + buf = udp_recv_with_timeout(info->tftp_socket, tftp_recv_timeout); + // buf = udp_recv_with_timeout(info->tftp_socket,TFTP_RECV_TIMEOUT); + /* Foxconn add end by Cliff Wang, 03/23/2010 */ + + if (buf == NULL) continue; + + /* + * Got a response, make sure it's right + */ + + ebuf_get_u16_be(buf,cmd); + + if (cmd == TFTP_OP_ERROR) { + /* + * Process the error return (XXX: remove xprintf here) + */ + ebuf_get_u16_be(buf,error); + xprintf("TFTP write error %d: %s\n",error,ebuf_ptr(buf)); + info->tftp_error = 1; + info->tftp_lastblock = 1; + udp_free(buf); + return CFE_ERR_IOERR; + } + + if (cmd != TFTP_OP_ACK) { + udp_free(buf); + continue; + } + + ebuf_get_u16_be(buf,block); + if (block != (info->tftp_blknum)) { + udp_free(buf); + continue; + } + + /* + * It's the correct response. Update the block # + */ + + info->tftp_blknum++; + if (info->tftp_blklen != TFTP_BLOCKSIZE) info->tftp_lastblock = 1; + udp_free(buf); + break; + } + + /* + * If we had some failure, mark the stream with an error + */ + + if (retries == TFTP_MAX_RETRIES) { + info->tftp_error = 1; + return CFE_ERR_TIMEOUT; + } + + return 0; +} + +/* Foxconn add start by Cliff Wang, 03/23/2010 */ +static int _tftpd_open(tftp_info_t *info,char *hostname,char *filename,int mode) +{ + ebuf_t *buf = NULL; + uint16_t type; + int res; + int retries; + char ch = 0; + + /* + * * Open a UDP socket + * */ + + info->tftp_socket = udp_socket(UDP_PROTO_TFTP); + info->tftp_lastblock = 0; + info->tftp_error = 0; + info->tftp_filemode = mode; + + /* + * * Listen for requests + * */ + + res = udp_bind(info->tftp_socket,UDP_PROTO_TFTP); + if (res < 0) return res; + + res = CFE_ERR_TIMEOUT; + + for (retries = 0; retries < tftp_max_retries; retries++) { + while (console_status()) { + console_read(&ch,1); + if (ch == 3) break; + } + + if (ch == 3) { + res = CFE_ERR_INTR; + break; + } + + buf = udp_recv_with_timeout(info->tftp_socket,tftp_recv_timeout); + if (buf == NULL) continue; + + /* + * * Process the request + * */ + + ebuf_get_u16_be(buf,type); + + switch (type) { + case TFTP_OP_RRQ: + udp_connect(info->tftp_socket,(uint16_t) buf->eb_usrdata); + memcpy(info->tftp_ipaddr,buf->eb_usrptr,IP_ADDR_LEN); + info->tftp_blknum = 1; + info->tftp_blklen = 0; + res = 0; + break; + + case TFTP_OP_WRQ: + udp_connect(info->tftp_socket,(uint16_t) buf->eb_usrdata); + memcpy(info->tftp_ipaddr,buf->eb_usrptr,IP_ADDR_LEN); + info->tftp_blknum = 0; + res = _tftp_readmore(info); + break; + + default: + /* + * aren't expecting any of these messages + * */ + res = CFE_ERR_PROTOCOLERR; + break; + } + + udp_free(buf); + break; + } + + if (res) { + udp_close(info->tftp_socket); + info->tftp_socket = -1; + } + + return res; +} + + +/* ********************************************************************* + * _tftp_close(info) + * + * Close a TFTP file. There are two cases for what we do + * here. If we're closing the file mid-stream, send an error + * packet to tell the host we're getting out early. Otherwise, + * just ack the final packet and the connection will close + * gracefully. + * + * Input parameters: + * info - TFTP info + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +static int _tftp_close(tftp_info_t *info) +{ + ebuf_t *buf; + const char *emsg = "transfer cancelled"; /* some error message */ + + if (info->tftp_socket == -1) return 0; + + if (info->tftp_filemode == FILE_MODE_READ) { + buf = udp_alloc(); + if (buf) { + /* If we're on the EOF packet, just send an ack */ + if (info->tftp_lastblock) { + ebuf_append_u16_be(buf,TFTP_OP_ACK); + ebuf_append_u16_be(buf,info->tftp_blknum); + } + else { + ebuf_append_u16_be(buf,TFTP_OP_ERROR); + ebuf_append_u16_be(buf,TFTP_ERR_DISKFULL); + ebuf_append_bytes(buf,emsg,strlen(emsg)+1); + } + udp_send(info->tftp_socket,buf,info->tftp_ipaddr); + } + } + else { + /* Just flush out the remaining write data, if any */ + _tftp_writemore(info); + } + + udp_close(info->tftp_socket); + info->tftp_socket = -1; + return 0; +} + + + +/* ********************************************************************* + * tftp_fileop_init(fsctx,device) + * + * Create a file system device context for the TFTP service + * + * Input parameters: + * fsctx - location to place context information + * device - underlying device (unused) + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int tftp_fileop_init(void **fsctx,void *dev) +{ + void *ref; + + ref = KMALLOC(sizeof(tftp_fsctx_t),0); + + if (!ref) return CFE_ERR_NOMEM; + *fsctx = ref; + return 0; +} + +/* ********************************************************************* + * tftp_fileop_open(ref,fsctx,filename,mode) + * + * This is the filesystem entry point for opening a TFTP file. + * Allocate a tftp_info structure, open the TFTP file, and + * return a handle. + * + * Input parameters: + * ref - location to place reference data (the TFTP structure) + * fsctx - filesystem context + * filename - name of remote file to open + * mode - FILE_MODE_READ or FILE_MODE_WRITE + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int tftp_fileop_open(void **ref,void *fsctx,char *filename,int mode) +{ + tftp_info_t *info; + char *host; + char *file; + int res; + + if ((mode != FILE_MODE_READ) && (mode != FILE_MODE_WRITE)) { + /* must be either read or write, not both */ + return CFE_ERR_UNSUPPORTED; + } + + /* Allocate the tftp info structure */ + + info = KMALLOC(sizeof(tftp_info_t),0); + if (!info) { + return CFE_ERR_NOMEM; + } + + + info->tftp_filename = lib_strdup(filename); + if (!info->tftp_filename) { + KFREE(info); + return CFE_ERR_NOMEM; + } + + lib_chop_filename(info->tftp_filename,&host,&file); + + /* Open the file */ + /* Foxconn add start by Cliff Wang, 03/23/2010 */ + if (!*host && !*file) { + /* TFTP server if hostname and filename are not specified */ + res = _tftpd_open(info,host,file,mode); + } else { + res = _tftp_open(info,host,file,mode); + } + /* Foxconn add end by Cliff Wang, 03/23/2010 */ + + if (res == 0) { + info->tftp_blkoffset = 0; + info->tftp_fileoffset = 0; + *ref = info; + } + else { + KFREE(info->tftp_filename); + KFREE(info); + *ref = NULL; + } + return res; +} + +/* ********************************************************************* + * tftp_fileop_read(ref,buf,len) + * + * Read some bytes from the remote TFTP file. Do this by copying + * data from the block buffer, and reading more data from the + * remote file as necessary. + * + * Input parameters: + * ref - tftp_info structure + * buf - destination buffer address (NULL to read data and + * not copy it anywhere) + * len - number of bytes to read + * + * Return value: + * number of bytes read, 0 for EOF, <0 if an error occured. + ********************************************************************* */ + +static int tftp_fileop_read(void *ref,uint8_t *buf,int len) +{ + tftp_info_t *info = (tftp_info_t *) ref; + int copied = 0; + int amtcopy; + int res; + + if (info->tftp_error) return CFE_ERR_IOERR; + if (info->tftp_filemode == FILE_MODE_WRITE) return CFE_ERR_UNSUPPORTED; + + while (len) { + + if (info->tftp_blkoffset >= info->tftp_blklen) break; + amtcopy = len; + + if (amtcopy > (info->tftp_blklen-info->tftp_blkoffset)) { + amtcopy = (info->tftp_blklen-info->tftp_blkoffset); + } + + if (buf) { + memcpy(buf,&(info->tftp_data[info->tftp_blkoffset]),amtcopy); + buf += amtcopy; + } + + info->tftp_blkoffset += amtcopy; + len -= amtcopy; + info->tftp_fileoffset += amtcopy; + copied += amtcopy; + + if (info->tftp_blkoffset >= info->tftp_blklen) { + res = _tftp_readmore(info); + if (res != 0) break; + info->tftp_blkoffset = 0; + } + } + + return copied; +} + +/* ********************************************************************* + * tftp_fileop_write(ref,buf,len) + * + * Write some bytes to the remote TFTP file. Do this by copying + * data to the block buffer, and writing data to the + * remote file as necessary. + * + * Input parameters: + * ref - tftp_info structure + * buf - source buffer address + * len - number of bytes to write + * + * Return value: + * number of bytes written, 0 for EOF, <0 if an error occured. + ********************************************************************* */ + +static int tftp_fileop_write(void *ref,uint8_t *buf,int len) +{ + tftp_info_t *info = (tftp_info_t *) ref; + int copied = 0; + int amtcopy; + int res; + + if (info->tftp_error) return CFE_ERR_IOERR; + if (info->tftp_filemode == FILE_MODE_READ) return CFE_ERR_UNSUPPORTED; + + while (len) { + + amtcopy = len; + if (amtcopy > (TFTP_BLOCKSIZE - info->tftp_blklen)) { + amtcopy = (TFTP_BLOCKSIZE - info->tftp_blklen); + } + + memcpy(&(info->tftp_data[info->tftp_blklen]),buf,amtcopy); + buf += amtcopy; + + info->tftp_blklen += amtcopy; + len -= amtcopy; + info->tftp_fileoffset += amtcopy; + copied += amtcopy; + + if (info->tftp_blklen == TFTP_BLOCKSIZE) { + res = _tftp_writemore(info); + if (res != 0) { + break; + } + info->tftp_blklen = 0; + } + } + + return copied; +} + +/* ********************************************************************* + * tftp_fileop_seek(ref,offset,how) + * + * Seek within a TFTP file. Note that you can only seek *forward*, + * as TFTP doesn't really let you go backwards. (I suppose you + * could reopen the file, but thus far nobody needs to go + * backwards). You can only seek in a file in read mode. + * + * Input parameters: + * ref - our tftp information + * offset - distance to move + * how - how to move, (FILE_SEEK_*) + * + * Return value: + * new offset, or <0 if an error occured. + ********************************************************************* */ + +static int tftp_fileop_seek(void *ref,int offset,int how) +{ + tftp_info_t *info = (tftp_info_t *) ref; + int delta; + int startloc; + int res; + + if (info->tftp_filemode == FILE_MODE_WRITE) return CFE_ERR_UNSUPPORTED; + + switch (how) { + case FILE_SEEK_BEGINNING: + startloc = info->tftp_fileoffset; + break; + case FILE_SEEK_CURRENT: + startloc = 0; + break; + default: + startloc = 0; + break; + } + + delta = offset - startloc; + if (delta < 0) { + xprintf("Warning: negative seek on tftp file attempted\n"); + return CFE_ERR_UNSUPPORTED; + } + res = tftp_fileop_read(ref,NULL,delta); + if (res < 0) return res; + + return info->tftp_fileoffset; +} + + +/* ********************************************************************* + * tftp_fileop_close(ref) + * + * Close the TFTP file. + * + * Input parameters: + * ref - our TFTP info + * + * Return value: + * nothing + ********************************************************************* */ + +static void tftp_fileop_close(void *ref) +{ + tftp_info_t *info = (tftp_info_t *) ref; + + _tftp_close(info); + + KFREE(info->tftp_filename); + KFREE(info); +} + + +/* ********************************************************************* + * tftp_fileop_uninit(fsctx) + * + * Uninitialize the filesystem context, freeing allocated + * resources. + * + * Input parameters: + * fsctx - our context + * + * Return value: + * nothing + ********************************************************************* */ + +static void tftp_fileop_uninit(void *fsctx) +{ + KFREE(fsctx); +} diff --git a/cfe/cfe/net/net_udp.c b/cfe/cfe/net/net_udp.c new file mode 100644 index 0000000..968081a --- /dev/null +++ b/cfe/cfe/net/net_udp.c @@ -0,0 +1,637 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * User Datagram Protocol File: net_udp.c + * + * This module implements the User Datagram Protocol (RFCxxxx) + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + + +#include "lib_types.h" +#include "lib_string.h" +#include "lib_queue.h" +#include "lib_malloc.h" +#include "lib_printf.h" + +#include "cfe_timer.h" + +#include "net_ebuf.h" +#include "net_ether.h" + +#include "net_ip.h" + +#include "cfe_error.h" + +/* ********************************************************************* + * Constants + ********************************************************************* */ + +#define UDP_HDR_LENGTH 8 +#define UDP_PORTBASE 1024 +#define UDP_MAX_PORTS 4 + +/* ********************************************************************* + * Types + ********************************************************************* */ + +typedef struct udp_port_s udp_port_t; + +/* + * UDP port structure - describes an open UDP port. + */ + +struct udp_port_s { + uint16_t up_destport; /* destination port number */ + uint16_t up_srcport; /* source port number */ + queue_t up_rxqueue; /* queue of received packets */ + int up_maxqueue; /* max # of elements on rx queue */ + int up_inuse; /* nonzero if port is in use */ +}; + + +/* + * UDP stack information - describes the entire UDP layer. + */ + +struct udp_info_s { + uint16_t ui_portbase; + void *ui_ref; + ip_info_t *ui_ipinfo; + udp_port_t ui_ports[UDP_MAX_PORTS]; +}; + +/* ********************************************************************* + * Forward declarations + ********************************************************************* */ + +static int _udp_rx_callback(void *ref,ebuf_t *buf,uint8_t *destaddr,uint8_t *srcaddr); + + +/* ********************************************************************* + * udp_find_port(info,port) + * + * Locate an open port. Scan the list of ports looking for one + * that is both open and has a matching source port number. + * + * Input parameters: + * info - UDP stack information + * port - source port # we're looking for + * + * Return value: + * udp_port_t pointer or NULL if no port was found + ********************************************************************* */ + +static udp_port_t *_udp_find_port(udp_info_t *info,uint16_t port) +{ + int idx; + udp_port_t *udp = info->ui_ports; + + for (idx = 0; idx < UDP_MAX_PORTS; idx++) { + if (udp->up_inuse && (udp->up_srcport == port)) { + return udp; + } + udp++; + } + + return NULL; +} + +/* ********************************************************************* + * _udp_socket(info,port) + * + * Open a UDP socket. This is an internal function used by + * the network API layer. + * + * Input parameters: + * info - UDP stack information + * port - port number to open + * + * Return value: + * port number (0 based) or an error code (<0) if an error + * occured. + ********************************************************************* */ + +int _udp_socket(udp_info_t *info,uint16_t port) +{ + extern int32_t _getticks(void); /* return value of CP0 COUNT */ + int idx; + udp_port_t *udp; + uint16_t srcport = UDP_PORTBASE + (_getticks() & 0xFFF); + + while (_udp_find_port(info,srcport)) { /* should always exit */ + srcport++; + if (srcport > (UDP_PORTBASE+4096)) srcport = UDP_PORTBASE; + } + + udp = info->ui_ports; + + for (idx = 0; idx < UDP_MAX_PORTS; idx++) { + if (!udp->up_inuse) break; + udp++; + } + + if (idx == UDP_MAX_PORTS) return CFE_ERR_NOHANDLES; + + udp->up_destport = port; + udp->up_srcport = srcport; + udp->up_maxqueue = 2; + udp->up_inuse = TRUE; + q_init(&(udp->up_rxqueue)); + + return idx; +} + + +/* ********************************************************************* + * _udp_close(info,s) + * + * Internal function to close an open UDP port. This routine is + * called by the high-level network API. + * + * Input parameters: + * info - UDP stack information + * s - an open UDP socket handle (returned from _udp_open) + * + * Return value: + * nothing + ********************************************************************* */ + +void _udp_close(udp_info_t *info,int s) +{ + udp_port_t *udp = &(info->ui_ports[s]); + ebuf_t *buf; + + while ((buf = (ebuf_t *) q_deqnext(&(udp->up_rxqueue)))) { + _ip_free(info->ui_ipinfo,buf); + } + + udp->up_srcport = 0; + udp->up_destport = 0; + udp->up_maxqueue = 0; + udp->up_inuse = FALSE; +} + +/* ********************************************************************* + * _udp_send(info,s,buf,dest) + * + * Transmit a UDP datagram. Note that we never send fragmented + * datagrams, so all datagrams must be less than the MTU. + * + * Input parameters: + * info - UDP stack information + * s - an open UDP socket handle (returned from _udp_open) + * buf - an ebuf to send + * dest - destination IP address + * + * Return value: + * 0 if packet was sent + * else error code + ********************************************************************* */ + +int _udp_send(udp_info_t *info,int s,ebuf_t *buf,uint8_t *dest) +{ + udp_port_t *udp = &(info->ui_ports[s]); + uint8_t *udphdr; + int udplen; + uint8_t pseudoheader[12]; + uint16_t cksum; + + /* + * Calculate the length of the IP datagram (includes UDP header) + */ + + udplen = ebuf_length(buf) + UDP_HDR_LENGTH; + + /* + * Build the pseudoheader, which is part of the checksum calculation + */ + + _ip_getaddr(info->ui_ipinfo,&pseudoheader[0]); + memcpy(&pseudoheader[4],dest,IP_ADDR_LEN); + pseudoheader[8] = 0; + pseudoheader[9] = IPPROTO_UDP; + pseudoheader[10] = (udplen >> 8) & 0xFF; + pseudoheader[11] = (udplen & 0xFF); + + /* + * Back up and build the actual UDP header in the packet + */ + + ebuf_seek(buf,-UDP_HDR_LENGTH); + udphdr = ebuf_ptr(buf); + + ebuf_put_u16_be(buf,udp->up_srcport); + ebuf_put_u16_be(buf,udp->up_destport); + ebuf_put_u16_be(buf,udplen); + ebuf_put_u16_be(buf,0); + + ebuf_prepend(buf,UDP_HDR_LENGTH); + + /* + * Checksum the packet and insert the checksum into the header + */ + + cksum = ip_chksum(0,pseudoheader,sizeof(pseudoheader)); + cksum = ip_chksum(cksum,udphdr,udplen); + cksum = ~cksum; + if (cksum == 0) cksum = 0xFFFF; + udphdr[6] = (cksum >> 8) & 0xFF; + udphdr[7] = (cksum & 0xFF); + + /* + * Off it goes! + */ + + _ip_send(info->ui_ipinfo,buf,dest,IPPROTO_UDP); + + return 0; +} + +/* ********************************************************************* + * _udp_bind(info,s,port) + * + * Bind a UDP socket to a particular port number. Basically, + * all this means is we set the "source" port number. + * + * Input parameters: + * info - UDP stack information + * s - an open UDP socket (from _udp_open) + * port - port number to assign to the UDP socket + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +int _udp_bind(udp_info_t *info,int s,uint16_t port) +{ + udp_port_t *udp = &(info->ui_ports[s]); + + if (_udp_find_port(info,port)) return CFE_ERR_ALREADYBOUND; + + udp->up_srcport = port; + + return 0; +} + + +/* ********************************************************************* + * _udp_connect(info,s,port) + * + * "connect" a UDP socket to a particular port number. Basically, + * this just sets the "destination" port number. It is used for + * protocols like TFTP where the destination port number changes + * after the port is open. + * + * Input parameters: + * info - UDP stack information + * s - an open UDP socket (from _udp_open) + * port - port number to assign to the UDP socket + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int _udp_connect(udp_info_t *info,int s,uint16_t port) +{ + udp_port_t *udp = &(info->ui_ports[s]); + + udp->up_destport = port; + + return 0; +} + +/* ********************************************************************* + * _udp_rx_callback(ref,buf,destaddr,srcaddr) + * + * Receive callback routine from the IP layer. When an IP + * packet of protocol type "UDP" is received, this routine gets + * called. + * + * Input parameters: + * ref - reference data (pointer to our UDP stack info) + * buf - the ebuf, currently pointing at the UDP header + * destaddr - the destination IP address (usually our IP address) + * srcaddr - the source IP address + * + * Return value: + * ETH_KEEP to keep (not deallocate) the packet + * ETH_DROP to deallocate the packet. + ********************************************************************* */ + +static int _udp_rx_callback(void *ref,ebuf_t *buf,uint8_t *destaddr,uint8_t *srcaddr) +{ + uint8_t pseudoheader[12]; + int udplen; + uint16_t calccksum; + uint16_t origcksum; + uint8_t *udphdr; + uint16_t srcport; + uint16_t dstport; + uint16_t udplen2; + udp_port_t *udp; + udp_info_t *info = (udp_info_t *) ref; + + + + /* + * get a pointer to the UDP header + */ + + udplen = ebuf_length(buf); + udphdr = ebuf_ptr(buf); + + /* + * see if we are checking checksums (cksum field != 0) + */ + + if ((udphdr[6] | udphdr[7]) != 0) { + + /* + * construct the pseudoheader for the cksum calculation + */ + + memcpy(&pseudoheader[0],srcaddr,IP_ADDR_LEN); + memcpy(&pseudoheader[4],destaddr,IP_ADDR_LEN); + pseudoheader[8] = 0; + pseudoheader[9] = IPPROTO_UDP; + pseudoheader[10] = (udplen >> 8) & 0xFF; + pseudoheader[11] = (udplen & 0xFF); + + origcksum = ((uint16_t) udphdr[6] << 8) | (uint16_t) udphdr[7]; + udphdr[6] = udphdr[7] = 0; + + calccksum = ip_chksum(0,pseudoheader,sizeof(pseudoheader)); + calccksum = ip_chksum(calccksum,udphdr,udplen); + if (calccksum != 0xffff) { + calccksum = ~calccksum; + } + + if (calccksum != origcksum) { + return ETH_DROP; + } + } + + /* Read the other UDP header fields from the packet */ + + ebuf_get_u16_be(buf,srcport); + ebuf_get_u16_be(buf,dstport); + ebuf_get_u16_be(buf,udplen2); + ebuf_skip(buf,2); + + /* + * It's no good if the lengths don't match. The length + * reported by IP should be the length in the UDP header + 8 + */ + + if (udplen2 != (uint16_t) udplen) { + return ETH_DROP; + } + + /* + * Okay, start looking for a matching port + */ + + udp = _udp_find_port(info,dstport); + if (!udp) { + return ETH_DROP; /* drop packet if no matching port */ + /* XXX should send ICMP message here */ + } + + buf->eb_usrdata = (int) srcport; + buf->eb_usrptr = srcaddr; + + /* + * Drop packet if queue is full + */ + + if (q_count(&(udp->up_rxqueue)) >= udp->up_maxqueue) { + return ETH_DROP; + } + + /* + * Add to receive queue + */ + + ebuf_setlength(buf,udplen2-UDP_HDR_LENGTH); + q_enqueue(&(udp->up_rxqueue),(queue_t *) buf); + + return ETH_KEEP; +} + + +/* ********************************************************************* + * _udp_recv(info,s) + * + * Receive a packet from the specified UDP socket. + * + * Input parameters: + * info - UDP stack information + * s - an open UDP socket handle (from _udp_open) + * + * Return value: + * an ebuf, or NULL if no packets have been received. + ********************************************************************* */ + +ebuf_t *_udp_recv(udp_info_t *info,int s) +{ + ebuf_t *buf; + udp_port_t *udp = &(info->ui_ports[s]); + + buf = (ebuf_t *) q_deqnext(&(udp->up_rxqueue)); + + return buf; +} + + +/* ********************************************************************* + * _udp_init(ipi,ref) + * + * Initialize the UDP module. This routine registers our + * protocol with the IP layer. + * + * Input parameters: + * ipi - IP information (including our IP address, etc.) + * ref - reference data, stored in our UDP stack structure + * + * Return value: + * udp_info_t (allocated) or NULL if something went wrong. + ********************************************************************* */ + +udp_info_t *_udp_init(ip_info_t *ipi,void *ref) +{ + udp_info_t *info; + udp_port_t *udp; + int idx; + + /* + * Allocate some memory for our structure + */ + + info = KMALLOC(sizeof(udp_info_t),0); + + if (info == NULL) return NULL; + + memset(info,0,sizeof(udp_info_t)); + + /* + * Fill in the fields. + */ + + info->ui_ref = ref; + info->ui_ipinfo = ipi; + udp = info->ui_ports; + for (idx = 0; idx < UDP_MAX_PORTS; idx++) { + udp->up_inuse = FALSE; + q_init(&(udp->up_rxqueue)); + udp++; + } + + /* + * Register our protocol with IP + */ + + _ip_register(ipi,IPPROTO_UDP,_udp_rx_callback,info); + + return info; +} + + +/* ********************************************************************* + * _udp_uninit(info) + * + * Uninitialize the UDP module, deregistering ourselves from the + * IP layer. + * + * Input parameters: + * info - UDP stack information + * + * Return value: + * nothing + ********************************************************************* */ + +void _udp_uninit(udp_info_t *info) +{ + int idx; + udp_port_t *udp; + ebuf_t *buf; + + /* + * Unregister from IP + */ + + _ip_deregister(info->ui_ipinfo,IPPROTO_UDP); + + /* + * Free up any packets that were waiting + */ + + udp = info->ui_ports; + for (idx = 0; idx < UDP_MAX_PORTS; idx++) { + if (udp->up_inuse) { + while ((buf = (ebuf_t *) q_deqnext(&(udp->up_rxqueue)))) { + _ip_free(info->ui_ipinfo,buf); + } + } + udp++; + } + + /* + * Free the stack info + */ + + KFREE(info); +} + +/* ********************************************************************* + * _udp_alloc(info) + * + * Allocate a buffer for use with UDP. This routine obtains an + * ebuf and adjusts its header to include room for the UDP + * header. + * + * Input parameters: + * info - UDP stack information + * + * Return value: + * ebuf, or NULL if there are none left + ********************************************************************* */ + +ebuf_t *_udp_alloc(udp_info_t *info) +{ + ebuf_t *buf; + + /* + * Get an ebuf + */ + + buf = _ip_alloc(info->ui_ipinfo); + + if (!buf) return NULL; + + /* + * make room for the udp header + */ + + ebuf_seek(buf,UDP_HDR_LENGTH); + ebuf_setlength(buf,0); + + return buf; +} + +/* ********************************************************************* + * _udp_free(info,buf) + * + * Return an ebuf to the pool. + * + * Input parameters: + * info - UDP stack info + * buf - an ebuf + * + * Return value: + * nothing + ********************************************************************* */ + +void _udp_free(udp_info_t *info,ebuf_t *buf) +{ + _ip_free(info->ui_ipinfo,buf); +} |