Patch for WANIPv6FirewallControl:1 service
[igd2-for-linux:igd2-for-linux.git] / linuxigd2 / src / pinholev6.c
1 /** 
2  * This file is part of igd2-for-linux project
3  * Copyright © 2011 France Telecom.
4  * Contact: fabrice.fontaine@orange-ftgroup.com
5  * Developer(s): fabrice.fontaine@orange-ftgroup.com, rmenard.ext@orange-ftgroup.com
6  *  
7  * This program is free software: you can redistribute it and/or modify 
8  * it under the terms of the GNU General Public License as published by 
9  * the Free Software Foundation, either version 2 of the License, or 
10  * (at your option) any later version. 
11  * 
12  * This program is distributed in the hope that it will be useful, 
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of 
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
15  * GNU General Public License for more details. 
16  * 
17  * You should have received a copy of the GNU General Public License 
18  * along with this program, see the /doc directory of this program. If 
19  * not, see http://www.gnu.org/licenses/. 
20  * 
21  */
22
23 #ifdef __cplusplus
24 extern "C" {
25 #endif
26
27 #include <netinet/in.h>
28 #include <stdio.h>
29 #include <arpa/inet.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <upnp/TimerThread.h>
34 #include <upnp/upnpconfig.h>
35 #include <regex.h>
36 #include <sys/types.h>
37 #include <time.h>
38
39 #include "util.h"
40 #include "globals.h"
41 #include "gatedevice.h"
42 #include "pinholev6.h"
43
44 static const char * add_rule_str = "ip6tables -I %s " //upnp forward chain
45         "-i %s "        //input interface
46         "-o %s "        //output interface
47         "-s %s "        //source address
48         "-d %s "        //destionation address
49         "-p %i "        //protocol
50         "--sport %i "   //source port
51         "--dport %i "   //destination port
52         "-j ACCEPT";
53
54 static const char * add_rule_raw_str = "ip6tables -t raw -I PREROUTING "
55         "-i %s "        //input interface
56         "-s %s "        //source address
57         "-d %s "        //destination address
58         "-p %i "        //protocol
59         "--sport %i "   //source port
60         "--dport %i "   //destination port
61         "-j TRACE";
62
63 //no remote -> no source address in the rule
64 static const char * add_rule_no_remote_str = "ip6tables "
65         "-I %s "        //upnp forward chain
66         "-i %s "        //input interface
67         "-o %s "        //output interface
68         "-d %s "        //destination address
69         "-p %i "        //protocol
70         "--sport %i "   //source port
71         "--dport %i "   //destination port
72         "-j ACCEPT";
73
74 static const char * add_rule_raw_no_remote_str = "ip6tables -t raw "
75         "-I PREROUTING "
76         "-i %s "        //input interface
77         "-d %s "        //destination address
78         "-p %i "        //protocol
79         "--sport %i "   //source port
80         "--dport %i "   //destination port
81         "-j TRACE";
82
83
84 static const char * del_rule_str = "ip6tables -D %s " //upnp forward chain
85         "-i %s "        //input interface
86         "-o %s "        //output interface
87         "-s %s "        //source address
88         "-d %s "        //destination address
89         "-p %i "        //protocol
90         "--sport %i "   //source port
91         "--dport %i "   //destination port
92         "-j ACCEPT";
93
94 static const char * del_rule_raw_str = "ip6tables -t raw -D PREROUTING "
95         "-i %s "        //input interface
96         "-s %s "        //source address
97         "-d %s "        //destination address
98         "-p %i "        //protocol
99         "--sport %i "   //source port
100         "--dport %i "   //destination port
101         "-j TRACE";
102
103 static const char * del_rule_no_remote_str = "ip6tables "
104         "-D %s "        //upnp forward chain
105         "-i %s "        //input interface
106         "-o %s "        //output interface
107         "-d %s "        //destination address
108         "-p %i "        //protocol
109         "--sport %i "   //source port
110         "--dport %i "   //destination port
111         "-j ACCEPT";
112
113 static const char * del_rule_raw_no_remote_str = "ip6tables -t raw "
114         "-D PREROUTING "
115         "-i %s "        //input interface
116         "-d %s "        //destination address
117         "-p %i "        //protocol
118         "--sport %i "   //source port
119         "--dport %i "   //destination port
120         "-j TRACE";
121
122 /**
123  * PRIVATE FUNCTIONS
124  */
125
126 extern ithread_mutex_t DevMutex;
127
128 int phv6_scheduleExpiration(struct pinholev6 *pinhole);
129
130 int phv6_cancelExpiration(struct pinholev6 *pinhole);
131
132
133 /**
134  * this functions seeks an available id in the pinhole list
135  *
136  * @param pointer to the newly found unique id.
137  * @return 1 if ok, 0 otherwise
138  */
139 int findUniqueID(uint32_t * uniqueId)
140 {
141     struct pinholev6 *pinhole;
142     uint32_t candidate_id;
143
144     pinhole = ph_first;
145     candidate_id = 0;
146
147
148     while(pinhole != NULL)
149     {
150         if(pinhole->unique_id == candidate_id) {
151             candidate_id++;
152             if (candidate_id == 0) {
153                 //integer overflow, no Unique ID available
154                 return 0;
155             }
156             pinhole = ph_first;
157         }
158         else pinhole = pinhole->next;
159     }
160
161     *uniqueId = candidate_id;
162
163     return 1;
164
165 }
166
167 /**
168  * -----------------------------------------------------------------------------
169  * PUBLIC FUNCTIONS
170  * -----------------------------------------------------------------------------
171  */
172
173 /**
174  * This functions initializes ip6tables and the pinhole list
175  *
176  * @return 1 if ok
177  */
178 int phv6_init(void)
179 {
180     //pinhole list initialization
181     ph_first = NULL;
182 #ifdef UPNP_ENABLE_IPV6
183     int rc;
184     //string used for system commands
185     char command[250];
186
187     //the nf_conntrack module gives the outbound pinhole timeout information
188     trace(3, "loading nf_conntrack module");
189     rc = system("/sbin/modprobe nf_conntrack");
190
191     //the ip6t_LOG module enables the "raw" table for ip6tables
192     //this is useful for the checkpinholeworking action
193     trace(3, "loading ip6t_LOG module");
194     rc = system("/sbin/modprobe ip6t_LOG");
195
196     trace(3, "ip6tables initialization");
197
198     snprintf(command, 250, "ip6tables -I INPUT -p tcp --dport %i -j ACCEPT", UpnpGetServerPort6());
199     rc = system(command);
200     snprintf(command, 250, "ip6tables -I INPUT -p udp --dport %i -j ACCEPT", UpnpGetServerPort6());
201     rc = system(command);
202 #endif
203
204     return 1;
205 }
206
207
208
209 /**
210  * This function closes ip6tables and free the pinhole list
211  *
212  * @return 1 if ok
213  */
214 int phv6_close(void)
215 {
216     //pinhole list deletion
217     struct pinholev6 * pinhole;
218     struct pinholev6 * p_delete = ph_first;
219     //int rc;
220
221     while(p_delete!=NULL)
222     {
223         pinhole = p_delete->next;
224         phv6_cancelExpiration(p_delete);
225         phv6_ip6table_deleteRule(p_delete->internal_client,
226                 p_delete->remote_host,
227                 p_delete->internal_port,
228                 p_delete->remote_port,
229                 p_delete->protocol);
230         free(p_delete->internal_client);
231         if(p_delete->remote_host != NULL) free(p_delete->remote_host);
232         free(p_delete);
233         p_delete = pinhole;
234     }
235
236     trace(3, "ip6tables reset");
237
238     return 1;
239 }
240
241 /**
242  * This function gives the rule number associated with the
243  * given pinhole id in the ip6tables policies
244  *
245  * @param id the pinhole's unique id
246  * @param lineNumber a pointer to an int that stores the line number
247  * @return 1 if the pinhole is found, 0 otherwise
248  */
249 int phv6_findLineNumber(uint32_t id, int * lineNumber)
250 {
251     struct pinholev6 *p;
252     //the rule number begins at 1
253     int index = 1;
254
255     p = ph_first;
256
257     //as the newest pinhole are inserted at the beginning of queue
258     //the index is the depth of the pinhole in the queue
259     while( p != NULL )
260     {
261         if( p->unique_id == id )
262         {
263             *lineNumber = index;
264             return 1;
265         }
266         else
267         {
268             p = p->next;
269         }
270         index++;
271     }
272
273     return 0;
274 }
275
276 /**
277  * This function gives the number of packets that went through
278  * the given pinhole
279  *
280  * @param id the pinhole's unique id
281  * @param packets a pointer to an int that stores the result
282  * @return 1 if the pinhole has been found, 0 otherwise
283  */
284 int phv6_getPinholePackets(uint32_t id, int * packets)
285 {
286     int lineNumber;
287     FILE * pipe;
288     char command[100];
289     char garbage[100];
290     int rc;
291
292     if(phv6_findLineNumber(id, &lineNumber))
293     {
294         trace(1, "line number : %i", lineNumber);
295         snprintf(command, 100, "ip6tables -L %s %i -v -n -x",
296                 g_vars.ipv6forwardChain,
297                 lineNumber);
298         pipe = popen(command, "r");
299         rc = fscanf(pipe, "%i %s", packets, garbage);
300         pclose(pipe);
301
302         return 1;
303     }
304
305     return 0;
306 }
307
308 /**
309  * This funtion seeks the pinhole according to the id given in parameter
310  * if the pinhole is found, the pointer given in paramter is updated with the
311  * pinhole
312  *
313  * @param the searched id
314  * @param a pointer to the searched pinhole
315  * @return 1 if found, 0 otherwise
316  */
317 int phv6_findPinhole(uint32_t id, struct pinholev6 ** pinhole)
318 {
319     struct pinholev6 *p;
320
321     p = ph_first;
322
323     while( p != NULL )
324     {
325         if( p->unique_id == id )
326         {
327             *pinhole = p;
328             return 1;
329         }
330         else
331         {
332             p = p->next;
333         }
334     }
335
336     return 0;
337
338 }
339
340 /**
341  * This function verifies if the given pinhole already exists in the list
342  *
343  * @param internal_client A string representing the client address
344  * @param remote_host A string representing the remote host
345  * @param internal_port A string representing the internal port
346  * @param remote_port A string representing the remote port
347  * @param protocol A string representing the protocol
348  * @param uniqueID An int pointer giving the uniqueid of the existing pinhole
349  * @return 1 if true with the uniqueID value, 0 otherwise
350  */
351 int phv6_existingPinhole(char *_internal_client,
352         char *_remote_host,
353         char *_internal_port,
354         char *_remote_port,
355         char *_protocol,
356         uint32_t *uniqueID)
357 {
358     struct pinholev6 *p = ph_first;
359     struct in6_addr internal_client;
360     struct in6_addr remote_host;
361     uint16_t internal_port;
362     uint16_t remote_port;
363     uint16_t protocol;
364
365     inet_pton(AF_INET6, _internal_client, &internal_client);
366     inet_pton(AF_INET6, _remote_host, &remote_host);
367     internal_port = atoi(_internal_port);
368     remote_port = atoi(_remote_port);
369     protocol = atoi(_protocol);
370
371     while(p != NULL)
372     {
373         if((memcmp(p->internal_client, &internal_client, 16) == 0)
374                 && (p->internal_port == internal_port)
375                 && (p->remote_port == remote_port)
376                 && (p->protocol == protocol))
377         {
378             if(p->remote_host != NULL) {
379                 if(memcmp(p->remote_host, &remote_host, 16) == 0) {
380                     *uniqueID = p->unique_id;
381                     return 1;
382                 }
383             }
384             //wildcard case
385             else if(strcmp(_remote_host, "") == 0) {
386                 *uniqueID = p->unique_id;
387                 return 1;
388             }
389
390
391         }
392         p = p->next;
393     }
394     return 0;
395 }
396
397 /**
398  * This functions adds a pinhole in the pinhole list
399  *
400  * @param internal_client A string representing the client address
401  * @param remote_host A string representing the remote host
402  * @param internal_port A string representing the internal port
403  * @param remote_port A string representing the remote port
404  * @param protocol A string representing the protocol
405  * @param lease_time A unsigned integer giving the desired lease_time
406  * @param uniqueId An int pointer giving the uniqueid of the existing pinhole
407  * @return 1 if Ok, 0 otherwise. The new unique_id is given in the pointer
408  */
409 int phv6_addPinhole(char *internal_client,
410         char *remote_host,
411         char *internal_port,
412         char *remote_port,
413         char *protocol,
414         uint32_t lease_time,
415         uint32_t *uniqueId)
416 {
417     struct pinholev6 *p_new;
418
419     //allocate the pinhole memory
420     p_new = (struct pinholev6 *)malloc(sizeof(struct pinholev6));
421     if(p_new == NULL) return -1;
422
423     //copy the internal client address
424     p_new->internal_client = (struct in6_addr *)malloc(sizeof(struct in6_addr));
425     if(p_new->internal_client == NULL) {
426         free(p_new);
427         return -1;
428     }
429     inet_pton(AF_INET6, internal_client, p_new->internal_client);
430
431     //copy the remote host address (if not wildcarded)
432     if(strcmp(remote_host, "") != 0) {
433         p_new->remote_host = (struct in6_addr *)malloc(sizeof(struct in6_addr));
434         if(p_new->remote_host == NULL) {
435             free(p_new->internal_client);
436             free(p_new);
437             return -1;
438         }
439
440         inet_pton(AF_INET6, remote_host, p_new->remote_host);
441     }
442     else p_new->remote_host = NULL;
443
444     p_new->internal_port = atoi(internal_port);
445     p_new->remote_port = atoi(remote_port);
446     p_new->protocol = atoi(protocol);
447     p_new->lease_time = lease_time;
448     p_new->next = NULL;
449
450     findUniqueID(&p_new->unique_id);
451     *uniqueId = p_new->unique_id;
452
453     if(ph_first == NULL)
454     {
455         ph_first = p_new;
456     }
457     else
458     {
459         //adding the new pinhole at the top of the queue
460         p_new->next = ph_first;
461         ph_first = p_new;
462     }
463
464     phv6_scheduleExpiration(p_new);
465     phv6_ip6table_addRule(p_new->internal_client,
466             p_new->remote_host,
467             p_new->internal_port,
468             p_new->remote_port,
469             p_new->protocol);
470
471
472     return 1;
473 }
474
475 /**
476  * Deletes the pinhole which unique_id is given in parameter.
477  *
478  * @param id The unique id of the pinhole to delete
479  * @return 1 if ok, 0 otherwise
480  */
481 int phv6_deletePinhole(uint32_t id)
482 {
483     struct pinholev6 *p;
484     struct pinholev6 *p_delete;
485
486     if(ph_first == NULL) return 0;
487
488     //this case is when the first pinhole of the list is the targeted one
489     if(ph_first->unique_id == id)
490     {
491         if(ph_first->event_id >= 0)
492             phv6_cancelExpiration(ph_first);
493
494         if(ph_first->next!= NULL)
495         {
496             p = ph_first->next;
497             phv6_ip6table_deleteRule(ph_first->internal_client,
498                     ph_first->remote_host,
499                     ph_first->internal_port,
500                     ph_first->remote_port,
501                     ph_first->protocol);
502             free(ph_first->internal_client);
503             if(ph_first->remote_host != NULL) free(ph_first->remote_host);
504             free(ph_first);
505             ph_first = p;
506         }
507         else
508         {
509             phv6_ip6table_deleteRule(ph_first->internal_client,
510                     ph_first->remote_host,
511                     ph_first->internal_port,
512                     ph_first->remote_port,
513                     ph_first->protocol);
514
515             free(ph_first->internal_client);
516             if(ph_first->remote_host != NULL) free(ph_first->remote_host);
517             free(ph_first);
518             ph_first = NULL;
519         }
520
521         return 1;
522     }
523
524     p = ph_first;
525
526     while(p->next != NULL)
527     {
528         if(p->next->unique_id == id)
529         {
530             //the pinhole has been found
531             p_delete = p->next;
532             p->next = p_delete->next;
533
534             if(p_delete->event_id >= 0)
535                 phv6_cancelExpiration(p_delete);
536
537             phv6_ip6table_deleteRule(p_delete->internal_client,
538                     p_delete->remote_host,
539                     p_delete->internal_port,
540                     p_delete->remote_port,
541                     p_delete->protocol);
542             free(p_delete->internal_client);
543             if(p_delete->remote_host != NULL) free(p_delete->remote_host);
544             free(p_delete);
545             return 1;
546         }
547         p = p->next;
548     }
549
550     return 0;
551 }
552
553 /**
554  * Updates the pinhole given in parameter with the new lease time
555  *
556  * @param id The unique id of the pinhole to update
557  * @param lease_time The new lease time
558  * @return 1 if ok 0 otherwise.
559  */
560 int phv6_updatePinhole(uint32_t id, uint32_t lease_time)
561 {
562     struct pinholev6 * pinhole;
563
564     if(phv6_findPinhole(id, &pinhole))
565     {
566         pinhole->lease_time = lease_time;
567         phv6_cancelExpiration(pinhole);
568         phv6_scheduleExpiration(pinhole);
569         return 1;
570     }
571
572     return 0;
573 }
574
575 /**
576  * adds a new rule in the IP6table configuration
577  *
578  * @param internal_client A string representing the client address
579  * @param remote_host A string representing the remote host
580  * @param internal_port A string representing the internal port
581  * @param remote_port A string representing the remote port
582  * @param protocol A string representing the protocol
583  * @return 1 if Ok
584  */
585 int phv6_ip6table_addRule(struct in6_addr *internal_client,
586         struct in6_addr *remote_host,
587         uint16_t internal_port,
588         uint16_t remote_port,
589         uint16_t protocol)
590 {
591
592     //string used to pass ip6tables commands
593     char command[250];
594     int rc;
595
596     //pinhole parameters. The remote host is tested
597     //to see if it is wildcarded
598     char internal_client_str[INET6_ADDRSTRLEN];
599     char remote_host_str[INET6_ADDRSTRLEN];
600
601     inet_ntop(AF_INET6, internal_client,
602             internal_client_str, INET6_ADDRSTRLEN);
603
604     //not wildcard
605     if (remote_host)
606     {
607         //add the firewall rule
608         inet_ntop(AF_INET6, remote_host,
609                 remote_host_str, INET6_ADDRSTRLEN);
610
611         snprintf(command, 250, add_rule_str,
612                 g_vars.ipv6forwardChain,
613                 g_vars.extInterfaceName,
614                 g_vars.intInterfaceName,
615                 remote_host_str,
616                 internal_client_str,
617                 protocol,
618                 remote_port,
619                 internal_port);
620
621         rc = system(command);
622
623         trace(3, command);
624
625         //add the trace rule
626         snprintf(command, 250, add_rule_raw_str,
627                 g_vars.extInterfaceName,
628                 remote_host_str,
629                 internal_client_str,
630                 protocol,
631                 remote_port,
632                 internal_port);
633
634         rc = system(command);
635
636         trace(3, command);
637
638     }
639     //remote host wildcarded
640     else
641     {
642         snprintf(command, 250, add_rule_no_remote_str,
643                 g_vars.ipv6forwardChain,
644                 g_vars.extInterfaceName,
645                 g_vars.intInterfaceName,
646                 internal_client_str,
647                 protocol,
648                 remote_port,
649                 internal_port);
650
651         rc = system(command);
652         trace(3, command);
653
654         snprintf(command, 250, add_rule_raw_no_remote_str,
655                 g_vars.extInterfaceName,
656                 internal_client_str,
657                 protocol,
658                 remote_port,
659                 internal_port);
660
661         rc = system(command);
662         trace(3, command);
663
664     }
665
666     return 1;
667 }
668
669 /**
670  * Deletes a rule in ip6tables
671  *
672  * @param internal_client A string representing the client address
673  * @param remote_host A string representing the remote host
674  * @param internal_port A string representing the internal port
675  * @param remote_port A string representing the remote port
676  * @param protocol A string representing the protocol
677  * @return 1 if Ok
678  */
679 int phv6_ip6table_deleteRule(struct in6_addr *internal_client,
680         struct in6_addr *remote_host,
681         uint16_t internal_port,
682         uint16_t remote_port,
683         uint16_t protocol)
684 {
685
686
687     char command[250];
688     char internal_client_str[INET6_ADDRSTRLEN];
689     char remote_host_str[INET6_ADDRSTRLEN];
690     int rc;
691
692     inet_ntop(AF_INET6, internal_client,
693             internal_client_str, INET6_ADDRSTRLEN);
694
695     if (remote_host)
696     {
697         inet_ntop(AF_INET6, remote_host,
698                 remote_host_str, INET6_ADDRSTRLEN);
699
700         snprintf(command, 250, del_rule_str,
701                 g_vars.ipv6forwardChain,
702                 g_vars.extInterfaceName,
703                 g_vars.intInterfaceName,
704                 remote_host_str,
705                 internal_client_str,
706                 protocol,
707                 remote_port,
708                 internal_port);
709
710         rc = system(command);
711         trace(3, command);
712
713         snprintf(command, 250, del_rule_raw_str,
714                 g_vars.extInterfaceName,
715                 remote_host_str,
716                 internal_client_str,
717                 protocol,
718                 remote_port,
719                 internal_port);
720
721         rc = system(command);
722         trace(3, command);
723
724     }
725     else
726     {
727         snprintf(command, 250, del_rule_no_remote_str,
728                 g_vars.ipv6forwardChain,
729                 g_vars.extInterfaceName,
730                 g_vars.intInterfaceName,
731                 internal_client_str,
732                 protocol,
733                 remote_port,
734                 internal_port);
735
736         rc = system(command);
737         trace(3, command);
738
739         snprintf(command, 250, del_rule_raw_no_remote_str,
740                 g_vars.extInterfaceName,
741                 internal_client_str,
742                 protocol,
743                 remote_port,
744                 internal_port);
745
746         rc = system(command);
747         trace(3, command);
748
749     }
750
751     return 1;
752 }
753
754 /**
755  * Set expiration event free.
756  *
757  * @param event Expiration event.
758  */
759 void phv6_freeEvent(struct phv6_expirationEvent *event)
760 {
761     if (event != NULL && event->pinhole !=NULL)
762         event->pinhole->event_id = -1;
763     free(event);
764 }
765
766 /**
767  * This function makes a pinhole expires as its lease time is reached
768  *
769  * @param data Expiration event.
770  */
771 void phv6_expiration(void *data)
772 {
773     struct phv6_expirationEvent *event = ( struct phv6_expirationEvent * ) data;
774
775     ithread_mutex_lock(&DevMutex);
776
777     event->pinhole->event_id = -1;
778     phv6_deletePinhole(event->pinhole->unique_id);
779     phv6_freeEvent(event);
780
781     ithread_mutex_unlock(&DevMutex);
782 }
783
784 /**
785  * This function schedules the expiration when this pinhole is created or updated
786  *
787  * @param pinhole The pinhole to expire
788  * @return the event_id created
789  */
790 int phv6_scheduleExpiration(struct pinholev6 *pinhole)
791 {
792     ThreadPoolJob job;
793     struct phv6_expirationEvent *event;
794
795     event = (struct phv6_expirationEvent *)malloc(
796             sizeof(struct phv6_expirationEvent));
797     if(event == NULL)
798     {
799         return 0;
800     }
801
802     event->pinhole = pinhole;
803
804     TPJobInit( &job, ( start_routine ) phv6_expiration, event );
805     TPJobSetFreeFunction( &job, ( free_routine ) phv6_freeEvent );
806
807     if( TimerThreadSchedule(&gExpirationTimerThread,
808             pinhole->lease_time,
809             REL_SEC,
810             &job,
811             SHORT_TERM,
812             &(event->event_id))
813             != UPNP_E_SUCCESS )
814     {
815         free( event );
816         return 0;
817     }
818
819     pinhole->event_id = event->event_id;
820
821     return event->event_id;
822 }
823
824 /**
825  * This function cancels the expiration when the pinhole is updated or deleted
826  *
827  * @param pinhole The pinhole to expire
828  * @return 1 if Ok
829  */
830 int phv6_cancelExpiration(struct pinholev6 * pinhole)
831 {
832     ThreadPoolJob job;
833
834     trace(1,"Canceling expiration for pinhole : %i",pinhole->unique_id);
835
836     if( TimerThreadRemove(&gExpirationTimerThread,pinhole->event_id, &job)==0 )
837     {
838         phv6_freeEvent((struct phv6_expirationEvent *)job.arg);
839     }
840     else
841     {
842         trace(1,"  TimerThreadRemove failed!");
843         return 0;
844     }
845     return 1;
846 }
847
848 /**
849  * This function checks if a pinhole really manages the packets that have to be
850  * treated by the pinhole given in parameter
851  *
852  * NB : this function does not literraly checks if the pinhole is working. This
853  * function verifies if some traffic matching the pinhole parameters is
854  * received by checking the data stored in /var/log/kern.log. Those data are generated
855  * by the ip6t_LOG module. If some traffic is received, it checks that the last packet
856  * matching the pinhole parameters passed through this pinhole. It the packets did so,
857  * the function returns 1. If the packet passed through another rule, it will return 0.
858  * If no traffic is detected, it returns -1.
859  *
860  * @param pinhole The pinhole to inspect
861  * @return -1 No traffic detected
862  * @return 0 Packet treated by another rule
863  * @return 1 The Pinhole manages the packets
864  */
865 int phv6_checkPinholeWorking(int pinhole_id)
866 {
867     regex_t re_packet;
868     regex_t re_rule;
869
870     char regex_packet[512];
871     char regex_rule[256];
872
873     regmatch_t pmatch[4];
874     time_t current_time = time(NULL);
875     time_t old_time = current_time - 60;
876     //60 sec before the action is called, traffic is detected
877     //within 1 minute
878
879
880     struct tm * cur_time_tm = malloc(sizeof(struct tm));
881     struct tm * old_time_tm = malloc(sizeof(struct tm));
882
883     char cur_time_str[100];
884     char old_time_str[100];
885
886     //this string is used to store the last occurence
887     //of a matching packet
888     char packet_line[1024];
889
890     //the pinhole to check
891     struct pinholev6 * pinhole = NULL;
892
893     int rule = 0;
894
895     char * protocol = "";
896
897     char internal_client_str[INET6_ADDRSTRLEN];
898     char remote_host_str[INET6_ADDRSTRLEN];
899
900     struct in6_addr internal_client;
901     struct in6_addr remote_host;
902
903     FILE * log_file;
904
905     phv6_findPinhole(pinhole_id, &pinhole);
906     phv6_findLineNumber(pinhole_id, &rule);
907
908     //processing the current time, and the curren_time - 60 sec
909     localtime_r(&current_time,cur_time_tm);
910     localtime_r(&old_time,old_time_tm);
911
912     //building the regex for those times.
913     strftime(cur_time_str, 100, "%b[[:blank:]]+%d[[:blank:]]+%H:%M", cur_time_tm);
914     strftime(old_time_str, 100, "%b[[:blank:]]+%d[[:blank:]]+%H:%M", old_time_tm);
915
916     if(pinhole->protocol == 6 ) protocol = "TCP";
917     else if(pinhole->protocol == 17) protocol = "UDP";
918     else if(pinhole->protocol == 136) protocol = "UDP_LITE";
919
920
921     // The trace is as following :
922     //Jul  2 16:31:38 r-lnx-bang000 kernel: [27793.027210]
923     //TRACE: filter:FORWARD_upnp:rule:4 IN=eth0 OUT=eth1
924     //SRC=3ffe:0000:0000:0000:0000:0000:0000:0004
925     //DST=2032:0000:0000:0000:0000:0000:0000:0002
926     //LEN=72 TC=0 HOPLIMIT=63 FLOWLBL=0 PROTO=TCP SPT=1234 DPT=5678
927     //SEQ=3186301092 ACK=1756907998 WINDOW=90 RES=0x00 ACK FIN URGP=0 OPT (0101080A001FA3A4006866CD)
928
929     //Building the regex matching the above trace
930     snprintf(regex_packet, 512,
931             "(%s|%s):[[:print:]]+filter:%s:[[:print:]]+"
932             "SRC=([[:graph:]]+)[[:blank:]]DST=([[:graph:]]+)[[:blank:]]"
933             "[[:print:]]+"
934             "PROTO=%s[[:blank:]]"
935             "SPT=%i[[:blank:]]"
936             "DPT=%i[[:blank:]][[:print:]]+",
937             cur_time_str,
938             old_time_str,
939             g_vars.ipv6forwardChain,
940             protocol,
941             pinhole->remote_port,
942             pinhole->internal_port);
943
944     //Building the regex to find the rule number in ip6tables
945     snprintf(regex_rule, 256, "filter:%s:rule:([[:digit:]]+)",g_vars.ipv6forwardChain);
946
947     regcomp(&re_packet,regex_packet, REG_EXTENDED);
948     regcomp(&re_rule, regex_rule, REG_EXTENDED);
949
950     //all the acket traces are store in kern.log (kernel module)
951     if ((log_file=fopen("/var/log/kern.log","r")) == NULL) return -1;
952     else {
953         char line[1024];
954         int found = 0;
955         // Walk through the file line by line
956         while (fgets(line,1024,log_file) != NULL)
957         {
958             // Search the last line where the packet is traced
959             if ( regexec(&re_packet,line,4,pmatch,0) == 0 )
960             {
961
962                 //find the remote_host
963                 strncpy (remote_host_str,
964                         &line[pmatch[2].rm_so],
965                         pmatch[2].rm_eo-pmatch[2].rm_so);
966                 remote_host_str[pmatch[2].rm_eo-pmatch[2].rm_so] = '\0';
967
968                 //find the internal_client
969                 strncpy (internal_client_str,
970                         &line[pmatch[3].rm_so],
971                         pmatch[3].rm_eo-pmatch[3].rm_so);
972                 internal_client_str[pmatch[3].rm_eo-pmatch[3].rm_so] = '\0';
973
974                 inet_pton(AF_INET6, internal_client_str, &internal_client);
975                 inet_pton(AF_INET6, remote_host_str, &remote_host);
976
977                 //testing the addresses
978                 if(IN6_ARE_ADDR_EQUAL(&internal_client,
979                         pinhole->internal_client) )
980
981                 {
982                     //if remote_host is wildcarded
983                     if (pinhole->remote_host != NULL) {
984                         if (IN6_ARE_ADDR_EQUAL(&remote_host,
985                                 pinhole->remote_host))
986                         {
987                             found = 1;
988                             strncpy(packet_line,line, 1024);
989                         }
990                     }
991                     else {
992                         //traffic found
993                         found = 1;
994                         strncpy(packet_line,line, 1024);
995                     }
996
997                 }
998             }
999         }
1000
1001         fclose(log_file);
1002         regfree(&re_packet);
1003         free(cur_time_tm);
1004         free(old_time_tm);
1005
1006         if(found == 0)
1007         {
1008             //no match found for this pinhole, no traffic error
1009             regfree(&re_rule);
1010             return -1;
1011
1012         } else {
1013
1014             //traffic has been found, check if this is the good pinhole
1015             if(regexec(&re_rule,packet_line,2,pmatch,0) == 0 )
1016             {
1017                 char * rule_str;
1018                 int start = pmatch[1].rm_so;
1019                 int end = pmatch[1].rm_eo;
1020                 size_t size = end - start;
1021
1022                 rule_str = malloc (sizeof (char) * (size + 1));
1023                 if (rule_str)
1024                 {
1025                     strncpy (rule_str, &packet_line[start], size);
1026                     rule_str[size] = '\0';
1027                     trace(1,"check ip6tables rule : %i", rule);
1028                     if(rule == atoi(rule_str))
1029                     {
1030                         //the pinhole manages the packets
1031                         regfree(&re_rule);
1032                         free (rule_str);
1033                         return 1;
1034                     }
1035                     else {
1036                         //this is not managed by the good rule
1037                         regfree(&re_rule);
1038                         free (rule_str);
1039                         return 0;
1040                     }
1041                 }
1042             }
1043         }
1044     }
1045
1046     return -1;
1047 }
1048
1049
1050 #ifdef __cplusplus
1051 }
1052 #endif
1053