#ifdef DSR_CRYPT
/* dsr_crypt addon rely on link cache */
#ifndef DSR_USE_LINK_CACHE
#error "dsr_crypt addon relys on link cache. you must compile with -DDSR_USE_LINK_CACHE"
#endif
#endif


#include "dsr_crypt.h"
#include <linux/wait.h>
#include <linux/sched.h>

#include <linux/proc_fs.h>
#include <cryptint_kernel.h>
#include "dsr_debug.h"
#include <linux/slab.h>
#include "dsr_header.h"
#include "dsr_output.h"
#include "dsr.h"
#include <linux/random.h>

#include "graph.h"
#include "spf.h"
#include "dsr_route.h"

#define DSRCRYPT_VALUE_LEN 256
#define DSRCRYPT_FN "dsr_crypt"

#define MODULE_VERSION "0.1"
#define MODULE_NAME "dsr_crypt"


static struct proc_dir_entry * proc_file_crypt;
static struct dsr_mqueue_t * mqueue = NULL; /* linked list */
static int mqueue_len = 0;
static spinlock_t mqueue_lock = SPIN_LOCK_UNLOCKED ;
//static rwlock_t mqueue_lock = RW_LOCK_UNLOCKED;
static unsigned long mqueue_lock_flags;


extern __u32 IPADDR;
extern struct graph *lc_graph;
extern struct node *my_node;

/* refresh (i.e. resend qos_req to expired nodes) timer */
static struct timer_list refresh_timer; 


/* wait queue for /proc filesystem reads */
static DECLARE_WAIT_QUEUE_HEAD(wait_for_msg);


/* calculate the metric of a link from src to dst */
unsigned int dsrcrypt_calc_metric(unsigned int src_qos, 
				  unsigned int src_trust, 
				  unsigned int dst_qos, 
				  unsigned int dst_trust)
{
    if(dst_trust == 0) return MAX_QOS * MAX_TRUST * 2 - dst_qos;
    else return MAX_QOS * MAX_TRUST - dst_qos * dst_trust;
}


/*
 * get this host's actual qos value
 */
__u8 dsrcrypt_get_qos() {
    __u8 retval;
    /* TODO: random was just for testing purposes */
    get_random_bytes(&retval, 1);
    return retval;
}


/* 
 * test if message queue is emtpy
 */
int mqueue_empty() {
    int rv;
    spin_lock_bh(&mqueue_lock);
//    read_lock_irqsave(&mqueue_lock, mqueue_lock_flags);

    rv = (mqueue == NULL);
//    read_unlock_irqrestore(&mqueue_lock, mqueue_lock_flags);
    spin_unlock_bh(&mqueue_lock);
    return rv;
}



/*
 * enqueue a message to the message queue
 */
void enqueue_msg(struct cryptint_msg_t *msg, size_t length) {
    struct dsr_mqueue_t *q = mqueue;
    struct dsr_mqueue_t *newnode;

//    write_lock_irqsave(&mqueue_lock, mqueue_lock_flags);
    spin_lock_bh(&mqueue_lock);
    if(mqueue_len >= MSG_QUEUE_MAXLEN) {
	printk(KERN_INFO "dsr_crypt: message queue too long, ignoring message\n");
	kfree(msg);
    } else {
	newnode = kmalloc(sizeof(struct dsr_mqueue_t), GFP_ATOMIC);
	if(!newnode) {
	    printk(KERN_INFO "dsr_crypt: enqueue_msg: failed to get memory, ignoring message\n");
	    kfree(msg);
	} else {
	    newnode->msg = msg;
	    newnode->msg_length = length;
	    newnode->next = NULL;
	    
	    if(mqueue == NULL) {
		mqueue = newnode;
	    } else {
		while(q->next) {
		    q = q->next;
		}
		q->next = newnode;
	    }
	    mqueue_len++;
	    wake_up(&wait_for_msg);
	}
    }
//    write_unlock_irqrestore(&mqueue_lock, mqueue_lock_flags);
    spin_unlock_bh(&mqueue_lock);
}



/*
 * dequeue head of message queue
 * returns the message and its length
 */
struct cryptint_msg_t *dequeue_msg(size_t *length) {
    struct dsr_mqueue_t *q;
    struct cryptint_msg_t *rv;

    spin_lock_bh(&mqueue_lock);
//    write_lock_irqsave(&mqueue_lock, mqueue_lock_flags);
    if(mqueue) {
	q = mqueue;
	rv = q->msg;
	*length = q->msg_length;
	mqueue = mqueue->next;
	mqueue_len--;
	kfree(q);
    } else {
	rv = NULL;
    }

    spin_unlock_bh(&mqueue_lock);
//    write_unlock_irqrestore(&mqueue_lock, mqueue_lock_flags);
    return rv;
}



/*
 * must be called from init_module()
 */
