/*
// Copyright (c) 2017 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/

/*
 *	Filename - l2_proto.c
 *	L2 Protocol Handler
 */

#include "l2_proto.h"

static struct proto_packet_type *proto_list[3];
/*
 *	Function to register the rx functions for different ethertypes. This is maintained in a list.
 */
void
list_add_type(uint16_t type,
				void (*func) (struct rte_mbuf **, uint16_t, uint64_t,
					l2_phy_interface_t *))
{
	if (type == ETHER_TYPE_IPv4) {
		proto_list[IPv4_VAL] =
				rte_malloc(NULL, sizeof(struct proto_packet_type),
						 RTE_CACHE_LINE_SIZE);
		proto_list[IPv4_VAL]->type = type;
		proto_list[IPv4_VAL]->func = func;
	}

	else if (type == ETHER_TYPE_ARP) {
		proto_list[ARP_VAL] =
				rte_malloc(NULL, sizeof(struct proto_packet_type),
						 RTE_CACHE_LINE_SIZE);
		proto_list[ARP_VAL]->type = type;
		proto_list[ARP_VAL]->func = func;
	} else if (type == ETHER_TYPE_IPv6) {
		proto_list[IPv6_VAL] =
				rte_malloc(NULL, sizeof(struct proto_packet_type),
						 RTE_CACHE_LINE_SIZE);
		proto_list[IPv6_VAL]->type = type;
		proto_list[IPv6_VAL]->func = func;
	}

}

/*
 *	Check the mac address to see whether it is destined to this host or not.
 *	Call relevant functions registered by other modules when the ethertype matches,
 *	if it is destined to this host. Drop the packet otherwise.
 */

void
l2_check_mac(struct rte_mbuf *m[IFM_BURST_SIZE], l2_phy_interface_t *port,
			 uint8_t i, uint64_t *pkts_mask, uint64_t *arp_pkts_mask,
			 uint64_t *ipv4_pkts_mask, uint64_t *ipv6_pkts_mask)
{
	struct ether_hdr *eth=NULL;
	uint16_t same_mac=0;
	uint16_t ethtype = 0;

	if (m[i] != NULL) {
		eth = rte_pktmbuf_mtod(m[i], struct ether_hdr *);
		if(eth)
		ethtype = rte_be_to_cpu_16(eth->ether_type);
		if (eth == NULL) {
			/*Destination MAC address inside the packet */
			printf("l2_check_mac: Ethernet Dest Addr NULL !!!\n");
			return;
		}
		ethtype = rte_be_to_cpu_16(eth->ether_type);
#if L2_PROTO_DBG
		printf("%s => mbuf pkt dest mac addr: %x:%x:%x:%x:%x:%x\n",
					 __FUNCTION__, eth->d_addr.addr_bytes[0],
					 eth->d_addr.addr_bytes[1], eth->d_addr.addr_bytes[2],
					 eth->d_addr.addr_bytes[3], eth->d_addr.addr_bytes[4],
					 eth->d_addr.addr_bytes[5]);
		printf("%s => port mac addr: %x:%x:%x:%x:%x:%x\n", __FUNCTION__,
					 port->macaddr[0], port->macaddr[1], port->macaddr[2],
					 port->macaddr[3], port->macaddr[4], port->macaddr[5]);

#endif
		/*     Compare the mac addresses       */
		same_mac =
				(is_same_ether_addr
				 (&eth->d_addr, (struct ether_addr *)port->macaddr)
				 ||
				 ((is_broadcast_ether_addr
					 ((struct ether_addr *)&eth->d_addr)
					 && (ethtype == ETHER_TYPE_ARP)))
				 || (ethtype == ETHER_TYPE_IPv6
			 && eth->d_addr.addr_bytes[0] == 0x33
			 && eth->d_addr.addr_bytes[1] == 0x33));

		if (!same_mac) {
			uint64_t temp_mask = 1LLU << i;
			*pkts_mask ^= temp_mask;
			rte_pktmbuf_free(m[i]);
			m[i] = NULL;
		} else if ((ethtype == ETHER_TYPE_IPv4) && same_mac) {
			uint64_t temp_mask = 1LLU << i;
			*ipv4_pkts_mask ^= temp_mask;
		} else if ((ethtype == ETHER_TYPE_ARP) && same_mac) {
			uint64_t temp_mask = 1LLU << i;
			*arp_pkts_mask ^= temp_mask;
		} else if ((ethtype == ETHER_TYPE_IPv6) && same_mac) {
			uint64_t temp_mask = 1LLU << i;
			*ipv6_pkts_mask ^= temp_mask;
		}
	}
	printf("\n%s: arp_pkts_mask = %" PRIu64 ", ipv4_pkts_mask = %" PRIu64
				 ", ipv6_pkts_mask =%" PRIu64 ", pkt-type = %x, sam_mac = %d\n",
				 __FUNCTION__, *arp_pkts_mask, *ipv4_pkts_mask, *ipv6_pkts_mask,
				 ethtype, same_mac);
}

