/* Copyright (C) 2010 Eric Hsiao <erichs0608@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

/* Kernel module implementing an IP set type: the time type */

#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/errno.h>

#include <linux/netfilter_ipv4/ip_set_time.h>

static int
time_utest(struct ip_set *set, const void *data, u_int32_t size)
{
	struct ip_set_time *map = set->data;
	struct list_head *head = &map->head;
	const struct ip_set_req_time *req = data;
	struct list_head *pos;
	struct time_list *tmp;
	
	list_for_each(pos, head) {
		tmp = (struct time_list *) list_entry(pos, struct time_list, list);
		if (memcmp(&tmp->time, req, sizeof(struct ip_set_req_time)) == 0)
			return 1;
	}
	return 0;
}

static int
time_ktest(struct ip_set *set,
	      const struct sk_buff *skb,
	      const u_int32_t *flags)
{
	struct ip_set_time *map = set->data;
	struct list_head *head = &map->head;
	struct list_head *pos;
	struct time_list *tmp;
	
	list_for_each(pos, head) {
		tmp = (struct time_list *) list_entry(pos, struct time_list, list);
		if(time_match(skb, (void *)&tmp->time) == 1)
			return 1;
	}
	return 0;
}

static int
time_uadd(struct ip_set *set, const void *data, u_int32_t size)
{
	struct ip_set_time *map = set->data;
	struct list_head *head = &map->head;
	const struct ip_set_req_time *req = data;
	struct list_head *pos;
	struct time_list *tmp;
	struct time_list *time_list;

		
	list_for_each(pos,head) {
		tmp = (struct time_list *) list_entry(pos, struct time_list, list);
		if (memcmp(&tmp->time, req, sizeof(struct ip_set_req_time)) == 0)
			return -EEXIST;
	}	
	
	time_list = kmalloc(sizeof(struct time_list), GFP_KERNEL);
	if (!time_list)
		return -ENOMEM;
	memcpy(&time_list->time, req, sizeof(struct ip_set_req_time));
	list_add(&time_list->list, head);
	map->size++;
	return 0;
}

static int
time_kadd(struct ip_set *set,
	     const struct sk_buff *skb,
	     const u_int32_t *flags)
{
	return 0;
}

static int
time_udel(struct ip_set *set, const void *data, u_int32_t size)
{
	struct ip_set_time *map = set->data;
	struct list_head *head = &map->head;
	const struct ip_set_req_time *req = data;
	struct list_head *pos, *q;
	struct time_list *tmp;
	
	list_for_each_safe(pos, q, head) {
		tmp = (struct time_list *) list_entry(pos, struct time_list, list);
		if (memcmp(&tmp->time, req, sizeof(struct ip_set_req_time)) == 0) {
			list_del(pos);
			kfree(tmp);
			map->size--;
			return 0;
		}
	}
	
	return -EEXIST;
}

static int
time_kdel(struct ip_set *set,
	     const struct sk_buff *skb,
	     const u_int32_t *flags)
{
	return 0;
}

static int
time_create(struct ip_set *set, const void *data, u_int32_t size)
{
	struct ip_set_time *map;
	//const struct ip_set_req_time_create *req = data;
	map= kmalloc(sizeof(struct ip_set_time) , GFP_KERNEL);
	if (!map)
		return -ENOMEM;
	map->size = 0;
	INIT_LIST_HEAD(&map->head);
	set->data = map;
	return 0;
}                 

static void
time_destroy(struct ip_set *set)
{
	struct ip_set_time *map = set->data;
	struct list_head *head = &map->head;
	struct list_head *pos, *q;
	struct time_list *tmp;
	
	list_for_each_safe(pos, q, head) {
		tmp = (struct time_list *) list_entry(pos, struct time_list, list);
		list_del(pos);
		kfree(tmp);
	}
	
	kfree(map);
	
	return;
}

static void
time_flush(struct ip_set *set)
{
	struct ip_set_time *map = set->data;
	struct list_head *head = &map->head;
	struct list_head *pos, *q;
	struct time_list *tmp;
	
	list_for_each_safe(pos, q, head) {
		tmp = (struct time_list *) list_entry(pos, struct time_list, list);
		list_del(pos);
		kfree(tmp);
	}
	
	map->size = 0;
	
	return;
}

static void
time_list_header(const struct ip_set *set, void *data)
{
	const struct ip_set_time *map = set->data;
	struct ip_set_req_time_create *header = data;
	
	header->size = map->size;
}

static int
time_list_members_size(const struct ip_set *set, char dont_align)
{
	struct ip_set_time *map = set->data;
	return map->size*sizeof(struct ip_set_req_time);
}

static void
time_list_members(const struct ip_set *set, void *data, char dont_align)
{
	struct ip_set_time *map = set->data;
	struct ip_set_req_time *d = data;
	struct list_head *head = &map->head;
	struct list_head *pos;
	struct time_list *tmp;
	
	list_for_each(pos,head) {
		tmp = (struct time_list *) list_entry(pos, struct time_list, list);
		memcpy(d, &tmp->time, sizeof(struct ip_set_req_time));
		d++;
	}
}

IP_SET_TYPE(time, IPSET_DATA_SINGLE)

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Eric Hsiao <erichs0608@gmail.com>");
MODULE_DESCRIPTION("time type of IP sets");

REGISTER_MODULE(time)