int dsrcrypt_init() {


    /* Crate the /proc entry */
    proc_file_crypt = create_proc_entry(DSRCRYPT_FN, 0600, &proc_root);
    if(proc_file_crypt == NULL) {
	return -ENOMEM;
    }
    proc_file_crypt->read_proc = dsrcrypt_proc_read;
    proc_file_crypt->write_proc = dsrcrypt_proc_write;
    proc_file_crypt->owner = THIS_MODULE;
    proc_file_crypt->data = NULL;
//  proc_file_crypt->mode =  S_IWUSR |S_IRUSR | S_IRGRP | S_IROTH;
    proc_file_crypt->uid = 0;
    proc_file_crypt->gid = 0;

    /* initialize the refresh timer */
#ifndef DSR_DEBUG_NO_REFRESH_TIMER
    init_timer(&(refresh_timer));
    refresh_timer.function = route_refresh_timeout;
    refresh_timer.expires = jiffies + QOS_CRYPT_REFRESH_INTERVAL * HZ;
    add_timer(&(refresh_timer));
#endif

    return 0;
}



/*
 * must be called from module_cleanup()
 */
void dsrcrypt_cleanup() {
#ifndef DSR_DEBUG_NO_REFRESH_TIMER
    del_timer(&(refresh_timer));
#endif
    remove_proc_entry(DSRCRYPT_FN, &proc_root);
}


/*
 * this is called when a userland process reads from /proc/dsr_crypt.
 * puts the process into interruptible sleep until a new message arrives.
 */
int dsrcrypt_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data) {
    size_t len=0;
    struct cryptint_msg_t *msg;

    MOD_INC_USE_COUNT;


  retry:

    /* Wait until there is a data in the box */
//    DEBUGP("dsrcrypt_proc_read: waiting for a message (off=%i, count=%i)\n", (int)off, count);
    if(wait_event_interruptible(wait_for_msg, !mqueue_empty()) == -ERESTARTSYS) {
	DEBUGP("wait_event_interruptible: caught signal -ERESTARTSYS. cleaning up.\n");
	MOD_DEC_USE_COUNT;
	return -EINVAL;
    };
//    DEBUGP("dsrcrypt_proc_read: copy message to /proc/%s\n", DSRCRYPT_FN);

    msg = dequeue_msg(&len);
    if(!msg) {
	DEBUGP("dsrcrypt_proc_read: oops, someone stole our message, jump back\n");
	goto retry;
    }

//    len = sizeof(struct cryptint_msg_t);
    if(count < len) {
	DEBUGP("dsrcrypt_proc_read: oops, userland process tries to read the data in pieces (count < msg_lenght)... this is not implemented yet: discarding message.\n");
	/* do not copy anything */
	len = 0;
    } else {
	memcpy(page+off, msg, len);
    }
    *eof = 1;
    
    kfree(msg);
    MOD_DEC_USE_COUNT;
   
    return len;
}


/*
 * this is called when a userland process writes to /proc/dsr_crypt.
 * the message will be processed by the module
 */
int dsrcrypt_proc_write(struct file* file, const char* buf, unsigned long count, void* data) {
    int rv=0;
    struct cryptint_msg_t msg;

    MOD_INC_USE_COUNT;

//    DEBUGP("dsrcrypt_proc_write: message arrived (count=%i)\n", (int)count);

    /* test message length */
    if(count > sizeof(struct cryptint_msg_t)) {
	printk(KERN_INFO "%s %s - data too long\n", MODULE_NAME, MODULE_VERSION);
	MOD_DEC_USE_COUNT;
	return -EFAULT;
    }
    /* at least the msgtype must be present */
    if(count < sizeof(unsigned int)) {
	printk(KERN_INFO "%s %s - data too short\n", MODULE_NAME, MODULE_VERSION);
	MOD_DEC_USE_COUNT;
	return -EFAULT;
    }

    /* copy and interpret msg */
    if(copy_from_user(&msg, buf, count)) {
	printk(KERN_INFO "EFAULT on data read \n");
	rv = -EFAULT;
    } else {
	switch(msg.type) {
	case CRYPTINT_MSG_QOS_TRUST:
	{ 
	    /* update qos and trust values for addr */
	    struct cryptint_msg_qos_trust_t *answer;
	    
	    DEBUGP("dsrcrypt_proc_write: got message CRYPTINT_MSG_QOS_TRUST\n");
	    answer = (struct cryptint_msg_qos_trust_t *)&msg;
//		DEBUGP("new {qos trust} value for [%u.%u.%u.%u]  =  {%i  %i}\n", NIPQUAD(answer->addr), answer->qos, answer->trust);
	    route_set_qos_trust(answer->addr, answer->qos, answer->trust, answer->sig_type);
	}
	break;
	case CRYPTINT_MSG_QOS_SIGN_REP:
	{ 
	    /* update qos and trust values for addr */
	    struct cryptint_msg_qos_sign_rep_t * answer = (struct cryptint_msg_qos_sign_rep_t *)&msg;

	    DEBUGP("dsrcrypt_proc_write: got message CRYPTINT_MSG_QOS_SIGN_REP\n");
	    send_qos_crypt(answer->taddr, answer->plain, answer->sig, answer->sig_length);
	}
	break;
	default:
	    printk(KERN_INFO "/proc/dsr_crypt: got unknown message: type %i\n", msg.type);
	}
    }

    MOD_DEC_USE_COUNT;
    return rv ? rv : count ;
}




