/*
 * Copyright (c) 2001-2003, Adam Dunkels.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote
 *    products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * 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) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This file is part of the uIP TCP/IP stack.
 *
 * $Id: uip_arp.c,v 1.8 2006/06/02 23:36:21 adam Exp $
 *
 */


#include "uip_arp.h"


#include <string.h>

#ifdef __ICCARM__
	#pragma pack(1)
#endif

struct arp_hdr
{
  struct uip_eth_hdr ethhdr;
  u16_t hwtype;
  u16_t protocol;
  u8_t hwlen;
  u8_t protolen;
  u16_t opcode;
  struct NyLPC_TEthAddr shwaddr;
  struct NyLPC_TIPv4Addr sipaddr;
  struct NyLPC_TEthAddr dhwaddr;
  struct NyLPC_TIPv4Addr dipaddr;
} PACK_STRUCT_END;

#ifdef __ICCARM__
	#pragma pack()
#endif

#ifdef __ICCARM__
	#pragma pack(1)
#endif

struct ethip_hdr {
  struct uip_eth_hdr ethhdr;
  /* IP header. */
  u8_t vhl,
    tos,
    len[2],
    ipid[2],
    ipoffset[2],
    ttl,
    proto;
  u16_t ipchksum;
  struct NyLPC_TIPv4Addr srcipaddr;
  struct NyLPC_TIPv4Addr destipaddr;
} PACK_STRUCT_END;

#ifdef __ICCARM__
	#pragma pack()
#endif

#define ARP_REQUEST 1
#define ARP_REPLY   2

#define ARP_HWTYPE_ETH 1

struct arp_entry {
	struct NyLPC_TIPv4Addr ipaddr;
	struct NyLPC_TEthAddr ethaddr;
	u8_t time;
};

static const struct NyLPC_TEthAddr broadcast_ethaddr =
  {{0xff,0xff,0xff,0xff,0xff,0xff}};
static const struct NyLPC_TIPv4Addr broadcast_ipaddr = {0xfffffff};

static struct arp_entry arp_table[UIP_ARPTAB_SIZE];
static u8_t i, c;

static u8_t arptime;
static u8_t tmpage;
const static NyLPC_TcIPv4Config_t* cfg;
/*-----------------------------------------------------------------------------------*/
/**
 * Initialize the ARP module.
 *
 */
/*-----------------------------------------------------------------------------------*/
void
uip_arp_init(const NyLPC_TcIPv4Config_t* i_ref_config)
{
	cfg=i_ref_config;
	for(i = 0; i < UIP_ARPTAB_SIZE; ++i) {
		memset(&(arp_table[i].ipaddr), 0, sizeof(struct NyLPC_TIPv4Addr));
	}
}
/*-----------------------------------------------------------------------------------*/
/**
 * Periodic ARP processing function.
 *
 * This function performs periodic timer processing in the ARP module
 * and should be called at regular intervals. The recommended interval
 * is 10 seconds between the calls.
 *
 */
/*-----------------------------------------------------------------------------------*/
void
uip_arp_timer(void)
{
  struct arp_entry *tabptr;

  ++arptime;
  for(i = 0; i < UIP_ARPTAB_SIZE; ++i) {
    tabptr = &arp_table[i];
    if(tabptr->ipaddr.v!=0 && arptime - tabptr->time >= UIP_ARP_MAXAGE) {
    	tabptr->ipaddr.v=0;
    }
  }

}
/*-----------------------------------------------------------------------------------*/
static void
uip_arp_update(const struct NyLPC_TIPv4Addr* ipaddr, const struct NyLPC_TEthAddr *ethaddr)
{
	register struct arp_entry *tabptr;
	/* Walk through the ARP mapping table and try to find an entry to
	update. If none is found, the IP -> MAC address mapping is
	inserted in the ARP table. */
	for(i = 0; i < UIP_ARPTAB_SIZE; ++i) {
		tabptr = &arp_table[i];
		/* Only check those entries that are actually in use. */
		if(tabptr->ipaddr.v != 0)
		{
			/* Check if the source IP address of the incoming packet matches
			the IP address in this ARP table entry. */
			if(ipaddr->v == tabptr->ipaddr.v) {
				/* An old entry found, update this and return. */
				memcpy(tabptr->ethaddr.addr, ethaddr->addr, 6);
				tabptr->time = arptime;

				return;
			}
		}
	}

	/* If we get here, no existing ARP table entry was found, so we
	create one. */
	/* First, we try to find an unused entry in the ARP table. */
	for(i = 0; i < UIP_ARPTAB_SIZE; ++i) {
		tabptr = &arp_table[i];
		if(tabptr->ipaddr.v==0)
		{
			break;
		}
	}

	/* If no unused entry is found, we try to find the oldest entry and
	throw it away. */
	if(i == UIP_ARPTAB_SIZE) {
		tmpage = 0;
		c = 0;
		for(i = 0; i < UIP_ARPTAB_SIZE; ++i) {
			tabptr = &arp_table[i];
			if(arptime - tabptr->time > tmpage) {
				tmpage = arptime - tabptr->time;
				c = i;
			}
		}
		i = c;
		tabptr = &arp_table[i];
	}
	
	/* Now, i is the ARP table entry which we will fill with the new information. */
	tabptr->ipaddr=*ipaddr;
	memcpy(tabptr->ethaddr.addr, ethaddr->addr, 6);
	tabptr->time = arptime;
}
/*-----------------------------------------------------------------------------------*/
/**
 * ARP processing for incoming IP packets
 *
 * This function should be called by the device driver when an IP
 * packet has been received. The function will check if the address is
 * in the ARP cache, and if so the ARP cache entry will be
 * refreshed. If no ARP cache entry was found, a new one is created.
 *
 * This function expects an IP packet with a prepended Ethernet header
 * in the uip_buf[] buffer, and the length of the packet in the global
 * variable uip_len.
 */
