/*
// 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.
*/
#include "lib_acl.h"
#include "vnf_common.h"
#include <rte_port.h>
#define ACL_LIB_DEBUG 0
static struct rte_acl_field_def field_format_ipv4[] = {
	/* Protocol */
	[0] = {
				 .type = RTE_ACL_FIELD_TYPE_BITMASK,
				 .size = sizeof(uint8_t),
				 .field_index = 0,
				 .input_index = 0,
				 .offset = sizeof(struct ether_hdr) +
				 offsetof(struct ipv4_hdr, next_proto_id),
				 },

	/* Source IP address (IPv4) */
	[1] = {
				 .type = RTE_ACL_FIELD_TYPE_MASK,
				 .size = sizeof(uint32_t),
				 .field_index = 1,
				 .input_index = 1,
				 .offset = sizeof(struct ether_hdr) +
				 offsetof(struct ipv4_hdr, src_addr),
				 },

	/* Destination IP address (IPv4) */
	[2] = {
				 .type = RTE_ACL_FIELD_TYPE_MASK,
				 .size = sizeof(uint32_t),
				 .field_index = 2,
				 .input_index = 2,
				 .offset = sizeof(struct ether_hdr) +
				 offsetof(struct ipv4_hdr, dst_addr),
				 },

	/* Source Port */
	[3] = {
				 .type = RTE_ACL_FIELD_TYPE_RANGE,
				 .size = sizeof(uint16_t),
				 .field_index = 3,
				 .input_index = 3,
				 .offset = sizeof(struct ether_hdr) +
				 sizeof(struct ipv4_hdr) + offsetof(struct tcp_hdr, src_port),
				 },

	/* Destination Port */
	[4] = {
				 .type = RTE_ACL_FIELD_TYPE_RANGE,
				 .size = sizeof(uint16_t),
				 .field_index = 4,
				 .input_index = 3,
				 .offset = sizeof(struct ether_hdr) +
				 sizeof(struct ipv4_hdr) + offsetof(struct tcp_hdr, dst_port),
				 },
};

#define SIZEOF_VLAN_HDR                          4

static struct rte_acl_field_def field_format_vlan_ipv4[] = {
	/* Protocol */
	[0] = {
				 .type = RTE_ACL_FIELD_TYPE_BITMASK,
				 .size = sizeof(uint8_t),
				 .field_index = 0,
				 .input_index = 0,
				 .offset = sizeof(struct ether_hdr) +
				 SIZEOF_VLAN_HDR + offsetof(struct ipv4_hdr, next_proto_id),
				 },

	/* Source IP address (IPv4) */
	[1] = {
				 .type = RTE_ACL_FIELD_TYPE_MASK,
				 .size = sizeof(uint32_t),
				 .field_index = 1,
				 .input_index = 1,
				 .offset = sizeof(struct ether_hdr) +
				 SIZEOF_VLAN_HDR + offsetof(struct ipv4_hdr, src_addr),
				 },

	/* Destination IP address (IPv4) */
	[2] = {
				 .type = RTE_ACL_FIELD_TYPE_MASK,
				 .size = sizeof(uint32_t),
				 .field_index = 2,
				 .input_index = 2,
				 .offset = sizeof(struct ether_hdr) +
				 SIZEOF_VLAN_HDR + offsetof(struct ipv4_hdr, dst_addr),
				 },

	/* Source Port */
	[3] = {
				 .type = RTE_ACL_FIELD_TYPE_RANGE,
				 .size = sizeof(uint16_t),
				 .field_index = 3,
				 .input_index = 3,
				 .offset = sizeof(struct ether_hdr) +
				 SIZEOF_VLAN_HDR +
				 sizeof(struct ipv4_hdr) + offsetof(struct tcp_hdr, src_port),
				 },

	/* Destination Port */
	[4] = {
				 .type = RTE_ACL_FIELD_TYPE_RANGE,
				 .size = sizeof(uint16_t),
				 .field_index = 4,
				 .input_index = 4,
				 .offset = sizeof(struct ether_hdr) +
				 SIZEOF_VLAN_HDR +
				 sizeof(struct ipv4_hdr) + offsetof(struct tcp_hdr, dst_port),
				 },
};

#define SIZEOF_QINQ_HEADER                       8