/*
 * verify signature of qos_crypt option
 */
void proc_qos_crypt_opt(struct sk_buff * skb, unsigned char * optptr) {
    struct dsr_qos_crypt_opt * opt = (struct dsr_qos_crypt_opt *) optptr;
    struct cryptint_msg_qos_verify_t *msg;
    struct node *n;

    if(skb->nh.iph->daddr != IPADDR) {
	DEBUGP("  proc_qos_crypt_opt: not recipient, ignoring.\n");
    } else {
	DEBUGP("  proc_qos_crypt_opt: type=%i, len=%i, sigtype=%i, qos=%i, nonce=%i, addr=0x%x\n", 
	       opt->type, 
	       opt->len, 
	       opt->sigtype, 
	       opt->qos, 
	       opt->nonce,
	       opt->addr);

	if(opt->len > sizeof(struct cryptint_msg_qos_verify_t) - sizeof(unsigned int)) { 
	    printk(KERN_INFO "dsr_crypt: proc_qos_crypt_opt: signature too big. ignoring this option.\n"); 
	} else {
	    if((n = node_get(lc_graph, opt->addr)) == NULL) {
		DEBUGP("  proc_qos_crypt_opt: host not in graph, ignoring.\n");
	    } else {
		/* verify the nonce */
		if(opt->nonce != n->state.nonce) {
		    printk(KERN_INFO "proc_qos_crypt_opt: received wrong nonce (could be some kind of attack)\n");
		} else {
		    /* everything is fine, check the signature */

#ifndef DSR_DEBUG_NO_REQ_TIMER
		    /* first remove the timer for this reply */
		    if(n) del_timer(&(n->state.timer));
#endif	
		    msg = kmalloc(sizeof(struct cryptint_msg_t), GFP_ATOMIC);
		    if(!msg) {
			printk(KERN_INFO "dsr_crypt: proc_qos_crypt_opt: failed to get memory, ignore message\n");
		    } else {
			msg->type = CRYPTINT_MSG_QOS_VERIFY;
			msg->sig_type = opt->sigtype;
			msg->sig_length = opt->len - CRYPTINT_QOS_PLAINTEXT_SIZE;
			msg->qos = opt->qos;
			msg->addr = opt->addr;
			memcpy(msg->plain, &(opt->qos), CRYPTINT_QOS_PLAINTEXT_SIZE);
			memcpy(msg->sig, opt->sig, msg->sig_length);
			DEBUGP("  proc_qos_crypt_opt: trigger CRYPTINT_MSG_QOS_VERIFY message: sigtype=%i, sig_length=%i\n", msg->sig_type, msg->sig_length);
			/* careful with that typecast! 
			 * cryptint_msg_t may be smaller than cryptint_msg_qos_verify_t */
			enqueue_msg((struct cryptint_msg_t *)msg, sizeof(struct cryptint_msg_qos_verify_t));
		    }
		}
	    }
	}
    }
}


void proc_qos_crypt_req_opt(struct sk_buff * skb, unsigned char * optptr) {
    struct dsr_qos_crypt_req_opt * opt = (struct dsr_qos_crypt_req_opt *) optptr;
    struct cryptint_msg_qos_sign_t *msg;
    struct dsr_qos_crypt_opt plain;
    unsigned char * ptr;
    int i;
    int n_addr;

    DEBUGP("  proc_qos_crypt_req_opt: type=%i, sigtype=%i, nonce=%i, len=%i\n", 
	   opt->type, 
	   opt->sigtype, 
	   opt->nonce,
	   opt->len);
    n_addr = (opt->len - 4) >> 2;
    i = 0;
    while(i < n_addr && opt->addr[i] != IPADDR) i++;
    if(i == n_addr) {
	DEBUGP("  proc_qos_crypt_req_opt: not in recipient list, ignoring.\n");
    } else {
	/* we are in recipient list */
	msg = kmalloc(sizeof(struct cryptint_msg_qos_sign_t), GFP_ATOMIC);
	if(!msg) {
	    printk(KERN_INFO "proc_qos_crypt_req_opt: failed to get memory, ignore message\n");
	} else {
	    msg->type = CRYPTINT_MSG_QOS_SIGN;
	    msg->sig_type = opt->sigtype;
	    msg->taddr = skb->nh.iph->saddr;
	    
	    plain.qos = dsrcrypt_get_qos();
	    plain.sigtype = opt->sigtype;
	    plain.addr = IPADDR;
	    plain.nonce = opt->nonce;
	    ptr = (unsigned char *)(&plain);
	    memcpy(msg->plain, ptr + 2, CRYPTINT_QOS_PLAINTEXT_SIZE);
	    
	    DEBUGP("  proc_qos_crypt_opt: trigger CRYPTINT_MSG_QOS_SIGN message\n");
	    /* careful with that typecast! 
	     * cryptint_msg_t may be smaller than cryptint_msg_qos_verify_t */
	    enqueue_msg((struct cryptint_msg_t *)msg, sizeof(struct cryptint_msg_qos_sign_t));
	}
    }
}