/*-----------------------------------------------------------------------------------*/
#if 1
void
uip_arp_ipin(const struct NyLPC_TEthernetIIHeader* i_eth,struct NyLPC_TIPv4Addr i_ip_src)
{
	//EtherとIPv4の値よ読みだす。
	/* Only insert/update an entry if the source IP address of the
	incoming IP packet comes from a host on the local network. */
	if((i_ip_src.v & cfg->netmask.v) != (cfg->ip_addr.v & cfg->netmask.v))
	{
		return;
	}
	uip_arp_update(&(i_ip_src), &(i_eth->src));
	return;
}
#endif /* 0 */
/*-----------------------------------------------------------------------------------*/
/**
 * ARP processing for incoming ARP packets.
 *
 * This function should be called by the device driver when an ARP
 * packet has been received. The function will act differently
 * depending on the ARP packet type: if it is a reply for a request
 * that we previously sent out, the ARP cache will be filled in with
 * the values from the ARP reply. If the incoming ARP packet is an ARP
 * request for our IP address, an ARP reply packet is created and put
 * into the uip_buf[] buffer.
 *
 * When the function returns, the value of the global variable uip_len
 * indicates whether the device driver should send out a packet or
 * not. If uip_len is zero, no packet should be sent. If uip_len is
 * non-zero, it contains the length of the outbound packet that is
 * present in the uip_buf[] buffer.
 *
 * This function expects an ARP packet with a prepended Ethernet
 * header in the uip_buf[] buffer, and the length of the packet in the
 * global variable uip_len.
 */
/*-----------------------------------------------------------------------------------*/






/**
 * arpパケットを処理します。
 */
NyLPC_TBool uip_arp_arpin(struct NyLPC_TArpHeader* i_arp,u16_t i_len)
{
	if(i_len<sizeof(struct NyLPC_TArpHeader))
	{
		return 0;
	}
	switch(i_arp->opcode) {
	case NyLPC_HTONS(ARP_REQUEST):
		/* ARP request. If it asked for our address, we send out a reply. */
		if(NyLPC_TIPv4Addr_isEqual(&(i_arp->dipaddr), &(cfg->ip_addr)))
		{
			/* First, we register the one who made the request in our ARP
			table, since it is likely that we will do more communication
			with this host in the future. */
			uip_arp_update(&(i_arp->sipaddr), &i_arp->shwaddr);

			/* The reply opcode is 2. */
			i_arp->opcode = NyLPC_HTONS(2);

			memcpy(i_arp->dhwaddr.addr, i_arp->shwaddr.addr, 6);
			memcpy(i_arp->shwaddr.addr, cfg->eth_mac.addr, 6);

			i_arp->dipaddr = i_arp->sipaddr;
			i_arp->sipaddr = cfg->ip_addr;

			return NyLPC_TBool_TRUE;
		}
		break;
	case NyLPC_HTONS(ARP_REPLY):
		// ARP reply. We insert or update the ARP table if it was meant for us.
		if(NyLPC_TIPv4Addr_isEqual(&(i_arp->dipaddr),&(cfg->ip_addr))) {
			uip_arp_update(&(i_arp->sipaddr), &i_arp->shwaddr);
		}
		return NyLPC_TBool_TRUE;
	}
	return NyLPC_TBool_FALSE;
}
/*-----------------------------------------------------------------------------------*/
/**
 * Prepend Ethernet header to an outbound IP packet and see if we need
 * to send out an ARP request.
 *
 * This function should be called before sending out an IP packet. The
 * function checks the destination IP address of the IP packet to see
 * what Ethernet MAC address that should be used as a destination MAC
 * address on the Ethernet.
 *
 * If the destination IP address is in the local network (determined
 * by logical ANDing of netmask and our IP address), the function
 * checks the ARP cache to see if an entry for the destination IP
 * address is found. If so, an Ethernet header is prepended and the
 * function returns. If no ARP cache entry is found for the
 * destination IP address, the packet in the uip_buf[] is replaced by
 * an ARP request packet for the IP address. The IP packet is dropped
 * and it is assumed that they higher level protocols (e.g., TCP)
 * eventually will retransmit the dropped packet.
 *
 * If the destination IP address is not on the local network, the IP
 * address of the default router is used instead.
 *
 * When the function returns, a packet is present in the uip_buf[]
 * buffer, and the length of the packet is in the global variable
 * uip_len.
 */
/*-----------------------------------------------------------------------------------*/

/**
 * IPアドレス-MACアドレス交換
 */
const struct NyLPC_TEthAddr* uip_arp_IPv4toEthAddr(
	const struct NyLPC_TIPv4Addr i_ip_addr)
{
	int i;
	struct NyLPC_TIPv4Addr ip;
	struct arp_entry *tabptr;
	//ブロードキャストならそのまま
	if(NyLPC_TIPv4Addr_isEqual(&i_ip_addr,&broadcast_ipaddr))
	{
		return &broadcast_ethaddr;
	}
	//LocalIPでなければ、デフォルトゲートウェイのアドレスに設定。
	if(!NyLPC_cIPv4Config_isLocalIP(cfg,&i_ip_addr)){
		ip=cfg->dr_addr;
	}else{
		ip=i_ip_addr;
	}
	//ARPテーブルから検索
	for(i = UIP_ARPTAB_SIZE-1; i>=0 ;i--) {
		tabptr = &arp_table[i];
		if(NyLPC_TIPv4Addr_isEqual(&ip,&(tabptr->ipaddr))) {
			return &tabptr->ethaddr;
		}
	}
	return NULL;
}










/*-----------------------------------------------------------------------------------*/

/** @} */
/** @} */
