/* Kernel AODV v2.1 National Institute of Standards and Technology Luke Klein-Berndt ----------------------------------------------------- Version 2.1 new features: * Much more stable! * Added locks around important areas * Multihop Internet gatewaying now works * Multicast hack added * Many bug fixes! ----------------------------------------------------- Originally based upon MadHoc code. I am not sure how much of it is left anymore, but MadHoc proved to be a great starting point. MadHoc was written by - Fredrik Lilieblad, Oskar Mattsson, Petra Nylund, Dan Ouchterlony and Anders Roxenhag Mail: mad-hoc@flyinglinux.net This software is Open Source under the GNU General Public Licence. */ #include "rebroadcast_queue.h" /**************************************************** rebroadcast_queue ---------------------------------------------------- This is a queue used to store multicast packets that have to be rebroadcast. They get queued until they should be rebroadcast. We don't want to do are rebroadcasting on an interupt so the queued packets are rebroadcasted by a seperate thread which handles it at a later date... hopefully! :) ****************************************************/ #ifdef AODV_MULTICAST extern u_int32_t g_broadcast_ip; struct rebroadcast_queue_entry *rebroadcast_queue; struct rebroadcast_queue_entry *end_rebroadcast_queue; spinlock_t rebroadcast_lock = SPIN_LOCK_UNLOCKED; static int rebroadcast_pid; static wait_queue_head_t rebroadcast_wait; static atomic_t kill_thread; /**************************************************** insert_rebroadcast_queue_entry ---------------------------------------------------- Inserts an event into the event queue. dev is the device the event was recieved on. data points to the event itself. ****************************************************/ void lock_rebroadcast() { spin_lock_bh(&rebroadcast_lock); } void unlock_rebroadcast() { spin_unlock_bh(&rebroadcast_lock); } int insert_rebroadcast_queue_entry( u_int32_t dst_ip, unsigned int size,void *data,u_int8_t ttl) { struct rebroadcast_queue_entry *new_entry; //create a new entry if ((new_entry = (struct rebroadcast_queue_entry*)kmalloc(sizeof(struct rebroadcast_queue_entry),GFP_ATOMIC)) == NULL) { printk(KERN_WARNING "AODV: Error trying to allocate Rebroadcast Queue Entry\n"); return -ENOMEM; } new_entry->dst_ip = dst_ip; new_entry->size = size; new_entry->ttl=ttl-1; new_entry->time=getcurrtime(); new_entry->prev=NULL; //create space for the data and copy it there if ((new_entry->data = kmalloc(size,GFP_ATOMIC)) == NULL) { kfree(new_entry); printk(KERN_WARNING "AODV: Error trying to allocate Rebroadcast Queue Entry Data\n"); return -ENOMEM; } memcpy(new_entry->data,data,size); /*lock table*/ lock_rebroadcast(); //Set all the variables new_entry->next=rebroadcast_queue; if (rebroadcast_queue==NULL) { end_rebroadcast_queue=new_entry; } else { rebroadcast_queue->prev=new_entry; } rebroadcast_queue=new_entry; /*unlock table*/ unlock_rebroadcast(); kick_rebroadcast(); return 0; } /**************************************************** get_first_rebroadcast_queue_entry ---------------------------------------------------- Gets the first entry from the event queue ****************************************************/ struct rebroadcast_queue_entry *get_next_rebroadcast_queue_entry( void ) { struct rebroadcast_queue_entry *temp_entry=NULL; /*lock table*/ lock_rebroadcast(); if (end_rebroadcast_queue!=NULL) { temp_entry=end_rebroadcast_queue; if (temp_entry->prev==NULL) { end_rebroadcast_queue=NULL; rebroadcast_queue=NULL; } else { end_rebroadcast_queue=temp_entry->prev; end_rebroadcast_queue->next=NULL; } } /*unlock table*/ unlock_rebroadcast(); return temp_entry; } /**************************************************** init_rebroadcast_queue ---------------------------------------------------- Initalizes the event queue ****************************************************/ int init_rebroadcast_queue( void ) { rebroadcast_queue = NULL; end_rebroadcast_queue = NULL; rebroadcast_pid=kernel_thread((void *) &rebroadcast_thread,NULL,0); return 0; } void kill_rebroadcast_thread() { wake_up_interruptible(&rebroadcast_wait); //lower semaphore for the thread atomic_set(&kill_thread,1); atomic_set(&kill_thread,1); atomic_set(&kill_thread,1); } void kick_rebroadcast() { //We are trying to wake up AODV!!! //AODV thread is an interupptible sleep state.... this interupts it wake_up_interruptible(&rebroadcast_wait); } void rebroadcast_thread() { //The queue holding all the events to be dealt with struct rebroadcast_queue_entry *working_event; struct timeval delay_time; delay_time.tv_sec = 0; delay_time.tv_usec = 100000; //Initalize the variables init_waitqueue_head(&rebroadcast_wait); atomic_set(&kill_thread,0); //Name our thread lock_kernel(); sprintf(current->comm,"rebroadcast"); exit_mm(current); unlock_kernel(); //why would I ever want to stop ? :) for (;;) { //should the thread exit? if (atomic_read(&kill_thread)) { return; } //goto sleep until we recieve an interupt interruptible_sleep_on(&rebroadcast_wait); //should the thread exit? if (atomic_read(&kill_thread)) { return; } //While the buffer is not empty while((working_event=get_next_rebroadcast_queue_entry())!=NULL) { if ((getcurrtime() - working_event->time) < 50) { rebroadcast(working_event->dst_ip,working_event->size,working_event->data,working_event->ttl); } else { printk(KERN_NOTICE "AODV: Rebroadcast queue getting funky!\n"); } kfree(working_event->data); kfree(working_event); } } } #endif