static struct rte_acl_field_def field_format_qinq_ipv4[] = {
	/* Protocol */
	[0] = {
				 .type = RTE_ACL_FIELD_TYPE_BITMASK,
				 .size = sizeof(uint8_t),
				 .field_index = 0,
				 .input_index = 0,
				 .offset = sizeof(struct ether_hdr) +
				 SIZEOF_QINQ_HEADER + offsetof(struct ipv4_hdr, next_proto_id),
				 },

	/* Source IP address (IPv4) */
	[1] = {
				 .type = RTE_ACL_FIELD_TYPE_MASK,
				 .size = sizeof(uint32_t),
				 .field_index = 1,
				 .input_index = 1,
				 .offset = sizeof(struct ether_hdr) +
				 SIZEOF_QINQ_HEADER + offsetof(struct ipv4_hdr, src_addr),
				 },

	/* Destination IP address (IPv4) */
	[2] = {
				 .type = RTE_ACL_FIELD_TYPE_MASK,
				 .size = sizeof(uint32_t),
				 .field_index = 2,
				 .input_index = 2,
				 .offset = sizeof(struct ether_hdr) +
				 SIZEOF_QINQ_HEADER + offsetof(struct ipv4_hdr, dst_addr),
				 },

	/* Source Port */
	[3] = {
				 .type = RTE_ACL_FIELD_TYPE_RANGE,
				 .size = sizeof(uint16_t),
				 .field_index = 3,
				 .input_index = 3,
				 .offset = sizeof(struct ether_hdr) +
				 SIZEOF_QINQ_HEADER +
				 sizeof(struct ipv4_hdr) + offsetof(struct tcp_hdr, src_port),
				 },

	/* Destination Port */
	[4] = {
				 .type = RTE_ACL_FIELD_TYPE_RANGE,
				 .size = sizeof(uint16_t),
				 .field_index = 4,
				 .input_index = 4,
				 .offset = sizeof(struct ether_hdr) +
				 SIZEOF_QINQ_HEADER +
				 sizeof(struct ipv4_hdr) + offsetof(struct tcp_hdr, dst_port),
				 },
};

static struct rte_acl_field_def field_format_ipv6[] = {
	/* Protocol */
	[0] = {
				 .type = RTE_ACL_FIELD_TYPE_BITMASK,
				 .size = sizeof(uint8_t),
				 .field_index = 0,
				 .input_index = 0,
				 .offset = sizeof(struct ether_hdr) +
				 offsetof(struct ipv6_hdr, proto),
				 },

	/* Source IP address (IPv6) */
	[1] = {
				 .type = RTE_ACL_FIELD_TYPE_MASK,
				 .size = sizeof(uint32_t),
				 .field_index = 1,
				 .input_index = 1,
				 .offset = sizeof(struct ether_hdr) +
				 offsetof(struct ipv6_hdr, src_addr),
				 },

	[2] = {
				 .type = RTE_ACL_FIELD_TYPE_MASK,
				 .size = sizeof(uint32_t),
				 .field_index = 2,
				 .input_index = 2,
				 .offset = sizeof(struct ether_hdr) +
				 offsetof(struct ipv6_hdr, src_addr) + sizeof(uint32_t),
				 },

	[3] = {
				 .type = RTE_ACL_FIELD_TYPE_MASK,
				 .size = sizeof(uint32_t),
				 .field_index = 3,
				 .input_index = 3,
				 .offset = sizeof(struct ether_hdr) +
				 offsetof(struct ipv6_hdr, src_addr) + 2 * sizeof(uint32_t),
				 },

	[4] = {
				 .type = RTE_ACL_FIELD_TYPE_MASK,
				 .size = sizeof(uint32_t),
				 .field_index = 4,
				 .input_index = 4,
				 .offset = sizeof(struct ether_hdr) +
				 offsetof(struct ipv6_hdr, src_addr) + 3 * sizeof(uint32_t),
				 },

	/* Destination IP address (IPv6) */
	[5] = {
				 .type = RTE_ACL_FIELD_TYPE_MASK,
				 .size = sizeof(uint32_t),
				 .field_index = 5,
				 .input_index = 5,
				 .offset = sizeof(struct ether_hdr) +
				 offsetof(struct ipv6_hdr, dst_addr),
				 },

