Basic support for libvirt (uml, qemu/kvm)
[vde.git] / vde-2 / src / vde_switch / plugins / iplog.c
bloba07e2bbc302a96d037c7acfd590371d27871b088
1 /* This is part of VDE Virtual Distributed Internet
3 * iplog: ip logging plugin for vde_switch
4 *
5 * Copyright 2010 Renzo Davoli University of Bologna - Italy
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, version 2, as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22 /* XXX missing:
23 search ip
26 #define _GNU_SOURCE
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <fcntl.h>
31 #include <stdint.h>
33 #include <config.h>
34 #include <vde.h>
35 #include <syslog.h>
36 #include <errno.h>
37 #include <limits.h>
38 #include <unistd.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <netdb.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <netinet/in.h>
45 #include <sys/uio.h>
46 #include <pwd.h>
47 #include <ctype.h>
49 #include <vdecommon.h>
51 #include <vdeplugin.h>
53 static char *logfile;
54 static int logfilefd=-1;
56 #define D_LOGIP 0300
57 static struct dbgcl dl[]= {
58 {"iplog/newip","show new ip addresses",D_LOGIP|D_PLUS},
60 #define D_LOGIP_NEWIP (dl)
62 /* lists of ip ranges to log */
63 struct ip4logaddr {
64 struct ip4logaddr *next;
65 uint32_t addr;
66 uint32_t mask;
69 struct ip6logaddr {
70 struct ip6logaddr *next;
71 uint32_t addr[4];
72 uint32_t mask[4];
75 struct ip4logaddr *ip4loghead;
76 struct ip6logaddr *ip6loghead;
78 /* packet header structure layer 2 and 3*/
79 #define ETH_ALEN 6
80 struct header {
81 unsigned char dest[ETH_ALEN];
82 unsigned char src[ETH_ALEN];
83 unsigned char proto[2];
86 union body {
87 struct {
88 unsigned char version;
89 unsigned char filler[11];
90 unsigned char ip4src[4];
91 unsigned char ip4dst[4];
92 } v4;
93 struct {
94 unsigned char version;
95 unsigned char filler[7];
96 unsigned char ip6src[16];
97 unsigned char ip6dst[16];
98 } v6;
99 struct {
100 unsigned char priovlan[2];
101 } vlan;
104 /* vde plugin data */
105 struct plugin vde_plugin_data={
106 .name="iplog",
107 .help="log ip/port/user assignment",
110 /* translate ipv4 ipv6 addresses into strings for logging */
111 static inline int ip42string(uint32_t *addr, char *hostname, unsigned int len)
113 struct sockaddr_in ip4addr;
114 ip4addr.sin_family=AF_INET;
115 ip4addr.sin_port=0;
116 ip4addr.sin_addr.s_addr = *addr;
117 return getnameinfo((struct sockaddr *)&ip4addr,sizeof(ip4addr),
118 hostname,len,NULL,0,NI_NUMERICHOST);
121 static inline int ip62string(uint32_t *addr, char *hostname, unsigned int len)
123 struct sockaddr_in6 ip6addr;
124 ip6addr.sin6_family=AF_INET6;
125 ip6addr.sin6_port=0;
126 ip6addr.sin6_flowinfo=0;
127 ip6addr.sin6_scope_id=0;
128 memcpy(&ip6addr.sin6_addr.s6_addr,addr,16);
129 return getnameinfo((struct sockaddr *)&ip6addr,sizeof(ip6addr),
130 hostname,len,NULL,0,NI_NUMERICHOST);
133 /* hash table of recently seen ip addresses, collision lists are double linked */
134 #define IP_HASH_SIZE 1024
136 struct ip_hash_entry {
137 struct ip_hash_entry *next;
138 struct ip_hash_entry **prev;
139 time_t last_seen;
140 int port;
141 short vlan;
142 short len;
143 unsigned char ipaddr[4];
146 static struct ip_hash_entry **iph;
148 static inline int ip_hash(int len,unsigned char *addr)
150 if (len == 4)
151 return((addr[0]+2*addr[1]+3*addr[2]+5*addr[3]) % IP_HASH_SIZE);
152 else
153 return((addr[0]+2*addr[1]+3*addr[2]+5*addr[3]+
154 7*addr[4]+11*addr[5]+13*addr[6]+17*addr[7]+
155 19*addr[8]+23*addr[9]+29*addr[10]+31*addr[11]+
156 37*addr[12]+41*addr[13]+43*addr[14]+47*addr[15]) % IP_HASH_SIZE);
159 /* search ip address into the hash tacle and add it if it does not exist.
160 log each new item added */
161 static void ip_find_in_hash_update(int len,unsigned char *addr,int vlan,int port)
163 struct ip_hash_entry *e;
164 int k = ip_hash(len, addr);
165 time_t now;
166 for(e = iph[k]; e && memcmp(e->ipaddr, addr, len) && e->len == len &&
167 e->vlan == vlan; e = e->next)
169 if(e == NULL) {
170 e = (struct ip_hash_entry *) malloc(sizeof(*e)+(len-4));
171 if(e == NULL){
172 printlog(LOG_WARNING,"Failed to malloc ip_hash entry %s",strerror(errno));
173 return;
175 memcpy(e->ipaddr, addr, len);
176 if(iph[k] != NULL) iph[k]->prev = &(e->next);
177 e->next = iph[k];
178 e->prev = &(iph[k]);
179 e->vlan = vlan;
180 e->len = len;
181 e->port = -1;
182 iph[k] = e;
184 now=qtime();
185 e->last_seen = now;
186 if(e->port != port || e->vlan != vlan) {
187 e->port=port;
188 e->vlan = vlan;
189 char hostname[100];
190 char msg[256];
191 char lf[]="\n";
192 char stime[26];
193 struct iovec iov[]={{stime+4,16},{msg,0},{lf,1}};
195 if ((len==4 && ip42string((uint32_t *)addr,hostname,sizeof(hostname))==0) ||
196 (len==16 && ip62string((uint32_t *)addr,hostname,sizeof(hostname))==0)) {
197 struct passwd *pwd;
198 char *username;
199 if ((pwd=getpwuid(port_user(port))) == NULL)
200 username="(none)";
201 else
202 username=pwd->pw_name;
203 iov[1].iov_len=snprintf(msg,sizeof(msg),"ipv%d %s port=%d vlan=%d user=%s",
204 (len==4)?4:6, hostname, port, vlan, username);
205 if (logfilefd >= 0) {
206 time_t ntime=time(&ntime);
207 ctime_r(&ntime,stime);
208 writev(logfilefd,iov,3);
209 } else if (logfilefd != -1)
210 syslog(LOG_INFO, msg);
211 DBGOUT(D_LOGIP_NEWIP,"%s",msg);
216 /* pass through the hash table and execute function f for each element */
217 static void ip_for_all_hash(void (*f)(struct ip_hash_entry *, void *), void *arg)
219 int i;
220 struct ip_hash_entry *e, *next;
222 for(i = 0; i < IP_HASH_SIZE; i++){
223 for(e = iph[i]; e; e = next){
224 next = e->next;
225 (*f)(e, arg);
230 /* delete a hash table entry */
231 static inline void delete_hash_entry(struct ip_hash_entry *old)
233 *((old)->prev)=(old)->next;
234 if((old)->next != NULL) (old)->next->prev = (old)->prev;
235 free((old));
239 #define IP_GC_INTERVAL 10
240 #define IP_GC_EXPIRE 360
241 static int ip_gc_interval=IP_GC_INTERVAL;
242 static int ip_gc_expire=IP_GC_EXPIRE;
243 static unsigned int ip_gc_timerno;
245 /* clean from the hash table entries older than IP_GC_EXPIRE seconds, given that
246 * 'now' points to a time_t structure describing the current time */
247 static void ip_gc(struct ip_hash_entry *e, void *expiretime)
249 if(e->last_seen <= *((time_t *)expiretime))
250 delete_hash_entry(e);
253 /* clean old entries in the hash table 'h', and prepare the timer to be called
254 * again between GC_INTERVAL seconds */
255 static void ip_hash_gc(void *arg)
257 time_t t = qtime() - ip_gc_expire;
258 ip_for_all_hash(ip_gc, &t);
261 /* upcall from vde: new incomping packet */
262 #define UINT32(X) (((uint32_t *)&(X)))
263 static int iplog_pktin(struct dbgcl *event,void *arg,va_list v)
265 int vlan=0;
266 int port=va_arg(v,int);
267 unsigned char *buf=va_arg(v,unsigned char *);
268 //int len=va_arg(v,int);
269 struct header *ph=(struct header *) buf;
270 union body *pb=(union body *)(ph+1);
271 //fprintf(stderr,"packet from port %d len %d\n",port,len);
272 if (ph->proto[0]==0x81 && ph->proto[1]==0x00) { /*VLAN*/
273 vlan=((pb->vlan.priovlan[0] << 8) + pb->vlan.priovlan[1]) & 0xfff;
274 ph=(struct header *)(((char *)ph)+4);
275 pb=(union body *)(((char *)pb)+4);
277 if (ph->proto[0]==0x08 && ph->proto[1]==0x00 &&
278 pb->v4.version == 0x45) {
279 /*v4 */
280 struct ip4logaddr *ip4scan;
281 /* is the packet in one of the logged ranges? */
282 for (ip4scan=ip4loghead; ip4scan!=NULL; ip4scan=ip4scan->next) {
283 /*printf("%x %x %x\n",UINT32(pb->v4.ip4src[0]) , ip4scan->mask ,
284 ip4scan->addr);*/
285 uint32_t *addr=UINT32(pb->v4.ip4src[0]);
286 if ((addr[0] & ip4scan->mask) ==
287 ip4scan->addr) {
288 ip_find_in_hash_update(4,pb->v4.ip4src,vlan,port);
289 break;
293 else if (ph->proto[0]==0x86 && ph->proto[1]==0xdd &&
294 pb->v4.version == 0x60) {
295 /*v6 */
296 struct ip6logaddr *ip6scan;
297 /* is the packet in one of the logged ranges? */
298 for (ip6scan=ip6loghead; ip6scan!=NULL; ip6scan=ip6scan->next) {
299 /*printf("%x %x %x:",UINT32(pb->v6.ip6src[0]) , ip6scan->mask[0] , ip6scan->addr[0]);
300 printf("%x %x %x:",UINT32(pb->v6.ip6src[4]) , ip6scan->mask[1] , ip6scan->addr[1]);
301 printf("%x %x %x:",UINT32(pb->v6.ip6src[8]) , ip6scan->mask[2] , ip6scan->addr[2]);
302 printf("%x %x %x:",UINT32(pb->v6.ip6src[12]) , ip6scan->mask[3] , ip6scan->addr[3]);
303 printf("\n");*/
304 uint32_t *addr=UINT32(pb->v6.ip6src[0]);
305 if (
306 ((addr[0] & ip6scan->mask[0]) == ip6scan->addr[0]) &&
307 ((addr[1] & ip6scan->mask[1]) == ip6scan->addr[1]) &&
308 ((addr[2] & ip6scan->mask[2]) == ip6scan->addr[2]) &&
309 ((addr[3] & ip6scan->mask[3]) == ip6scan->addr[3])
312 ip_find_in_hash_update(16,pb->v6.ip6src,vlan,port);
313 break;
317 return 0;
320 /* delete all ip address on a specific port (when the port is closed) */
321 static void port_gc(struct ip_hash_entry *e, void *arg)
323 int *port=arg;
324 if(*port == e->port)
325 delete_hash_entry(e);
328 /* upcall from vde: a port has been closed */
329 static int iplog_port_minus(struct dbgcl *event,void *arg,va_list v)
331 int port=va_arg(v,int);
332 ip_for_all_hash(&port_gc, &port);
333 return 0;
336 /*user interface: showinfo */
337 static int ipshowinfo(FILE *fd)
339 printoutc(fd,"iplog: ip/port/user loggin plugin");
340 if (logfilefd<0) {
341 if (logfilefd == -1)
342 printoutc(fd,"log disabled");
343 else
344 printoutc(fd,"log on syslog");
345 } else
346 printoutc(fd,"log on file %s",logfile);
347 printoutc(fd,"GC interval %d secs",ip_gc_interval);
348 printoutc(fd,"GC expire %d secs",ip_gc_expire);
349 return 0;
352 /* close the old log file */
353 static void closelogfile(void)
355 if (logfilefd >= 0)
356 close(logfilefd);
357 if (logfile != NULL)
358 free(logfile);
361 /* change the log file */
362 static int iplogfile(char *arg)
364 if (*arg) {
365 if (strcmp(arg,"-")==0) {
366 closelogfile();
367 logfilefd=-2;
368 return 0;
369 } else {
370 int fd;
371 fd=open(arg,O_CREAT|O_WRONLY|O_APPEND,0600);
372 if (fd>=0) {
373 char abspath[PATH_MAX];
374 closelogfile();
375 logfilefd=fd;
376 vde_realpath(arg,abspath);
377 logfile=strdup(abspath);
378 return 0;
379 } else
380 return ENOENT;
382 } else
383 return EINVAL;
386 /* add a v4 range (recursive) */
387 static int iplog4radd(struct ip4logaddr **ph, uint32_t addr, uint32_t mask)
389 if (*ph == NULL) {
390 *ph=malloc(sizeof(struct ip4logaddr));
391 if (*ph==NULL)
392 return ENOMEM;
393 else {
394 (*ph)->next=NULL;
395 (*ph)->addr=addr;
396 (*ph)->mask=mask;
397 return 0;
399 } else {
400 if ((*ph)->addr==addr && (*ph)->mask==mask)
401 return EEXIST;
402 else
403 return iplog4radd(&((*ph)->next),addr,mask);
407 /* add a v6 range (recursive) */
408 static int iplog6radd(struct ip6logaddr **ph, uint32_t addr[4], uint32_t mask[4])
410 if (*ph == NULL) {
411 *ph=malloc(sizeof(struct ip6logaddr));
412 if (*ph==NULL)
413 return ENOMEM;
414 else {
415 (*ph)->next=NULL;
416 memcpy((void *)((*ph)->addr),addr,16);
417 memcpy((void *)((*ph)->mask),mask,16);
418 return 0;
420 } else {
421 if (memcmp(&((*ph)->addr),addr,16) == 0 &&
422 memcmp(&((*ph)->mask),mask,16) == 0)
423 return EEXIST;
424 else
425 return iplog6radd(&((*ph)->next),addr,mask);
429 /* delete a v4 range (recursive) */
430 static int iplog4rdel(struct ip4logaddr **ph, uint32_t addr, uint32_t mask)
432 if (*ph == NULL) {
433 return ENOENT;
434 } else {
435 if ((*ph)->addr==addr && (*ph)->mask==mask) {
436 struct ip4logaddr *this=*ph;
437 *ph=(*ph)->next;
438 free(this);
439 return 0;
440 } else
441 return iplog4rdel(&((*ph)->next),addr,mask);
445 /* delete a v6 range (recursive) */
446 static int iplog6rdel(struct ip6logaddr **ph, uint32_t addr[4], uint32_t mask[4])
448 if (*ph == NULL) {
449 return ENOENT;
450 } else {
451 if (memcmp(&((*ph)->addr),addr,16) == 0 &&
452 memcmp(&((*ph)->mask),mask,16) == 0) {
453 struct ip6logaddr *this=*ph;
454 *ph=(*ph)->next;
455 free(this);
456 return 0;
457 } else
458 return iplog6rdel(&((*ph)->next),addr,mask);
462 /* create a mask from the number of bits */
463 static void n2mask(int len,int n, uint32_t *out)
465 char m[len];
466 int i;
467 for (i=0;i<len;i++,n-=8) {
468 if (n>=8)
469 m[i]=0xff;
470 else if (n>0)
471 m[i]=~((1<<(8-n))-1);
472 else
473 m[i]=0;
475 len=(len+sizeof(uint32_t)-1)/sizeof(uint32_t);
476 for (i=0;i<len;i++)
477 out[i]=*(((uint32_t *)m)+i);
480 /* compute the number of bits from a mask */
481 static int mask2n(int len, void *addr)
483 char *m=addr;
484 int n=0;
485 int i,sm;
486 for (i=0;i<len;i++) {
487 for (sm=0x80;sm!=0;sm>>=1) {
488 if (m[i] & sm)
489 n++;
490 else
491 return n;
494 return n;
497 /* convert an ipv4 or ipv6 address into addr/mask */
498 static int char2addr_mask(char *arg, uint32_t *addr, uint32_t *mask)
500 struct addrinfo *ai;
501 char *smask=strrchr(arg,'/');
502 int len;
503 if (smask != NULL) {
504 *smask=0;
505 smask++;
507 if (getaddrinfo(arg,NULL,NULL,&ai) != 0)
508 return -1;
509 else {
510 if (ai->ai_family == AF_INET) {
511 struct sockaddr_in *ip4addr=(struct sockaddr_in *) ai->ai_addr;
512 len=4;
513 if (smask != NULL)
514 n2mask(len,atoi(smask),mask);
515 else
516 n2mask(len,32,mask);
517 addr[0]=ip4addr->sin_addr.s_addr & mask[0];
518 } else if (ai->ai_family == AF_INET6) {
519 int i;
520 struct sockaddr_in6 *ip6addr=(struct sockaddr_in6 *) ai->ai_addr;
521 len=16;
522 if (smask != NULL)
523 n2mask(len,atoi(smask),mask);
524 else
525 n2mask(len,128,mask);
526 for (i=0;i<4;i++)
527 addr[i]=*(((uint32_t *)ip6addr->sin6_addr.s6_addr)+i) & mask[i];
528 } else
529 len=-1;
530 freeaddrinfo(ai);
531 return len;
535 /* user interface: add an ipv4 or ipv6 range */
536 static int iplogadd(char *arg)
538 uint32_t addr[4],mask[4];
539 int len=char2addr_mask(arg,addr,mask);
540 if (len == 4)
541 return iplog4radd(&ip4loghead,addr[0],mask[0]);
542 else if (len == 16)
543 return iplog6radd(&ip6loghead,addr,mask);
544 else
545 return EINVAL;
548 /* user interface: delete an ipv4 or ipv6 range */
549 static int iplogdel(char *arg)
551 uint32_t addr[4],mask[4];
552 int len=char2addr_mask(arg,addr,mask);
553 if (len == 4)
554 return iplog4rdel(&ip4loghead,addr[0],mask[0]);
555 else if (len == 16)
556 return iplog6rdel(&ip6loghead,addr,mask);
557 else
558 return EINVAL;
561 /* list the ipv4 ranges */
562 static void iplog4rlist(struct ip4logaddr *ph, FILE *fd)
564 if (ph != NULL) {
565 char hostname[20];
566 if (ip42string(&ph->addr,hostname,sizeof(hostname)) == 0)
567 printoutc(fd," ipv4: %s/%d",hostname,mask2n(4,&ph->mask));
568 iplog4rlist(ph->next,fd);
572 /* list the ipv6 ranges */
573 static void iplog6rlist(struct ip6logaddr *ph, FILE *fd)
575 if (ph != NULL) {
576 char hostname[100];
577 if (ip62string(ph->addr,hostname,sizeof(hostname)) == 0)
578 printoutc(fd," ipv6: %s/%d",hostname,mask2n(16,&ph->mask));
579 iplog6rlist(ph->next,fd);
583 /* user interfaces list the ip ranges (v4 and v6)*/
584 static int iploglist(FILE *fd)
586 iplog4rlist(ip4loghead,fd);
587 iplog6rlist(ip6loghead,fd);
588 return 0;
591 /* user interfaces set the garbage collection interval*/
592 int iplog_set_gc_interval(int p)
594 qtimer_del(ip_gc_timerno);
595 ip_gc_interval=p;
596 ip_gc_timerno=qtimer_add(ip_gc_interval,0,ip_hash_gc,NULL);
597 return 0;
600 /* user interfaces set the expire interval*/
601 int iplog_set_gc_expire(int e)
603 ip_gc_expire=e;
604 return 0;
607 /* print an item of the recent ip hash table */
608 static void iplog_iplist_item(struct ip_hash_entry *e, void *arg)
610 FILE *fd=arg;
611 char hostname[100];
612 if ((e->len==4 && ip42string((uint32_t *)e->ipaddr,hostname,sizeof(hostname))==0) ||
613 (e->len==16 && ip62string((uint32_t *)e->ipaddr,hostname,sizeof(hostname))==0)) {
614 struct passwd *pwd;
615 char *username;
616 if ((pwd=getpwuid(port_user(e->port))) == NULL)
617 username="(none)";
618 else
619 username=pwd->pw_name;
620 printoutc(fd,"ipv%d %s port=%d user=%s", (e->len==4)?4:6, hostname, e->port, username);
624 /* user interface: list all the ip addresses in the hash table */
625 static int iplog_iplist(FILE *fd)
627 ip_for_all_hash(iplog_iplist_item, fd);
628 return 0;
631 /* user interface: list the ip addresses on a specific port */
632 struct ipport_data {
633 FILE *fd;
634 int port;
637 static void iplog_ipport_item(struct ip_hash_entry *e, void *arg)
639 struct ipport_data *pipd=arg;
640 if (e->port == pipd->port)
641 iplog_iplist_item(e,pipd->fd);
644 static int iplog_ipport(FILE *fd,int port)
646 struct ipport_data ipd={fd, port};
647 ip_for_all_hash(iplog_ipport_item, &ipd);
648 return 0;
651 /* user interface: list the ip addresses of a specific user */
652 struct ipuser_data {
653 FILE *fd;
654 uid_t user;
657 static void iplog_ipuser_item(struct ip_hash_entry *e, void *arg)
659 struct ipuser_data *piud=arg;
660 if (port_user(e->port) == piud->user)
661 iplog_iplist_item(e,piud->fd);
664 static int iplog_ipuser(FILE *fd,char *user)
666 struct passwd *pwd;
667 struct ipuser_data iud={.fd=fd};
668 if (user==NULL || *user==0)
669 return EINVAL;
670 if (isdigit(*user))
671 pwd=getpwuid(atoi(user));
672 else
673 pwd=getpwnam(user);
674 if (pwd == NULL)
675 return EINVAL;
676 iud.user=pwd->pw_uid;
677 ip_for_all_hash(iplog_ipuser_item, &iud);
678 return 0;
681 /* user interface: search an ip address in the hash table */
682 static void iplog_ipsearch_item(int len,unsigned char *addr, FILE *fd)
684 struct ip_hash_entry *e;
685 int k = ip_hash(len, addr);
686 for(e = iph[k]; e && memcmp(e->ipaddr, addr, len) && e->len == len; e = e->next)
688 if(e != NULL)
689 iplog_iplist_item(e,fd);
692 static int iplog_ipsearch(FILE *fd,char *addr)
694 struct addrinfo *ai;
695 int rv=0;
696 if (addr==NULL || *addr==0)
697 return EINVAL;
698 if (getaddrinfo(addr,NULL,NULL,&ai) != 0)
699 return EINVAL;
700 if (ai->ai_family == AF_INET) {
701 struct sockaddr_in *ip4addr=(struct sockaddr_in *) ai->ai_addr;
702 iplog_ipsearch_item(4, (unsigned char *) &ip4addr->sin_addr.s_addr, fd);
703 } else if (ai->ai_family == AF_INET6) {
704 struct sockaddr_in6 *ip6addr=(struct sockaddr_in6 *) ai->ai_addr;
705 iplog_ipsearch_item(16, ip6addr->sin6_addr.s6_addr , fd);
706 } else
707 rv=EINVAL;
708 freeaddrinfo(ai);
709 return rv;
712 /* command list */
713 static struct comlist cl[]={
714 {"iplog","============","IP/Mac/User Logging",NULL,NOARG},
715 {"iplog/showinfo","","Show info on logging",ipshowinfo,NOARG|WITHFILE},
716 {"iplog/logfile","pathname","Set the logfile",iplogfile,STRARG},
717 {"iplog/ipadd","ipaddr/mask","add an ipv4/v6 range",iplogadd,STRARG},
718 {"iplog/ipdel","ipaddr/mask","del an ipv6/v6 range",iplogdel,STRARG},
719 {"iplog/list","","list ip ranges",iploglist,NOARG|WITHFILE},
720 {"iplog/setgcint","N","change garbage collector interval",iplog_set_gc_interval,INTARG},
721 {"iplog/setexpire","N","change iplog entries expire time",iplog_set_gc_expire,INTARG},
722 {"iplog/iplist","","list active IP",iplog_iplist,NOARG|WITHFILE},
723 {"iplog/ipport","port","list active IP on a port",iplog_ipport,INTARG|WITHFILE},
724 {"iplog/ipuser","user","list active IP of a user",iplog_ipuser,STRARG|WITHFILE},
725 {"iplog/ipsearch","ipaddr","search an IP address",iplog_ipsearch,STRARG|WITHFILE},
728 static void
729 __attribute__ ((constructor))
730 init (void)
732 iph=calloc(IP_HASH_SIZE,sizeof(struct ip_hash_entry *));
733 ADDCL(cl);
734 ADDDBGCL(dl);
735 ip_gc_timerno=qtimer_add(ip_gc_interval,0,ip_hash_gc,NULL);
736 eventadd(iplog_pktin, "packet/in", NULL);
737 eventadd(iplog_port_minus, "port/-", NULL);
738 /* XXX add event port/minux */
741 static void
742 __attribute__ ((destructor))
743 fini (void)
745 time_t t = qtime();
746 eventdel(iplog_port_minus, "port/-", NULL);
747 eventdel(iplog_pktin, "packet/in", NULL);
748 qtimer_del(ip_gc_timerno);
749 DELCL(cl);
750 DELDBGCL(dl);
751 ip_for_all_hash(ip_gc, &t);
752 free(iph);