void
protocol_handler_recv(struct rte_mbuf **pkts_burst, uint16_t nb_rx,
					l2_phy_interface_t *port)
{
	uint8_t i;
	uint64_t pkts_mask = 0;	//RTE_LEN2MASK(nb_rx, uint64_t);
	uint64_t arp_pkts_mask = 0;	//RTE_LEN2MASK(nb_rx, uint64_t);
	uint64_t ipv4_pkts_mask = 0;	//RTE_LEN2MASK(nb_rx, uint64_t);
	uint64_t ipv6_pkts_mask = 0;	//RTE_LEN2MASK(nb_rx, uint64_t);

	/*Check the mac address of every single packet and unset the bits in the packet mask
	 *for those packets which are not destined to this host
	 */
	for (i = 0; i < nb_rx; i++) {
		l2_check_mac(pkts_burst, port, i, &pkts_mask, &arp_pkts_mask,
					 &ipv4_pkts_mask, &ipv6_pkts_mask);
	}
	if (nb_rx) {
		if (arp_pkts_mask) {
			proto_list[ARP_VAL]->func(pkts_burst, nb_rx,
							arp_pkts_mask, port);
			printf
					("=================After ARP ==================\n");
		}
		if (ipv4_pkts_mask) {
			printf
					("=================Calling IPV4 L3 RX ==================\n");
			printf("====nb_rx:%u, ipv4_pkts_mask: %lu\n\n", nb_rx,
						 ipv4_pkts_mask);
			proto_list[IPv4_VAL]->func(pkts_burst, nb_rx,
							 ipv4_pkts_mask, port);
		}
		if (ipv6_pkts_mask) {
			printf
					("=================Calling IPV6 L3 RX ==================\n");
			printf("====nb_rx:%u, ipv6_pkts_mask: %lu\n\n", nb_rx,
						 ipv6_pkts_mask);
			proto_list[IPv6_VAL]->func(pkts_burst, nb_rx,
							 ipv6_pkts_mask, port);
		}
	}
}

#if 0
switch (qid) {
case 1:
	{
#if 0
		printf
				("=====================ENTERED ARP CASE================\n");
		while (cur->type != ETHER_TYPE_ARP && cur != NULL) {
			cur = cur->next;
		}
		if (cur != NULL) {
			//printf("L2 PROTO TEST-14=================================\n");
			printf
					("==============\nARPARPARPARP  \n=======================\n");
			cur->func(pkts_burst, nb_rx, pkts_mask, portid);
		}
#endif
		proto_list[ARP_VAL]->func(pkts_burst, nb_rx, arp_pkts_mask,
						portid);
		break;
	}
case 0:
	{
#if 0
		while (cur->type != ETHER_TYPE_IPv4 && cur != NULL) {
			cur = cur->next;
		}
		if (cur != NULL) {
			//printf("L2 PROTO TEST-15=================================\n");
			//printf("==============\nPkts mask in while calling IPv4 %d \n=======================\n",ipv4_pkts_mask);
			cur->func(pkts_burst, nb_rx, ipv4_pkts_mask, portid);
		}
		break;
#endif
		//      printf("=========Inside switch==============\n");
		proto_list[IPv4_VAL]->func(pkts_burst, nb_rx, ipv4_pkts_mask,
						 portid);
		break;
	}
	/*     case 2:
		 {
		 while(cur->type != ETHER_TYPE_IPv6  && cur != NULL)
		 {
		 cur = cur->next;
		 }
		 if(cur != NULL)
		 {
		 cur->func(pkts_burst, nb_rx, ipv6_pkts_mask, portid);
		 }
		 break;
		 } */
default:
	{
		rte_exit(EXIT_FAILURE, "Ethertype not found \n");
		break;
	}
}
#endif

/*
 *	L2 Stack Init for future


	void
l2_stack_init(void)
{

}

*/