	[6] = {
				 .type = RTE_ACL_FIELD_TYPE_MASK,
				 .size = sizeof(uint32_t),
				 .field_index = 6,
				 .input_index = 6,
				 .offset = sizeof(struct ether_hdr) +
				 offsetof(struct ipv6_hdr, dst_addr) + sizeof(uint32_t),
				 },

	[7] = {
				 .type = RTE_ACL_FIELD_TYPE_MASK,
				 .size = sizeof(uint32_t),
				 .field_index = 7,
				 .input_index = 7,
				 .offset = sizeof(struct ether_hdr) +
				 offsetof(struct ipv6_hdr, dst_addr) + 2 * sizeof(uint32_t),
				 },

	[8] = {
				 .type = RTE_ACL_FIELD_TYPE_MASK,
				 .size = sizeof(uint32_t),
				 .field_index = 8,
				 .input_index = 8,
				 .offset = sizeof(struct ether_hdr) +
				 offsetof(struct ipv6_hdr, dst_addr) + 3 * sizeof(uint32_t),
				 },

	/* Source Port */
	[9] = {
				 .type = RTE_ACL_FIELD_TYPE_RANGE,
				 .size = sizeof(uint16_t),
				 .field_index = 9,
				 .input_index = 9,
				 .offset = sizeof(struct ether_hdr) +
				 sizeof(struct ipv6_hdr) + offsetof(struct tcp_hdr, src_port),
				 },

	/* Destination Port */
	[10] = {
		.type = RTE_ACL_FIELD_TYPE_RANGE,
		.size = sizeof(uint16_t),
		.field_index = 10,
		.input_index = 9,
		.offset = sizeof(struct ether_hdr) +
		sizeof(struct ipv6_hdr) + offsetof(struct tcp_hdr, dst_port),
		},
};

void *lib_acl_create_active_standby_table_ipv4(uint8_t table_num,
		uint32_t *libacl_n_rules)
{
	printf("Create LIBACL active IPV4 Tables rte_socket_id(): %i\n",
			rte_socket_id());

	/* Create IPV4 LIBACL Rule Tables */
	struct rte_table_acl_params common_ipv4_table_libacl_params = {
		.name = "LIBACLIPV4A",
		.n_rules = *libacl_n_rules,
		.n_rule_fields = RTE_DIM(field_format_ipv4),
	};

	memcpy(common_ipv4_table_libacl_params.field_format,
			field_format_ipv4, sizeof(field_format_ipv4));

	uint32_t ipv4_entry_size = sizeof(struct lib_acl_table_entry);
	/* Create second IPV4 Table */
	if (table_num == 2)
		common_ipv4_table_libacl_params.name = "LIBACLIPV4B";
	return	rte_table_acl_ops.f_create(&common_ipv4_table_libacl_params,
			rte_socket_id(),
			ipv4_entry_size);


}

