Initial import
[olsrd.git] / lib / dyn_gw / src / olsrd_power.c
1
2 /*
3  * Copyright (c) 2004, Andreas T√łnnesen(andreto-at-olsr.org)
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without 
7  * modification, are permitted provided that the following conditions 
8  * are met:
9  *
10  * * Redistributions of source code must retain the above copyright notice, 
11  *   this list of conditions and the following disclaimer.
12  * * Redistributions in binary form must reproduce the above copyright notice, 
13  *   this list of conditions and the following disclaimer in the documentation 
14  *   and/or other materials provided with the distribution.
15  * * Neither the name of the UniK olsr daemon nor the names of its contributors 
16  *   may be used to endorse or promote products derived from this software 
17  *   without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
22  * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
23  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
26  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
27  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
28  * OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  */
31
32 /*
33  * Dynamic linked library example for UniK OLSRd
34  */
35
36 #include "olsrd_power.h"
37 #include <stdio.h>
38 #include <string.h>
39 #include <stdlib.h>
40
41 int ipc_socket;
42 int ipc_open;
43 int ipc_connection;
44
45 /**
46  *Do initialization here
47  *
48  *This function is called by the my_init
49  *function in uolsrd_plugin.c
50  */
51 int
52 olsr_plugin_init()
53 {
54   int i;
55   struct olsr_apm_info apm_info;
56
57   /* Initial IPC value */
58   ipc_open = 0;
59
60   /* Init list */
61   for(i = 0; i < HASHSIZE; i++)
62     {
63       list[i].next = &list[i];
64       list[i].prev = &list[i];
65     }
66
67   if(apm_read(&apm_info) < 0)
68     {
69       has_apm = 0;
70       olsr_printf(1, "No APM info avalible! This node will not generate powermessages!\n\n");
71     }
72   else
73     {
74       olsr_printf(1, "Node has APM info!\n");
75       has_apm = 1;
76     }
77
78   /* Register functions with olsrd */
79   olsr_parser_add_function(&olsr_parser, PARSER_TYPE, 1);
80
81   olsr_register_timeout_function(&olsr_timeout);
82
83   olsr_register_scheduler_event(&olsr_event, 2.5, 0, NULL);
84
85   return 1;
86 }
87
88 int
89 plugin_ipc_init()
90 {
91   struct sockaddr_in sin;
92
93   /* Init ipc socket */
94   if ((ipc_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) 
95     {
96       perror("IPC socket");
97       return 0;
98     }
99   else
100     {
101       /* Bind the socket */
102       
103       /* complete the socket structure */
104       memset(&sin, 0, sizeof(sin));
105       sin.sin_family = AF_INET;
106       sin.sin_addr.s_addr = INADDR_ANY;
107       sin.sin_port = htons(9999);
108       
109       /* bind the socket to the port number */
110       if (bind(ipc_socket, (struct sockaddr *) &sin, sizeof(sin)) == -1) 
111         {
112           perror("IPC bind");
113           return 0;
114         }
115       
116       /* show that we are willing to listen */
117       if (listen(ipc_socket, 1) == -1) 
118         {
119           perror("IPC listen");
120           return 0;
121         }
122
123
124       /* Register with olsrd */
125       add_olsr_socket(ipc_socket, &ipc_action);
126
127     }
128
129   return 1;
130 }
131
132 void
133 ipc_action(int fd)
134 {
135   struct sockaddr_in pin;
136   size_t addrlen;
137   char *addr;  
138
139   if ((ipc_connection = accept(ipc_socket, (struct sockaddr *)  &pin, &addrlen)) == -1)
140     {
141       perror("IPC accept");
142       exit(1);
143     }
144   else
145     {
146       addr = inet_ntoa(pin.sin_addr);
147       if(ntohl(pin.sin_addr.s_addr) != INADDR_LOOPBACK)
148         {
149           olsr_printf(1, "Front end-connection from foregin host(%s) not allowed!\n", addr);
150           close(ipc_connection);
151           return;
152         }
153       else
154         {
155           ipc_open = 1;
156           olsr_printf(1, "POWER: Connection from %s\n",addr);
157         }
158     }
159
160 }
161
162 /*
163  * destructor - called at unload
164  */
165 void
166 olsr_plugin_exit()
167 {
168   if(ipc_open)
169     close(ipc_socket);
170 }
171
172
173
174 /* Mulitpurpose funtion */
175 int
176 plugin_io(int cmd, void *data, size_t size)
177 {
178
179   switch(cmd)
180     {
181     default:
182       return 0;
183     }
184   
185   return 1;
186 }
187
188
189
190 /**
191  *A timeoutfunction called every time
192  *the scheduler is polled
193  */
194 void
195 olsr_timeout()
196 {
197   //printf("PLUGIN Timeout!\n");
198   struct pwrentry *tmp_list;
199   struct pwrentry *entry_to_delete;
200   int index;
201
202
203   for(index=0;index<HASHSIZE;index++)
204     {
205       tmp_list = list[index].next;
206       /*Traverse MID list*/
207       while(tmp_list != &list[index])
208         {
209           /*Check if the entry is timed out*/
210           if(olsr_timed_out(&tmp_list->timer))
211             {
212               entry_to_delete = tmp_list;
213               tmp_list = tmp_list->next;
214               olsr_printf(1, "POWER info for %s timed out.. deleting it\n", 
215                           olsr_ip_to_string(&entry_to_delete->originator));
216               /* Dequeue */
217               entry_to_delete->prev->next = entry_to_delete->next;
218               entry_to_delete->next->prev = entry_to_delete->prev;
219
220               /* Delete */
221               free(entry_to_delete);
222             }
223           else
224               tmp_list = tmp_list->next;
225         }
226     }
227
228   return;
229 }
230
231
232 /**
233  *Scheduled event
234  */
235 void
236 olsr_event()
237 {
238   union olsr_packet *packet;
239   union olsr_message *message;
240   struct interface *ifn;
241
242   /* If we can't produce power info we do nothing */ 
243   if(!has_apm)
244     return;
245
246   olsr_printf(1, "PLUG-IN: Generating package - ");
247
248   /* Cast the char* buffer to the packetformat */
249   packet = (union olsr_packet*)buffer;
250
251   /* Fetch the message based on IPversion */
252   if(ipversion == AF_INET)
253     message = (union olsr_message *)packet->v4.olsr_msg;
254   else
255     message = (union olsr_message *)packet->v6.olsr_msg;
256
257
258   /* looping trough interfaces */
259   for (ifn = ifs; ifn ; ifn = ifn->int_next) 
260     {
261       olsr_printf(1, "[%s]  ", ifn->int_name);
262       /* Fill message */
263       if(ipversion == AF_INET)
264         {
265           /* IPv4 */
266           message->v4.olsr_msgtype = MESSAGE_TYPE;
267           message->v4.olsr_vtime = double_to_me(7.5);
268           message->v4.olsr_msgsize = htons(sizeof(struct olsrmsg));
269           memcpy(&message->v4.originator, main_addr, ipsize);
270           message->v4.ttl = MAX_TTL;
271           message->v4.hopcnt = 0;
272           message->v4.seqno = htons(get_msg_seqno());
273           
274           get_powerstatus(&message->v4.msg);
275           
276           *outputsize = sizeof(struct olsrmsg) + sizeof(olsr_u32_t);
277           packet->v4.olsr_packlen = htons(*outputsize);
278         }
279       else
280         {
281           /* IPv6 */
282           message->v6.olsr_msgtype = MESSAGE_TYPE;
283           message->v6.olsr_vtime = double_to_me(7.5);
284           message->v6.olsr_msgsize = htons(sizeof(struct olsrmsg));
285           memcpy(&message->v6.originator, main_addr, ipsize);
286           message->v6.ttl = MAX_TTL;
287           message->v6.hopcnt = 0;
288           message->v6.seqno = htons(get_msg_seqno());
289           
290           get_powerstatus(&message->v6.msg);
291           
292           *outputsize = sizeof(struct olsrmsg6) + sizeof(olsr_u32_t);
293           packet->v6.olsr_packlen = htons(*outputsize);
294         }
295
296       /* Send data */
297       net_output(ifn);
298     }
299   olsr_printf(1, "\n");
300
301   print_power_table();
302
303   return;
304 }
305
306
307
308 void
309 olsr_parser(union olsr_message *m, struct interface *in_if, union olsr_ip_addr *in_addr)
310 {
311   struct  powermsg *message;
312   union olsr_ip_addr originator;
313   double vtime;
314
315   /* Fetch the originator of the messsage */
316   memcpy(&originator, &m->v4.originator, ipsize);
317
318   /* Fetch the message based on IP version */
319   if(ipversion == AF_INET)
320     {
321       message = &m->v4.msg;
322       vtime = me_to_double(m->v4.olsr_vtime);
323     }
324   else
325     {
326       message = &m->v6.msg;
327       vtime = me_to_double(m->v6.olsr_vtime);
328     }
329
330   /* Check if message originated from this node */
331   if(memcmp(&originator, main_addr, ipsize) == 0)
332     /* If so - back off */
333     return;
334
335   /* Check that the neighbor this message was received
336      from is symmetric */
337   if(check_neighbor_link(in_addr) != SYM_LINK)
338     {
339       /* If not symmetric - back off */
340       olsr_printf(1, "Received POWER from NON SYM neighbor %s\n", olsr_ip_to_string(in_addr));
341       return;
342     }
343
344   /* Check if this message has been processed before
345    * Remeber that this also registeres the message as
346    * processed if nessecary
347    */
348   if(!check_dup_proc(&originator,
349                      ntohs(m->v4.seqno))) /* REMEMBER NTOHS!! */
350     {
351       /* If so - do not process */
352       goto forward;
353     }
354
355   /* Process */
356
357   olsr_printf(1, "POWER PLUG-IN: Processing PWR from %s seqno: %d\n",
358               olsr_ip_to_string(&originator),
359               ntohs(m->v4.seqno));
360
361   /* Call a function that updates the database entry */
362   update_power_entry(&originator, message, vtime);
363
364
365  forward:
366   /* Forward the message if nessecary
367    * default_fwd does all the work for us!
368    */
369   default_fwd(m,
370               &originator,
371               ntohs(m->v4.seqno), /* IMPORTANT!!! */
372               in_if,
373               in_addr);
374
375 }
376
377
378
379 /**
380  *Update or register a new power entry
381  */
382 int
383 update_power_entry(union olsr_ip_addr *originator, struct powermsg *message, double vtime)
384 {
385   int hash;
386   struct pwrentry *entry;
387
388   hash = olsr_hashing(originator);
389
390   /* Check for the entry */
391   for(entry = list[hash].next;
392       entry != &list[hash];
393       entry = entry->next)
394     {
395       if(memcmp(originator, &entry->originator, ipsize) == 0)
396         {
397           /*
398           printf("Updaing power entry %s: ", olsr_ip_to_string(originator));
399           if(message->source_type == SOURCE_BATTERY)
400             printf("BATTERY P: %d%% T: %d mins\n",
401                    message->percentage,
402                    message->time_left);
403           else
404             printf("AC\n");
405           */
406
407           entry->source_type = message->source_type;
408           entry->percentage = message->percentage;
409           entry->time_left = message->time_left;
410
411           olsr_get_timestamp(vtime * 1000, &entry->timer);
412
413           return 0;
414         }
415     }
416
417   olsr_printf(1, "New power entry %s: ", olsr_ip_to_string(originator));
418
419   if(message->source_type == SOURCE_BATTERY)
420     olsr_printf(1, "BATTERY P: %d%% T: %d mins\n",
421            message->percentage,
422            message->time_left);
423   else
424     olsr_printf(1, "AC\n");
425
426   entry = olsr_malloc(sizeof(struct pwrentry), "POWERPLUGIN: new power entry");
427      
428   /* Fill struct */
429
430   memcpy(&entry->originator, originator, ipsize);
431
432   entry->source_type = message->source_type;
433   entry->percentage = message->percentage;
434   entry->time_left = message->time_left;
435   
436   olsr_get_timestamp(vtime * 1000, &entry->timer);
437
438   /* Queue */
439   entry->next = list[hash].next->prev;
440   entry->prev = &list[hash];
441   list[hash].next->prev = entry;
442   list[hash].next = entry;
443
444   return 1;
445 }
446
447
448 /**
449  *Print all registered power entries
450  */
451
452 void
453 print_power_table()
454 {
455   int hash;
456   struct pwrentry *entry;
457   char buf[200];
458
459   if(!ipc_open)
460     return;
461
462   ipc_send("--POWERTABLE--\n", 15);
463
464   for(hash = 0; hash < HASHSIZE; hash++)
465     /* Check for the entry */
466     for(entry = list[hash].next;
467         entry != &list[hash];
468         entry = entry->next)
469       {
470         sprintf(buf, "[%s]: ", olsr_ip_to_string(&entry->originator));
471         ipc_send(buf, strlen(buf));
472
473         if(entry->source_type == SOURCE_BATTERY)
474           {
475             sprintf(buf,
476                     "BATTERY P: %d%% T: %d mins\n",
477                     entry->percentage,
478                     entry->time_left);
479             ipc_send(buf, strlen(buf));
480           }
481         else
482           ipc_send("AC\n", 3);
483       }
484
485   ipc_send("--------------\n", 15);
486
487 }
488
489
490
491 int
492 ipc_send(char *data, int size)
493 {
494   if(!ipc_open)
495     return 0;
496
497   if (send(ipc_connection, data, size, MSG_NOSIGNAL) < 0) 
498     {
499       //perror("send - IPC");
500       olsr_printf(1, "(OUTPUT)IPC connection lost!\n");
501       close(ipc_connection);
502       //use_ipc = 0;
503       ipc_open = 0;
504       return -1;
505     }
506
507   return 1;
508 }
509
510 /**
511  *Fill a powermsg struct with power data
512  */
513 int
514 get_powerstatus(struct powermsg *msg)
515 {
516   struct olsr_apm_info apm_info;
517   
518   if(apm_read(&apm_info) < 0)
519     {
520       has_apm = 0;
521       olsr_printf(1, "No APM info avalible! This node will not generate powermessages!\n\n");
522     }
523
524   if(apm_info.ac_line_status)
525     {
526       msg->source_type = SOURCE_AC;
527       msg->percentage = 0;
528       msg->time_left = 0;
529     }
530   else
531     {
532       msg->source_type = SOURCE_BATTERY;
533       msg->percentage = apm_info.battery_percentage;
534       msg->time_left = apm_info.battery_time;
535     }
536
537   return 1;
538 }
539
540
541
542
543
544
545 int
546 apm_read(struct olsr_apm_info *ainfo)
547 {
548   char buffer[100];
549   char units[10];
550   FILE *apm_procfile;
551
552   /* Open procfile */
553   if((apm_procfile = fopen(APM_PROC, "r")) == NULL)
554     return -1;
555
556
557   fgets(buffer, sizeof(buffer) - 1, apm_procfile);
558   if(buffer == NULL)
559     {
560       /* Try re-opening the file */
561       if((apm_procfile = fopen(APM_PROC, "r")) < 0)
562         return -1;
563       fgets(buffer, sizeof(buffer) - 1, apm_procfile);
564       if(buffer == NULL)
565         {
566           /* Giving up */
567           fprintf(stderr, "OLSRD-POWER: Could not read APM info");
568           return -1;
569         }
570     }
571
572   buffer[sizeof(buffer) - 1] = '\0';
573
574   //printf("READ: %s\n", buffer);
575
576   /* Get the info */
577   sscanf(buffer, "%s %d.%d %x %x %x %x %d%% %d %s\n",
578          ainfo->driver_version,
579          &ainfo->apm_version_major,
580          &ainfo->apm_version_minor,
581          &ainfo->apm_flags,
582          &ainfo->ac_line_status,
583          &ainfo->battery_status,
584          &ainfo->battery_flags,
585          &ainfo->battery_percentage,
586          &ainfo->battery_time,
587          units);
588
589   ainfo->using_minutes = !strncmp(units, "min", 3) ? 1 : 0;
590
591   /*
592    * Should take care of old APM type info here
593    */
594
595   /*
596    * Fix possible percentage error
597    */
598   if(ainfo->battery_percentage > 100)
599     ainfo->battery_percentage = -1;
600
601   fclose(apm_procfile);
602
603   return 0;
604 }
605
606
607
608
609 /*************************************************************
610  *                 TOOLS DERIVED FROM OLSRD                  *
611  *************************************************************/
612
613
614 /**
615  *Hashing function. Creates a key based on
616  *an 32-bit address.
617  *@param address the address to hash
618  *@return the hash(a value in the 0-31 range)
619  */
620 olsr_u32_t
621 olsr_hashing(union olsr_ip_addr *address)
622 {
623   olsr_u32_t hash;
624   char *tmp;
625
626   if(ipversion == AF_INET)
627     /* IPv4 */  
628     hash = (ntohl(address->v4));
629   else
630     {
631       /* IPv6 */
632       tmp = (char *) &address->v6;
633       hash = (ntohl(*tmp));
634     }
635
636   //hash &= 0x7fffffff; 
637   hash &= HASHMASK;
638
639   return hash;
640 }
641
642
643
644 /**
645  *Checks if a timer has times out. That means
646  *if it is smaller than present time.
647  *@param timer the timeval struct to evaluate
648  *@return positive if the timer has not timed out,
649  *0 if it matches with present time and negative
650  *if it is timed out.
651  */
652 int
653 olsr_timed_out(struct timeval *timer)
654 {
655   return(timercmp(timer, now, <));
656 }
657
658
659
660 /**
661  *Initiates a "timer", wich is a timeval structure,
662  *with the value given in time_value.
663  *@param time_value the value to initialize the timer with
664  *@param hold_timer the timer itself
665  *@return nada
666  */
667 void
668 olsr_init_timer(olsr_u32_t time_value, struct timeval *hold_timer)
669
670   olsr_u16_t  time_value_sec;
671   olsr_u16_t  time_value_msec;
672
673   time_value_sec = time_value/1000;
674   time_value_msec = time_value-(time_value_sec*1000);
675
676   hold_timer->tv_sec = time_value_sec;
677   hold_timer->tv_usec = time_value_msec*1000;   
678 }
679
680
681
682
683
684 /**
685  *Generaties a timestamp a certain number of milliseconds
686  *into the future.
687  *
688  *@param time_value how many milliseconds from now
689  *@param hold_timer the timer itself
690  *@return nada
691  */
692 void
693 olsr_get_timestamp(olsr_u32_t delay, struct timeval *hold_timer)
694
695   olsr_u16_t  time_value_sec;
696   olsr_u16_t  time_value_msec;
697
698   time_value_sec = delay/1000;
699   time_value_msec= delay - (delay*1000);
700
701   hold_timer->tv_sec = now->tv_sec + time_value_sec;
702   hold_timer->tv_usec = now->tv_usec + (time_value_msec*1000);   
703 }
704
705
706 /**
707  *Converts a olsr_ip_addr to a string
708  *Goes for both IPv4 and IPv6
709  *
710  *NON REENTRANT! If you need to use this
711  *function twice in e.g. the same printf
712  *it will not work.
713  *You must use it in different calls e.g.
714  *two different printfs
715  *
716  *@param the IP to convert
717  *@return a pointer to a static string buffer
718  *representing the address in "dots and numbers"
719  *
720  */
721 char *
722 olsr_ip_to_string(union olsr_ip_addr *addr)
723 {
724
725   char *ret;
726   struct in_addr in;
727   
728   if(ipversion == AF_INET)
729     {
730       in.s_addr=addr->v4;
731       ret = inet_ntoa(in);
732     }
733   else
734     {
735       /* IPv6 */
736       ret = (char *)inet_ntop(AF_INET6, &addr->v6, ipv6_buf, sizeof(ipv6_buf));
737     }
738
739   return ret;
740 }
741
742