//==========================================================================
//
//      net/ip.c
//
//      Stand-alone IP networking support for RedBoot
//
//==========================================================================
// ####ECOSGPLCOPYRIGHTBEGIN####                                            
// -------------------------------------------                              
// This file is part of eCos, the Embedded Configurable Operating System.   
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under    
// the terms of the GNU General Public License as published by the Free     
// Software Foundation; either version 2 or (at your option) any later      
// version.                                                                 
//
// eCos is distributed in the hope that it will be useful, but WITHOUT      
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or    
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License    
// for more details.                                                        
//
// You should have received a copy of the GNU General Public License        
// along with eCos; if not, write to the Free Software Foundation, Inc.,    
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
//
// As a special exception, if other files instantiate templates or use      
// macros or inline functions from this file, or you compile this file      
// and link it with other works to produce a work based on this file,       
// this file does not by itself cause the resulting work to be covered by   
// the GNU General Public License. However the source code for this file    
// must still be made available in accordance with section (3) of the GNU   
// General Public License v2.                                               
//
// This exception does not invalidate any other reasons why a work based    
// on this file might be covered by the GNU General Public License.         
// -------------------------------------------                              
// ####ECOSGPLCOPYRIGHTEND####                                              
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):    gthomas
// Contributors: gthomas
// Date:         2000-07-14
// Purpose:      
// Description:  
//              
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================

#include <net/net.h>

#ifndef CYGDAT_REDBOOT_DEFAULT_IP_ADDR
# define CYGDAT_REDBOOT_DEFAULT_IP_ADDR 0, 0, 0, 0
#endif
#ifndef CYGDAT_REDBOOT_DEFAULT_IP_ADDR_MASK
# define CYGDAT_REDBOOT_DEFAULT_IP_ADDR_MASK 255, 255, 255, 0
#endif
#ifndef CYGDAT_REDBOOT_DEFAULT_GATEWAY_IP_ADDR
# define CYGDAT_REDBOOT_DEFAULT_GATEWAY_IP_ADDR 0, 0, 0, 0
#endif

ip_addr_t __local_ip_addr = { CYGDAT_REDBOOT_DEFAULT_IP_ADDR };
#ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
ip_addr_t __local_ip_mask = { CYGDAT_REDBOOT_DEFAULT_IP_ADDR_MASK };
ip_addr_t __local_ip_gate = { CYGDAT_REDBOOT_DEFAULT_GATEWAY_IP_ADDR };
#endif

static word ip_ident;

#ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
/*
 * See if an address is on the local network
 */
int 
__ip_addr_local(ip_addr_t *addr)
{
  return !(
           ((__local_ip_addr[0] ^ (*addr)[0]) & __local_ip_mask[0]) |
           ((__local_ip_addr[1] ^ (*addr)[1]) & __local_ip_mask[1]) |
           ((__local_ip_addr[2] ^ (*addr)[2]) & __local_ip_mask[2]) |
           ((__local_ip_addr[3] ^ (*addr)[3]) & __local_ip_mask[3]));
}
#endif

/*
 * Match given IP address to our address.
 * Check for broadcast matches as well.
 */
static int
ip_addr_match(ip_addr_t addr)
{
    if (addr[0] == 255 && addr[1] == 255 && addr[2] == 255 && addr[3] == 255)
	return 1;

    if (!memcmp(addr, __local_ip_addr, sizeof(ip_addr_t)))
	return 1;

    /*
     * Consider it an address match if we haven't gotten our IP address yet.
     * Some DHCP servers will address IP packets to the assigned address
     * instead of a IP broadcast address.
     */
    if (__local_ip_addr[0] == 0 && __local_ip_addr[1] == 0 &&
	__local_ip_addr[2] == 0 && __local_ip_addr[3] == 0)
	return 1;

    return 0;
}


extern void __tcp_handler(pktbuf_t *, ip_route_t *);

/*
 * Handle IP packets coming from the polled ethernet interface.
 */
void
__ip_handler(pktbuf_t *pkt, enet_addr_t *src_enet_addr)
{
    ip_header_t *ip = pkt->ip_hdr;
    ip_route_t  r;
    int         hdr_bytes;

    /* first make sure its ours and has a good checksum. */
    if (!ip_addr_match(ip->destination) ||
	__sum((word *)ip, ip->hdr_len << 2, 0) != 0) {
	__pktbuf_free(pkt);
	return;
    }

    memcpy(r.ip_addr, ip->source, sizeof(ip_addr_t));
    memcpy(r.enet_addr, src_enet_addr, sizeof(enet_addr_t));

    hdr_bytes = ip->hdr_len << 2;
    pkt->pkt_bytes = ntohs(ip->length) - hdr_bytes;

    switch (ip->protocol) {

#if NET_SUPPORT_ICMP
      case IP_PROTO_ICMP:
	pkt->icmp_hdr = (icmp_header_t *)(((char *)ip) + hdr_bytes);
	__icmp_handler(pkt, &r);
	break;
#endif

#if NET_SUPPORT_TCP
      case IP_PROTO_TCP:
	pkt->tcp_hdr = (tcp_header_t *)(((char *)ip) + hdr_bytes);
	__tcp_handler(pkt, &r);
	break;
#endif

#if NET_SUPPORT_UDP
      case IP_PROTO_UDP:
	pkt->udp_hdr = (udp_header_t *)(((char *)ip) + hdr_bytes);
	__udp_handler(pkt, &r);
	break;
#endif

      default:
	__pktbuf_free(pkt);
	break;
    }
}


/*
 * Send an IP packet.
 *
 * The IP data field should contain pkt->pkt_bytes of data.
 * pkt->[udp|tcp|icmp]_hdr points to the IP data field. Any
 * IP options are assumed to be already in place in the IP
 * options field.
 */
int
__ip_send(pktbuf_t *pkt, int protocol, ip_route_t *dest)
{
    ip_header_t *ip = pkt->ip_hdr;
    int         hdr_bytes;
    unsigned short cksum;
    
    /*
     * Figure out header length. The use udp_hdr is
     * somewhat arbitrary, but works because it is
     * a union with other IP protocol headers.
     */
    hdr_bytes = (((char *)pkt->udp_hdr) - ((char *)ip));

    pkt->pkt_bytes += hdr_bytes;

    ip->version = 4;
    ip->hdr_len = hdr_bytes >> 2;
    ip->tos = 0;
    ip->length = htons(pkt->pkt_bytes);
    ip->ident = htons(ip_ident);
    ip_ident++;
    ip->fragment = 0;
    ip->ttl = 255;
    ip->ttl = 64;
    ip->protocol = protocol;
    ip->checksum = 0;
    memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
    memcpy(ip->destination, dest->ip_addr, sizeof(ip_addr_t));
    cksum = __sum((word *)ip, hdr_bytes, 0);
    ip->checksum = htons(cksum);

    __enet_send(pkt, &dest->enet_addr, ETH_TYPE_IP);    
    return 0;
}