void *lib_acl_create_active_standby_table_ipv6(uint8_t table_num,
		uint32_t *libacl_n_rules)
{
	printf("Create LIBACL active IPV6 Tables rte_socket_id(): %i\n",
			rte_socket_id());
	/* Create IPV6 LIBACL Rule Tables */
	struct rte_table_acl_params common_ipv6_table_libacl_params = {
		.name = "LIBACLIPV6A",
		.n_rules = *libacl_n_rules,
		.n_rule_fields = RTE_DIM(field_format_ipv6),
	};

	memcpy(common_ipv6_table_libacl_params.field_format,
			field_format_ipv6, sizeof(field_format_ipv6));

	uint32_t ipv6_entry_size = sizeof(struct lib_acl_table_entry);
	/* Create second IPV6 table */
	if (table_num == 2)
		common_ipv6_table_libacl_params.name = "LIBACLIPV6B";
	return	rte_table_acl_ops.f_create(&common_ipv6_table_libacl_params,
			rte_socket_id(),
			ipv6_entry_size);


}
int lib_acl_parse_config(struct lib_acl *plib_acl,
		char *arg_name, char *arg_value,
		uint32_t *libacl_n_rules)
{
	uint32_t n_rules_present = 0;
	uint32_t pkt_type_present = 0;
	/* defaults */
	plib_acl->n_rules = DEFULT_NUM_RULE;
	*libacl_n_rules = DEFULT_NUM_RULE;
	plib_acl->n_rule_fields = RTE_DIM(field_format_ipv4);
	plib_acl->field_format = field_format_ipv4;
	plib_acl->field_format_size = sizeof(field_format_ipv4);
	if (strcmp(arg_name, "n_rules") == 0) {
		if (n_rules_present) {
			printf("n_rules_present");
			return -1;
		}
		n_rules_present = 1;

		plib_acl->n_rules = atoi(arg_value);
		*libacl_n_rules = atoi(arg_value);
		return 0;
	}
	if (strcmp(arg_name, "pkt_type") == 0) {
		if (pkt_type_present) {
			printf("pkt_type");
			return -1;
		}
		pkt_type_present = 1;

		/* ipv4 */
		if (strcmp(arg_value, "ipv4") == 0) {
			plib_acl->n_rule_fields =
				RTE_DIM(field_format_ipv4);
			plib_acl->field_format = field_format_ipv4;
			plib_acl->field_format_size =
				sizeof(field_format_ipv4);
			return 0;
		}

		/* vlan_ipv4 */
		if (strcmp(arg_value, "vlan_ipv4") == 0) {
			plib_acl->n_rule_fields =
				RTE_DIM(field_format_vlan_ipv4);
			plib_acl->field_format =
				field_format_vlan_ipv4;
			plib_acl->field_format_size =
				sizeof(field_format_vlan_ipv4);
			return 0;
		}

		/* qinq_ipv4 */
		if (strcmp(arg_value, "qinq_ipv4") == 0) {
			plib_acl->n_rule_fields =
				RTE_DIM(field_format_qinq_ipv4);
			plib_acl->field_format =
				field_format_qinq_ipv4;
			plib_acl->field_format_size =
				sizeof(field_format_qinq_ipv4);
			return 0;
		}

		/* ipv6 */
		if (strcmp(arg_value, "ipv6") == 0) {
			plib_acl->n_rule_fields =
				RTE_DIM(field_format_ipv6);
			plib_acl->field_format = field_format_ipv6;
			plib_acl->field_format_size =
				sizeof(field_format_ipv6);
			return 0;
		}
		/* other */
		printf("other");
		return -1;
	}
	/* Parameter not processed in this parse function */
	return 1;
}


/**
 * Main packet processing function.
 * 64 packet bit mask are used to identify which packets to forward.
 * Performs the following:
 *  - Burst lookup packets in the IPv4 ACL Rule Table.
 *  - Lookup Action Table, perform actions.
 *  - Burst lookup Connection Tracking, if enabled.
 *  - Lookup MAC address.
 *  - Set bit mask.
 *  - Packets with bit mask set are forwarded
 *
 * @param p
 *  A pointer to the pipeline.
 * @param pkts
 *  A pointer to a burst of packets.
 * @param n_pkts
 *  Number of packets to process.
 * @param arg
 *  A pointer to pipeline specific data.
 *
 * @return
 *  0 on success, negative on error.
 */
	uint64_t
