diff --git a/Makefile b/Makefile
index 8b5c2d4c3..1686167b3 100644
--- a/Makefile
+++ b/Makefile
@@ -44,7 +44,7 @@ SRCS-y += sol/main.c
# Libraries.
SRCS-y += lib/mailbox.c lib/net.c lib/flow.c lib/ipip.c \
lib/luajit-ffi-cdata.c lib/launch.c lib/lpm.c lib/acl.c lib/varip.c \
- lib/l2.c
+ lib/l2.c lib/space_saving.c
LDLIBS += $(LDIR) -Bstatic -lluajit-5.1 -Bdynamic -lm -lmnl
CFLAGS += $(WERROR_FLAGS) -I${GATEKEEPER}/include -I/usr/local/include/luajit-2.0/
diff --git a/include/space_saving.h b/include/space_saving.h
new file mode 100644
index 000000000..3068d735f
--- /dev/null
+++ b/include/space_saving.h
@@ -0,0 +1,130 @@
+/*
+ * Gatekeeper - DoS protection system.
+ * Copyright (C) 2016 Digirati LTDA.
+ *
+ * This program 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see .
+ */
+
+#ifndef _SPACE_SAVING_H_
+#define _SPACE_SAVING_H_
+
+#include
+
+#include "gatekeeper_gk.h"
+#include "gatekeeper_net.h"
+#include "gatekeeper_flow.h"
+
+/* Data structure for Counter Table */
+struct counter_table {
+
+ /* Counter for IPV4 address */
+ struct rte_hash *ct_ip4;
+
+ /* Counter for IPV6 address */
+ struct rte_hash *ct_ip6;
+};
+
+/* Data structure for Counter bucket */
+struct counter_bucket {
+
+ int proto;
+
+ union {
+ /* Bucket for IPV4 address */
+ struct rte_hash *bkt_ip4;
+
+ /* Bucket for IPV6 address */
+ struct rte_hash *bkt_ip6;
+ } bkt;
+};
+
+/* Data present in a Counter bucket */
+struct bucket_data {
+
+ int err;
+ struct ip_flow flow;
+};
+
+/* Data structure of IP data */
+struct ip_data {
+
+ int bkt_id;
+ struct bucket_data bkt_data;
+};
+
+static int
+setup_bucket_params(unsigned int socket_id, int bkt_id, int bkt_size,
+ int ip_ver, struct rte_hash_parameters *bkt_hash_params);
+
+static int
+create_bucket(struct gatekeeper_if *iface, struct gk_config *gk_conf,
+ int bkt_id);
+
+static int
+get_bucket(int bkt_id, int proto, struct counter_bucket *ct_bkt);
+
+static int
+get_bucket_entry(struct counter_bucket *ct_bkt, struct ip_flow *flow,
+ struct bucket_data *bkt_data);
+
+static int
+add_bucket_entry(struct counter_bucket *ct_bkt, struct ip_flow *flow,
+ struct bucket_data *bkt_data);
+
+static int
+delete_bucket_entry(struct counter_bucket *ct_bkt, struct ip_flow *flow);
+
+static int
+setup_counter_params(unsigned int socket_id, int identifier, int ht_size,
+ int ip_ver, struct rte_hash_parameters *ct_hash_params);
+
+static int
+create_counter_table(struct gatekeeper_if *iface, struct gk_config *gk_conf);
+
+static int
+get_bucket_id(struct counter_table *ct_table, struct ip_flow *flow,
+ int *bkt_id);
+
+static int
+add_bucket_id(struct counter_table *ct_table, struct ip_flow *flow,
+ int *bkt_id);
+
+static int
+delete_bucket_id(struct counter_table *ct_table, struct ip_flow *flow);
+
+static int
+update_bucket_id(struct counter_table *ct_table, struct ip_flow *flow);
+
+static int
+get_ip_data(struct counter_table *ct_table, struct ip_flow *flow,
+ struct ip_data *data);
+
+static int
+add_ip_data(struct counter_table *ct_table, struct ip_flow *flow,
+ struct ip_data *data);
+
+static int
+delete_ip_data(struct counter_table *ct_table, struct ip_flow *flow);
+
+static int
+update_ip_data(struct counter_table *ct_table, struct ip_flow *flow,
+ struct gatekeeper_if *iface, struct gk_config *gk_conf);
+
+static int
+space_saving(struct counter_table *ct_table, struct ip_flow *flow,
+ struct gatekeeper_if *iface, struct gk_config *gk_conf);
+
+int run(struct gatekeeper_if *iface, struct gk_config *gk_conf);
+#endif /* _SPACE_SAVING_H */
+
diff --git a/lib/space_saving.c b/lib/space_saving.c
new file mode 100644
index 000000000..e8986f514
--- /dev/null
+++ b/lib/space_saving.c
@@ -0,0 +1,796 @@
+/*
+ * Gatekeeper - DoS protection system.
+ * Copyright (C) 2016 Digirati LTDA.
+ *
+ * This program 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see .
+ */
+
+
+/*
+ * This Implementation is similar to the stream summary data structure decribed
+ * in the research paper on space saving algorithm by Ahmed Metwally,
+<<<<<<< HEAD
+ * Divyakant Agrawal and Amr El Abbadi.
+ */
+
+/*
+ * TODO #1 : Add a Tree based data structure such as AVL Tree or
+ * Van Emde Boas Tree to support sublinear bucket update operation.
+ */
+
+/* TODO #2 : Test the written code for correctness */
+
+#include
+
+#include "space_saving.h"
+
+/* Set up the parameters of a counter bucket */
+static int
+setup_bucket_params(unsigned int socket_id, int bkt_size, int bkt_id,
+ int ip_ver, struct rte_hash_parameters *bkt_hash_params)
+{
+ int ret;
+
+ int key_len = ip_ver == ETHER_TYPE_IPv4 ?
+ sizeof(struct in_addr) : sizeof(struct in6_addr);
+
+ bkt_hash_params->name = NULL;
+ bkt_hash_params->entries = bkt_size;
+ bkt_hash_params->key_len = key_len;
+ bkt_hash_params->hash_func = DEFAULT_HASH_FUNC;
+ bkt_hash_params->hash_func_init_val = 0;
+ bkt_hash_params->reserved = 0;
+
+ char bkt_name[64];
+ ret = snprintf(bkt_name, sizeof(bkt_name), "Bucket_number_%d_%d",
+ ip_ver, bkt_id);
+ RTE_VERIFY(ret > 0 && ret < (int)sizeof(bkt_name));
+
+ bkt_hash_params->name = bkt_name;
+ bkt_hash_params->socket_id = socket_id;
+
+ return ret;
+}
+
+/* Create a counter bucket */
+static int
+create_bucket(struct gatekeeper_if *iface, struct gk_config *gk_conf,
+ int bkt_id)
+{
+ int ret;
+ unsigned int socket_id;
+ struct rte_hash_parameters bkt_hash_params;
+ struct counter_bucket *ct_bkt =
+ (struct counter_bucket *)malloc(sizeof(struct counter_bucket));
+
+ socket_id = rte_lcore_to_socket_id(gk_conf->lcores[0]);
+
+ /*
+ * TODO : How to determine max_size of a bucket ?
+ * Currently it has been set to "10000" but final values has to be
+ * updated after testing.
+ */
+
+ if(ipv4_if_configured(iface)) {
+ ret = setup_bucket_params(socket_id, bkt_id, ETHER_TYPE_IPv4,
+ 10000, &bkt_hash_params);
+ if(ret < 0) {
+ RTE_LOG(ERR, HASH, "The bucket parameters "
+ "can't be set\n");
+ ret = -1;
+ }
+
+ ct_bkt->bkt.bkt_ip4 = rte_hash_create(&bkt_hash_params);
+ if(ct_bkt->bkt.bkt_ip4 == NULL) {
+ RTE_LOG(ERR, HASH, "Bucket number %d could not be "
+ "created!\n",bkt_id);
+ ret = -1;
+ }
+ }
+
+ if(ipv6_if_configured(iface)) {
+ ret = setup_bucket_params(socket_id, bkt_id, ETHER_TYPE_IPv6,
+ 10000, &bkt_hash_params);
+ if(ret < 0) {
+ RTE_LOG(ERR, HASH, "The bucket parameters "
+ "can't be set\n");
+ ret = -1;
+ }
+
+ ct_bkt->bkt.bkt_ip6 = rte_hash_create(&bkt_hash_params);
+ if(ct_bkt->bkt.bkt_ip6 == NULL) {
+ RTE_LOG(ERR, HASH, "Bucket number %d could not be "
+ "created!\n",bkt_id);
+ ret = -1;
+ }
+ }
+ return ret;
+}
+
+/* Retuns a pointer to a counter bucket */
+static int
+get_bucket(int bkt_id, int proto, struct counter_bucket *ct_bkt)
+{
+ int ret;
+ char bkt_name[64];
+
+ ret = snprintf(bkt_name, sizeof(bkt_name),
+ "Bucket_number_%d_%d", proto, bkt_id);
+ RTE_VERIFY(ret > 0 && ret < (int)sizeof(bkt_name));
+
+ ct_bkt->proto = proto;
+
+ switch(proto) {
+ case ETHER_TYPE_IPv4:
+ ct_bkt->bkt.bkt_ip4 = rte_hash_find_existing(bkt_name);
+ if(ct_bkt->bkt.bkt_ip4 == NULL) {
+ RTE_LOG(ERR, HASH, "Bucket number %d is not "
+ "present!\n", bkt_id);
+ ret = -2;
+ }
+ break;
+
+ case ETHER_TYPE_IPv6:
+ ct_bkt->bkt.bkt_ip6 = rte_hash_find_existing(bkt_name);
+ if(ct_bkt->bkt.bkt_ip6 == NULL) {
+ RTE_LOG(ERR, HASH, "Bucket number %d is not "
+ "present!\n", bkt_id);
+ ret = -2;
+ }
+ break;
+
+ case ETHER_TYPE_ARP:
+ ret = -1;
+ break;
+
+ default:
+ RTE_LOG(ERR, HASH, "Unknown network layer protocol\n");
+ ret = -1;
+ break;
+ }
+ return ret;
+}
+
+/* Returns data from a counter bucket */
+static int
+get_bucket_entry(struct counter_bucket *ct_bkt, struct ip_flow *flow,
+ struct bucket_data *bkt_data)
+{
+ int ret;
+
+ switch(flow->proto) {
+ case ETHER_TYPE_IPv4:
+ ret = rte_hash_lookup_data(ct_bkt->bkt.bkt_ip4, flow,
+ (void **)bkt_data);
+ if(ret < 0) {
+ RTE_LOG(ERR, HASH, "Bucket entry is not "
+ "present!\n");
+ ret = -1;
+ }
+ break;
+
+ case ETHER_TYPE_IPv6:
+ ret = rte_hash_lookup_data(ct_bkt->bkt.bkt_ip6, flow,
+ (void **)bkt_data);
+ if(ret < 0) {
+ RTE_LOG(ERR, HASH, "Bucket entry is not "
+ "present!\n");
+ ret = -1;
+ }
+ break;
+
+ case ETHER_TYPE_ARP:
+ ret = -1;
+ break;
+
+ default:
+ RTE_LOG(ERR, HASH, "Unknown network layer protocol\n");
+ ret = -1;
+ break;
+ }
+ return ret;
+}
+
+/* Add data to a counter bucket */
+static int
+add_bucket_entry(struct counter_bucket *ct_bkt, struct ip_flow *flow,
+ struct bucket_data *bkt_data)
+{
+ int ret;
+
+ switch(flow->proto) {
+ case ETHER_TYPE_IPv4:
+ ret = rte_hash_add_key_data(ct_bkt->bkt.bkt_ip4,
+ flow, bkt_data);
+ if(ret < 0) {
+ RTE_LOG(ERR, HASH, "Data cannot be added into "
+ "the bucket\n");
+ ret = -1;
+ }
+ break;
+
+ case ETHER_TYPE_IPv6:
+ ret = rte_hash_add_key_data(ct_bkt->bkt.bkt_ip6,
+ flow, bkt_data);
+ if(ret < 0) {
+ RTE_LOG(ERR, HASH, "Data cannot be added into "
+ "the bucket\n");
+ ret = -1;
+ }
+ break;
+
+ case ETHER_TYPE_ARP:
+ ret = -1;
+ break;
+
+ default:
+ RTE_LOG(ERR, HASH,"Unknown network layer protocol!\n");
+ ret = -1;
+ break;
+ }
+ return ret;
+}
+
+/* Deletes data from a counter bucket */
+static int
+delete_bucket_entry(struct counter_bucket *ct_bkt, struct ip_flow *flow)
+{
+ int ret;
+
+ switch(flow->proto) {
+ case ETHER_TYPE_IPv4:
+ ret = rte_hash_del_key(ct_bkt->bkt.bkt_ip4, flow);
+ if(ret < 0) {
+ RTE_LOG(ERR, HASH, "Data cannot be deleted "
+ "from the bucket\n");
+ ret = -1;
+ }
+ break;
+
+ case ETHER_TYPE_IPv6:
+ ret = rte_hash_del_key(ct_bkt->bkt.bkt_ip6, flow);
+ if(ret < 0) {
+ RTE_LOG(ERR, HASH, "Data cannot be deleted "
+ "from the bucket\n");
+ ret = -1;
+ }
+ break;
+
+ case ETHER_TYPE_ARP:
+ ret = -1;
+ break;
+
+ default:
+ RTE_LOG(ERR, HASH,"Unknown network layer protocol!\n");
+ ret = -1;
+ break;
+ }
+ return ret;
+}
+
+/* Set up parameters for a counter table */
+static int
+setup_counter_params(unsigned int socket_id, int identifier, int ht_size,
+ int ip_ver, struct rte_hash_parameters *ct_hash_params)
+{
+
+ int ret;
+ int key_len = ip_ver == ETHER_TYPE_IPv4 ?
+ sizeof(struct in_addr) : sizeof(struct in6_addr);
+
+ ct_hash_params->name = NULL;
+ ct_hash_params->entries = ht_size;
+ ct_hash_params->key_len = key_len;
+ ct_hash_params->hash_func = DEFAULT_HASH_FUNC;
+ ct_hash_params->hash_func_init_val = 0;
+ ct_hash_params->reserved = 0;
+
+ char ct_name[64];
+
+ ret = snprintf(ct_name, sizeof(ct_name), "counter_hash_%d", identifier);
+ RTE_VERIFY(ret > 0 && ret < (int)sizeof(ct_name));
+
+ ct_hash_params->name = ct_name;
+ ct_hash_params->socket_id = socket_id;
+
+ return ret;
+}
+
+/* Create a counter table */
+static int
+create_counter_table(struct gatekeeper_if *iface, struct gk_config *gk_conf)
+{
+ int ret;
+ unsigned int socket_id;
+ struct rte_hash_parameters ct_hash_params;
+ struct counter_table *ct_table = malloc(sizeof(*ct_table));
+
+ socket_id = rte_lcore_to_socket_id(gk_conf->lcores[0]);
+
+ /*
+ * TODO : How to get max_size for counter table ?
+ * Currently it has been set to 1000000 but final values to be updated
+ * after testing.
+ */
+
+ if(ipv4_if_configured(iface)) {
+ ret = setup_counter_params(socket_id, 0, ETHER_TYPE_IPv4,
+ 1000000, &ct_hash_params);
+ if(ret < 0) {
+ RTE_LOG(ERR, HASH, "Counter table parametets cannot "
+ "be set\n");
+ ret = -1;
+ }
+
+ ct_table->ct_ip4 = rte_hash_create(&ct_hash_params);
+ if(ct_table == NULL) {
+ RTE_LOG(ERR, HASH, "Counter table cannot be "
+ "created!\n");
+ ret = -1;
+ }
+ }
+
+ if(ipv6_if_configured(iface)) {
+ ret = setup_counter_params(socket_id, 1, ETHER_TYPE_IPv6,
+ 1000000, &ct_hash_params);
+ if(ret < 0) {
+ RTE_LOG(ERR, HASH, "Counter table parametets cannot "
+ "be set\n");
+ ret = -1;
+ }
+
+ ct_table->ct_ip6 = rte_hash_create(&ct_hash_params);
+ if(ct_table == NULL) {
+ RTE_LOG(ERR, HASH, "Counter table cannot be "
+ "created!\n");
+ ret = -1;
+ }
+ }
+ return ret;
+}
+
+/* Retuns bucket id from the counter table */
+static int
+get_bucket_id(struct counter_table *ct_table, struct ip_flow *flow,
+ int *bkt_id)
+{
+ int ret;
+
+ switch(flow->proto) {
+ case ETHER_TYPE_IPv4:
+ ret = rte_hash_lookup_data(ct_table->ct_ip4, flow,
+ (void **)bkt_id);
+ if(ret < 0) {
+ RTE_LOG(ERR, HASH, "Bucket id for IP entry "
+ "cannot be found!\n");
+ ret = -1;
+ }
+ break;
+
+ case ETHER_TYPE_IPv6:
+ ret = rte_hash_lookup_data(ct_table->ct_ip6, flow,
+ (void **)bkt_id);
+ if(ret < 0) {
+ RTE_LOG(ERR, HASH, "Bucket id for IP entry "
+ "cannot be found\n");
+ ret = -1;
+ }
+ break;
+
+ case ETHER_TYPE_ARP:
+ ret = -1;
+ break;
+
+ default:
+ RTE_LOG(ERR, HASH, "Unknown network layer protocol\n");
+ ret = -1;
+ break;
+ }
+ return ret;
+}
+
+/* Add a bucket id to the counter table */
+static int
+add_bucket_id(struct counter_table *ct_table, struct ip_flow *flow,
+ int *bkt_id)
+{
+ int ret;
+
+ switch(flow->proto) {
+ case ETHER_TYPE_IPv4:
+ ret = rte_hash_add_key_data(ct_table->ct_ip4, flow,
+ bkt_id);
+ if(ret < 0) {
+ RTE_LOG(ERR, HASH, "Data cannot be added "
+ "into the table\n");
+ ret = -1;
+ }
+ break;
+
+ case ETHER_TYPE_IPv6:
+ ret = rte_hash_add_key_data(ct_table->ct_ip6, flow,
+ bkt_id);
+ if(ret < 0) {
+ RTE_LOG(ERR, HASH, "Data cannot be added "
+ "into the table\n");
+ ret = -1;
+ }
+ break;
+
+ case ETHER_TYPE_ARP:
+ ret = -1;
+ break;
+
+ default:
+ RTE_LOG(ERR, HASH,"Unknown network layer protocol!\n");
+ ret = -1;
+ break;
+ }
+ return ret;
+}
+
+/* Deletes a bucket id from the counter table */
+static int
+delete_bucket_id(struct counter_table *ct_table, struct ip_flow *flow)
+{
+ int ret;
+
+ switch(flow->proto) {
+ case ETHER_TYPE_IPv4:
+ ret = rte_hash_del_key(ct_table->ct_ip4, flow);
+ if(ret < 0) {
+ RTE_LOG(ERR, HASH, "Data cannot be deleted "
+ "from the table\n");
+ ret = -1;
+ }
+ break;
+
+ case ETHER_TYPE_IPv6:
+ ret = rte_hash_del_key(ct_table->ct_ip6, flow);
+ if(ret < 0) {
+ RTE_LOG(ERR, HASH, "Data cannot be deleted "
+ "from the table\n");
+ ret = -1;
+ }
+ break;
+
+ case ETHER_TYPE_ARP:
+ ret = -1;
+ break;
+
+ default:
+ RTE_LOG(ERR, HASH,"Unknown network layer protocol!\n");
+ ret = -1;
+ break;
+ }
+ return ret;
+}
+
+/* Updates a bucket id in the counter table */
+static int
+update_bucket_id(struct counter_table *ct_table, struct ip_flow *flow)
+{
+ int ret;
+ int *bkt_id = (int *)malloc(sizeof(int));
+
+ ret = get_bucket_id(ct_table, flow, bkt_id);
+ if(ret < 0)
+ goto out;
+
+ (*bkt_id)++;
+
+ return ret;
+
+out:
+ ret = -1;
+ return ret;
+}
+
+/* Returns an IP entry from the IP counter */
+static int
+get_ip_data(struct counter_table *ct_table, struct ip_flow *flow,
+ struct ip_data *data)
+{
+ int ret;
+ int bkt_id;
+ struct counter_bucket ct_bkt = { 0 };
+ struct bucket_data bkt_data = { 0 };
+
+ ret = get_bucket_id(ct_table, flow, &bkt_id);
+ if(ret < 0)
+ goto out;
+
+ ret = get_bucket(bkt_id, flow->proto, &ct_bkt);
+ if(ret < 0)
+ goto out;
+
+ ret = get_bucket_entry(&ct_bkt, flow, &bkt_data);
+ if(ret < 0)
+ goto out;
+
+ data->bkt_id = bkt_id;
+ data->bkt_data = bkt_data;
+
+ return ret;
+
+out:
+ ret = -1;
+ return ret;
+}
+
+/* Adds an IP entry to the IP counter */
+static int
+add_ip_data(struct counter_table *ct_table, struct ip_flow *flow,
+ struct ip_data *data)
+{
+ int ret;
+ int bkt_id = data->bkt_id;
+ struct counter_bucket *ct_bkt = (struct counter_bucket *)
+ malloc(sizeof(struct counter_bucket));
+
+ ret = add_bucket_id(ct_table, flow, &bkt_id);
+ if(ret < 0)
+ goto out;
+
+ //avl_update(bkt_id, 1);
+
+ ret = get_bucket(bkt_id, flow->proto, ct_bkt);
+ if(ret < 0)
+ goto out;
+
+ ret = add_bucket_entry(ct_bkt, flow, &(data->bkt_data));
+ if(ret < 0)
+ goto out;
+
+ return ret;
+
+out:
+ ret = -1;
+ return ret;
+}
+
+/* Deletes an IP entry from the IP counter */
+static int
+delete_ip_data(struct counter_table *ct_table, struct ip_flow *flow)
+{
+ int ret;
+ int bkt_id;
+ struct counter_bucket *ct_bkt = (struct counter_bucket *)
+ malloc(sizeof(struct counter_bucket));
+
+ ret = get_bucket_id(ct_table, flow, &bkt_id);
+ if(ret < 0)
+ goto out;
+
+ //avl_update(bkt_id, -1);
+
+ ret = get_bucket(bkt_id, flow->proto, ct_bkt);
+ if(ret < 0)
+ goto out;
+
+ ret = delete_bucket_id(ct_table, flow);
+ if(ret < 0)
+ goto out;
+
+ ret = delete_bucket_entry(ct_bkt, flow);
+ if(ret < 0)
+ goto out;
+
+ return ret;
+
+out:
+ ret = -1;
+ return ret;
+}
+
+/* Moves the ip entry to next higher bucket */
+static int
+update_ip_data(struct counter_table *ct_table, struct ip_flow *flow,
+ struct gatekeeper_if *iface, struct gk_config *gk_conf)
+{
+ int ret;
+ struct ip_data *data = (struct ip_data *)
+ malloc(sizeof(struct ip_data));
+
+ struct counter_bucket *ct_bkt = (struct counter_bucket *)
+ malloc(sizeof(struct counter_bucket));
+
+ ret = get_ip_data(ct_table, flow, data);
+ if(ret < 0)
+ goto out;
+
+ ret = update_bucket_id(ct_table, flow);
+ if(ret < 0)
+ goto out;
+
+ //avl_update(data->bkt_id, -1);
+ data->bkt_id++;
+ //avl_update(data->bkt_id, 1);
+
+ ret = delete_ip_data(ct_table, flow);
+ if(ret < 0)
+ goto out;
+
+ ret = get_bucket(data->bkt_id, flow->proto, ct_bkt);
+
+ /* If next bucket is not found, create a new bucket */
+ if(ret == -2) {
+ ret = create_bucket(iface, gk_conf, data->bkt_id);
+ /*
+ * if(ret < 0)
+ * goto out;
+ */
+ ret = get_bucket(data->bkt_id, flow->proto, ct_bkt);
+ if(ret < 0)
+ goto out;
+ }
+
+ ret = add_ip_data(ct_table, flow, data);
+ if(ret < 0)
+ goto out;
+
+ return ret;
+
+out:
+ ret = -1;
+ return ret;
+}
+
+/*
+static int
+iterate_counter_table(struct counter_bkt *ct_bkt, int proto, int threshold,
+ struct bucket_data *data)
+{
+ uint32_t ret;
+ int count = 0;
+ struct rte_flow *flow;
+ int bkt_id;
+ int i;
+ uint32_t next = 0;
+
+ switch(proto) {
+ case ETHER_TYPE_IPv4:
+ {
+ time_t curr_time = time(0);
+ if(curr_time - start_time > 2)
+ break;
+
+ ret = rte_hash_iterate(counter->ct_ip4,
+ (const void **)&flow,
+ (void **)&bkt_id, &next);
+
+ }
+ break;
+
+ case ETHER_TYPE_IPv6:
+ break;
+
+ default:
+ RTE_LOG(ERR< HASH, "Unknown network layer protocol!\n");
+ ret = -1;
+ break;
+ }
+ return ret;
+}
+*/
+
+/* Bucket id of element with minimum frequency */
+int min_bkt_id;
+
+/* Implementation of the space saving algorithm */
+static int
+space_saving(struct counter_table *ct_table, struct ip_flow *flow,
+ struct gatekeeper_if *iface, struct gk_config *gk_conf)
+{
+ int ret = 0;
+ int new_bkt_id;
+ struct bucket_data new_bkt_data = { 0 };
+
+ struct counter_bucket *ct_bkt = (struct counter_bucket *)
+ malloc(sizeof(struct counter_bucket));
+
+ struct ip_data *data = (struct ip_data *)
+ malloc(sizeof(struct ip_data));
+
+ uint32_t next = 0;
+ struct ip_flow *min_flow = (struct ip_flow *)
+ malloc(sizeof(struct ip_flow));
+ struct bucket_data *min_bkt_data = (struct bucket_data *)
+ malloc(sizeof(struct bucket_data));
+
+ ret = get_ip_data(ct_table, flow, data);
+
+ if(ret >= 0) {
+ ret = update_ip_data(ct_table, flow, iface, gk_conf);
+ if(ret < 0)
+ goto out;
+ } else {
+ ret = -1;
+ while(ret < 0 && min_bkt_id < 1000000) {
+ ret = get_bucket(min_bkt_id, flow->proto, ct_bkt);
+ if(ret < 0)
+ min_bkt_id++;
+ }
+
+ switch(flow->proto) {
+ case ETHER_TYPE_IPv4:
+ ret = rte_hash_iterate(ct_bkt->bkt.bkt_ip4,
+ (const void **)min_flow,
+ (void **)min_bkt_data, &next);
+ if(ret < 0)
+ goto out;
+ break;
+
+ case ETHER_TYPE_IPv6:
+ ret = rte_hash_iterate(ct_bkt->bkt.bkt_ip6,
+ (const void **)min_flow,
+ (void **)min_bkt_data, &next);
+ if(ret < 0)
+ goto out;
+ break;
+
+ case ETHER_TYPE_ARP:
+ ret = -1;
+ break;
+
+ default:
+ RTE_LOG(ERR, HASH,"Unknown network layer protocol!\n");
+ goto out;
+ break;
+ }
+
+ new_bkt_id = min_bkt_id + 1;
+ new_bkt_data.err = min_bkt_data->err + 1;
+ new_bkt_data.flow = *flow;
+
+ ret = delete_ip_data(ct_table, min_flow);
+ if(ret < 0)
+ goto out;
+
+ data->bkt_id = new_bkt_id;
+ data->bkt_data = new_bkt_data;
+
+ ret = add_ip_data(ct_table, flow, data);
+ if(ret < 0)
+ goto out;
+
+ }
+ return ret;
+
+out:
+ ret = -1;
+ return ret;
+}
+
+/*
+ * Function to run space saving algorithm.
+ * TODO : Remove this function after implementing Space Saving Algorithm
+ * in Gatekeeper.
+ */
+
+int run(struct gatekeeper_if *iface, struct gk_config *gk_conf)
+{
+ int ret = 0;
+ ret = create_counter_table(iface, gk_conf);
+ if(ret < 0)
+ ret = -1;
+
+ struct counter_table *ct_table = (struct counter_table *)
+ malloc(sizeof(struct counter_table));
+ struct ip_flow *flow = (struct ip_flow *)
+ malloc(sizeof(struct ip_flow));
+ ret = space_saving(ct_table, flow, iface, gk_conf);
+ return ret;
+}
+