diff options
Diffstat (limited to 'cfe/cfe/net/net_ip.c')
-rwxr-xr-x | cfe/cfe/net/net_ip.c | 722 |
1 files changed, 722 insertions, 0 deletions
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; + } + } +} + |