lib_acl_ipv4_pkt_work_key(struct lib_acl *plib_acl,
	struct rte_mbuf **pkts, uint64_t pkts_mask,
	uint64_t *pkts_drop_without_rule,
	void *plib_acl_rule_table_ipv4_active,
	struct pipeline_action_key *action_array_active,
	struct action_counter_block (*p_action_counter_table)[action_array_max],
	uint64_t *conntrack_mask,
	uint64_t *connexist_mask)
{

	uint64_t lookup_hit_mask_ipv4 = 0;
	uint64_t lookup_miss_mask_ipv4 = 0;
	int status;

	if (ACL_LIB_DEBUG)
		printf("ACL IPV4 Lookup Mask Before = 0x%"PRIx64"\n",
				pkts_mask);
	status = rte_table_acl_ops.f_lookup(
			plib_acl_rule_table_ipv4_active,
			pkts, pkts_mask, &lookup_hit_mask_ipv4,
			(void **) plib_acl->plib_acl_entries_ipv4);
	if (status < 0)
		printf("Lookup Failed\n");
	if (ACL_LIB_DEBUG)
		printf("ACL IPV4 Lookup Mask After = 0x%"PRIx64"\n",
				lookup_hit_mask_ipv4);
	if (ACL_LIB_DEBUG)
		printf("ACL Lookup Mask After = 0x%"PRIx64"\n",
				lookup_hit_mask_ipv4);

	lookup_miss_mask_ipv4 = pkts_mask & (~lookup_hit_mask_ipv4);
	pkts_mask = lookup_hit_mask_ipv4;
	*pkts_drop_without_rule += __builtin_popcountll(lookup_miss_mask_ipv4);
	if (ACL_LIB_DEBUG)
		printf("pkt_work_acl_key pkts_drop: %" PRIu64 " n_pkts: %u\n",
				*pkts_drop_without_rule,
				__builtin_popcountll(lookup_miss_mask_ipv4));
	/* bitmap of packets left to process for ARP */
	uint64_t pkts_to_process = lookup_hit_mask_ipv4;

	for (; pkts_to_process;) {
		uint8_t pos = (uint8_t)__builtin_ctzll(pkts_to_process);
		/* bitmask representing only this packet */
		uint64_t pkt_mask = 1LLU << pos;
		/* remove this packet from remaining list */
		pkts_to_process &= ~pkt_mask;
		struct rte_mbuf *pkt = pkts[pos];



		struct lib_acl_table_entry *entry =
			(struct lib_acl_table_entry *)
			plib_acl->plib_acl_entries_ipv4[pos];
		uint16_t phy_port = entry->head.port_id;
		uint32_t action_id = entry->action_id;

		if (ACL_LIB_DEBUG)
			printf("action_id = %u\n", action_id);

		uint32_t dscp_offset = IP_START + IP_HDR_DSCP_OFST;

		if (action_array_active[action_id].action_bitmap &
				lib_acl_action_count) {
			p_action_counter_table
				[plib_acl->action_counter_index]
				[action_id].packetCount++;
			p_action_counter_table
				[plib_acl->action_counter_index]
				[action_id].byteCount +=
				rte_pktmbuf_pkt_len(pkt);
			if (ACL_LIB_DEBUG)
				printf("Action Count   Packet Count: %"
						PRIu64 "  Byte Count: %"
						PRIu64 "\n"
						, p_action_counter_table
						[plib_acl->action_counter_index]
						[action_id].packetCount,
						p_action_counter_table
						[plib_acl->action_counter_index]
						[action_id].byteCount);
		}

		if (action_array_active[action_id].action_bitmap &
				lib_acl_action_packet_drop) {

			/* Drop packet by changing the mask */
			if (ACL_LIB_DEBUG)
				printf("ACL before drop pkt_mask %"
						PRIx64", pkt_num %d\n",
						pkts_mask, pos);
			pkts_mask &= ~(1LLU << pos);
			(*pkts_drop_without_rule)++;
			if (ACL_LIB_DEBUG)
				printf("ACL after drop pkt_mask %" PRIx64
					", pkt_num %d, action_packet_drop %"
					PRIu64 "\n", pkts_mask, pos,
					*pkts_drop_without_rule);
		}

		if (action_array_active[action_id].action_bitmap &
				lib_acl_action_fwd) {
			phy_port = action_array_active[action_id].
				fwd_port;
			entry->head.port_id = phy_port;
			if (ACL_LIB_DEBUG)
				printf("Action FWD  Port ID: %"
						PRIu16"\n", phy_port);
		}

		if (action_array_active[action_id].action_bitmap &
				lib_acl_action_nat) {
			phy_port = action_array_active[action_id].
				nat_port;
			entry->head.port_id = phy_port;
			if (ACL_LIB_DEBUG)
				printf("Action NAT  Port ID: %"
						PRIu16"\n", phy_port);
		}

		if (action_array_active[action_id].action_bitmap &
				lib_acl_action_dscp) {

			/* Set DSCP priority */
			uint8_t *dscp = RTE_MBUF_METADATA_UINT8_PTR(pkt,
					dscp_offset);
			*dscp = action_array_active[action_id].
				dscp_priority << 2;
			if (ACL_LIB_DEBUG)
				printf("Action DSCP   DSCP Priority: %"
						PRIu16 "\n", *dscp);
		}

		if (action_array_active[action_id].action_bitmap &
				lib_acl_action_packet_accept) {
			if (ACL_LIB_DEBUG)
				printf("Action Accept\n");

			if (action_array_active[action_id].action_bitmap
					& lib_acl_action_conntrack) {

				/* Set conntrack bit for this pkt */
				*conntrack_mask |= pkt_mask;
				if (ACL_LIB_DEBUG)
					printf("ACL CT enabled: 0x%"
							PRIx64"  pkt_mask: 0x%"
							PRIx64"\n",
							*conntrack_mask,
							pkt_mask);
			}

			if (action_array_active[action_id].action_bitmap
					& lib_acl_action_connexist) {

				/* Set conntrack bit for this pkt */
				*conntrack_mask |= pkt_mask;

				/* Set connexist bit for this pkt for
				 * public -> private */
				/* Private -> public packet will open
				 * the connection */
				if (action_array_active[action_id].
						private_public ==
						lib_acl_public_private)
					*connexist_mask |= pkt_mask;

				if (ACL_LIB_DEBUG)
					printf("ACL Connexist ENB CT:0x%"
							PRIx64"  connexist: 0x%"
							PRIx64"  pkt_mask: 0x%"
							PRIx64"\n",
							*conntrack_mask,
							*connexist_mask,
							pkt_mask);
			}
		}

	}
	return pkts_mask;
}
/**
 * Main packet processing function.
 * 64 packet bit mask are used to identify which packets to forward.
 * Performs the following:
 *  - Burst lookup packets in the IPv6 ACL Rule Table.
 *  - Lookup Action Table, perform actions.
 *  - Burst lookup Connection Tracking, if enabled.
 *  - Lookup MAC address.
 *  - Set bit mask.
 *  - Packets with bit mask set are forwarded
 *
 * @param p
 *  A pointer to the pipeline.
 * @param pkts
 *  A pointer to a burst of packets.
 * @param n_pkts
 *  Number of packets to process.
 * @param arg
 *  A pointer to pipeline specific data.
 *
 * @return
 *  0 on success, negative on error.
 */
	uint64_t
lib_acl_ipv6_pkt_work_key(struct lib_acl *plib_acl,
	struct rte_mbuf **pkts, uint64_t pkts_mask,
	uint64_t *pkts_drop_without_rule,
	void *plib_acl_rule_table_ipv6_active,
	struct pipeline_action_key *action_array_active,
	struct action_counter_block (*p_action_counter_table)[action_array_max],
	uint64_t *conntrack_mask,
	uint64_t *connexist_mask)
{

	uint64_t lookup_hit_mask_ipv6 = 0;
	uint64_t lookup_miss_mask_ipv6 = 0;
	int status;


	if (ACL_LIB_DEBUG)
		printf("ACL IPV6 Lookup Mask Before = 0x%"PRIx64"\n",
				pkts_mask);
	status = rte_table_acl_ops.f_lookup(
			plib_acl_rule_table_ipv6_active,
			pkts, pkts_mask, &lookup_hit_mask_ipv6,
			(void **) plib_acl->plib_acl_entries_ipv6);
	if (status < 0)
		printf("Lookup Failed\n");
	if (ACL_LIB_DEBUG)
		printf("ACL IPV6 Lookup Mask After = 0x%"PRIx64"\n",
				lookup_hit_mask_ipv6);

	if (ACL_LIB_DEBUG)
		printf("ACL Lookup Mask After = 0x%"PRIx64"\n",
				lookup_hit_mask_ipv6);

	lookup_miss_mask_ipv6 = pkts_mask & (~lookup_hit_mask_ipv6);
	pkts_mask = lookup_hit_mask_ipv6;
	*pkts_drop_without_rule += __builtin_popcountll(lookup_miss_mask_ipv6);
	if (ACL_LIB_DEBUG)
		printf("pkt_work_acl_key pkts_drop: %" PRIu64 " n_pkts: %u\n",
				*pkts_drop_without_rule,
				__builtin_popcountll(lookup_miss_mask_ipv6));
	/* bitmap of packets left to process for ARP */
	uint64_t pkts_to_process = lookup_hit_mask_ipv6;

	for (; pkts_to_process;) {
		uint8_t pos = (uint8_t)__builtin_ctzll(pkts_to_process);
		/* bitmask representing only this packet */
		uint64_t pkt_mask = 1LLU << pos;
		/* remove this packet from remaining list */
		pkts_to_process &= ~pkt_mask;
		struct rte_mbuf *pkt = pkts[pos];


		struct lib_acl_table_entry *entry =
			(struct lib_acl_table_entry *)
			plib_acl->plib_acl_entries_ipv6[pos];
		uint16_t phy_port = entry->head.port_id;
		uint32_t action_id = entry->action_id;

		if (ACL_LIB_DEBUG)
			printf("action_id = %u\n", action_id);

		if (action_array_active[action_id].action_bitmap &
				lib_acl_action_count) {
			p_action_counter_table
				[plib_acl->action_counter_index]
				[action_id].packetCount++;
			p_action_counter_table
				[plib_acl->action_counter_index]
				[action_id].byteCount +=
				rte_pktmbuf_pkt_len(pkt);
			if (ACL_LIB_DEBUG)
				printf("Action Count   Packet Count: %"
						PRIu64 "  Byte Count: %"
						PRIu64 "\n",
						p_action_counter_table
						[plib_acl->action_counter_index]
						[action_id].packetCount,
						p_action_counter_table
						[plib_acl->action_counter_index]
						[action_id].byteCount);
		}

		if (action_array_active[action_id].action_bitmap &
				lib_acl_action_packet_drop) {
			/* Drop packet by changing the mask */
			if (ACL_LIB_DEBUG)
				printf("ACL before drop pkt_mask %"
						PRIx64", pkt_num %d\n",
						pkts_mask, pos);
			pkts_mask &= ~(1LLU << pos);
			(*pkts_drop_without_rule)++;
			if (ACL_LIB_DEBUG)
				printf("ACL after drop pkt_mask %" PRIx64
					", pkt_num %d, action_packet_drop %"
					PRIu64 "\n", pkts_mask, pos,
					*pkts_drop_without_rule);

		}

		if (action_array_active[action_id].action_bitmap &
				lib_acl_action_fwd) {
			phy_port = action_array_active[action_id].
				fwd_port;
			entry->head.port_id = phy_port;
			if (ACL_LIB_DEBUG)
				printf("Action FWD  Port ID: %"
						PRIu16"\n", phy_port);
		}

		if (action_array_active[action_id].action_bitmap &
				lib_acl_action_nat) {
			phy_port = action_array_active[action_id].
				nat_port;
			entry->head.port_id = phy_port;
			if (ACL_LIB_DEBUG)
				printf("Action NAT  Port ID: %"
						PRIu16"\n", phy_port);
		}

		if (action_array_active[action_id].action_bitmap &
				lib_acl_action_dscp) {

			/* Set DSCP priority */
			uint32_t dscp_offset = IP_START +
				IP_HDR_DSCP_OFST_IPV6;
			uint16_t *dscp = RTE_MBUF_METADATA_UINT16_PTR(
					pkt, dscp_offset);
			uint16_t temp = *dscp;
			uint16_t dscp_value = (rte_bswap16(temp) &
					0XF00F);
			uint8_t dscp_store =
				action_array_active
				[action_id].dscp_priority << 2;
			uint16_t dscp_temp = dscp_store;

			dscp_temp = dscp_temp << 4;
			*dscp = rte_bswap16(dscp_temp | dscp_value);
			if (ACL_LIB_DEBUG)
				printf("Action DSCP   DSCP Priority: %"
						PRIu16"\n", *dscp);
		}

		if (action_array_active[action_id].action_bitmap
				& lib_acl_action_packet_accept) {
			if (ACL_LIB_DEBUG)
				printf("Action Accept\n");

			if (action_array_active[action_id].action_bitmap
					& lib_acl_action_conntrack) {

				/* Set conntrack bit for this pkt */
				*conntrack_mask |= pkt_mask;
				if (ACL_LIB_DEBUG)
					printf("ACL CT enabled: 0x%"
							PRIx64" pkt_mask: 0x%"
							PRIx64"\n",
							*conntrack_mask,
							pkt_mask);
			}

			if (action_array_active[action_id].action_bitmap
					& lib_acl_action_connexist) {

				/* Set conntrack bit for this pkt */
				*conntrack_mask |= pkt_mask;

				/* Set connexist bit for this pkt for
				 * public -> private */
				/* Private -> public packet will open
				 * the connection */
				if (action_array_active[action_id].
						private_public ==
						lib_acl_public_private)
					*connexist_mask |= pkt_mask;

				if (ACL_LIB_DEBUG)
					printf("ACL Connexist ENB CT:0x%"
							PRIx64"  connexist: 0x%"
							PRIx64"  pkt_mask: 0x%"
							PRIx64"\n",
							*conntrack_mask,
							*connexist_mask,
							pkt_mask);
			}
		}
	}
	return pkts_mask;
}