1 /* This is part of VDE Virtual Distributed Internet
3 * iplog: ip logging plugin for vde_switch
5 * Copyright 2010 Renzo Davoli University of Bologna - Italy
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.
35 #include <sys/types.h>
36 #include <sys/socket.h>
40 #include <netinet/in.h>
45 #include <vdecommon.h>
47 #include <vdeplugin.h>
50 static int logfilefd
=-1;
53 static struct dbgcl dl
[]= {
54 {"iplog/newip","show new ip addresses",D_LOGIP
|D_PLUS
},
56 #define D_LOGIP_NEWIP (dl)
58 /* lists of ip ranges to log */
60 struct ip4logaddr
*next
;
66 struct ip6logaddr
*next
;
71 struct ip4logaddr
*ip4loghead
;
72 struct ip6logaddr
*ip6loghead
;
74 /* packet header structure layer 2 and 3*/
77 unsigned char dest
[ETH_ALEN
];
78 unsigned char src
[ETH_ALEN
];
79 unsigned char proto
[2];
84 unsigned char version
;
85 unsigned char filler
[11];
86 unsigned char ip4src
[4];
87 unsigned char ip4dst
[4];
90 unsigned char version
;
91 unsigned char filler
[7];
92 unsigned char ip6src
[16];
93 unsigned char ip6dst
[16];
96 unsigned char priovlan
[2];
100 /* vde plugin data */
101 struct plugin vde_plugin_data
={
103 .help
="log ip/port/user assignment",
106 /* translate ipv4 ipv6 addresses into strings for logging */
107 static inline int ip42string(uint32_t *addr
, char *hostname
, unsigned int len
)
109 struct sockaddr_in ip4addr
;
110 ip4addr
.sin_family
=AF_INET
;
112 ip4addr
.sin_addr
.s_addr
= *addr
;
113 return getnameinfo((struct sockaddr
*)&ip4addr
,sizeof(ip4addr
),
114 hostname
,len
,NULL
,0,NI_NUMERICHOST
);
117 static inline int ip62string(uint32_t *addr
, char *hostname
, unsigned int len
)
119 struct sockaddr_in6 ip6addr
;
120 ip6addr
.sin6_family
=AF_INET6
;
122 ip6addr
.sin6_flowinfo
=0;
123 ip6addr
.sin6_scope_id
=0;
124 memcpy(&ip6addr
.sin6_addr
.s6_addr
,addr
,16);
125 return getnameinfo((struct sockaddr
*)&ip6addr
,sizeof(ip6addr
),
126 hostname
,len
,NULL
,0,NI_NUMERICHOST
);
129 /* hash table of recently seen ip addresses, collision lists are double linked */
130 #define IP_HASH_SIZE 1024
132 struct ip_hash_entry
{
133 struct ip_hash_entry
*next
;
134 struct ip_hash_entry
**prev
;
138 unsigned char srcmac
[ETH_ALEN
];
140 unsigned char ipaddr
[4];
143 static struct ip_hash_entry
**iph
;
145 static inline int ip_hash(int len
,unsigned char *addr
)
148 return((addr
[0]+2*addr
[1]+3*addr
[2]+5*addr
[3]) % IP_HASH_SIZE
);
150 return((addr
[0]+2*addr
[1]+3*addr
[2]+5*addr
[3]+
151 7*addr
[4]+11*addr
[5]+13*addr
[6]+17*addr
[7]+
152 19*addr
[8]+23*addr
[9]+29*addr
[10]+31*addr
[11]+
153 37*addr
[12]+41*addr
[13]+43*addr
[14]+47*addr
[15]) % IP_HASH_SIZE
);
156 /* search ip address into the hash tacle and add it if it does not exist.
157 log each new item added */
158 static void ip_find_in_hash_update(int len
, unsigned char *addr
, unsigned char *srcmac
, int vlan
, int port
)
160 struct ip_hash_entry
*e
;
161 int k
= ip_hash(len
, addr
);
163 for(e
= iph
[k
]; e
&& memcmp(e
->ipaddr
, addr
, len
) && e
->len
== len
&&
164 e
->vlan
== vlan
; e
= e
->next
)
167 e
= (struct ip_hash_entry
*) malloc(sizeof(*e
)+(len
-4));
169 printlog(LOG_WARNING
,"Failed to malloc ip_hash entry %s",strerror(errno
));
172 memcpy(e
->ipaddr
, addr
, len
);
173 if(iph
[k
] != NULL
) iph
[k
]->prev
= &(e
->next
);
183 if(e
->port
!= port
|| e
->vlan
!= vlan
|| memcmp(e
->srcmac
,srcmac
,ETH_ALEN
)!=0) {
186 memcpy(e
->srcmac
,srcmac
,ETH_ALEN
);
191 struct iovec iov
[]={{stime
+4,16},{msg
,0},{lf
,1}};
193 if ((len
==4 && ip42string((uint32_t *)addr
,hostname
,sizeof(hostname
))==0) ||
194 (len
==16 && ip62string((uint32_t *)addr
,hostname
,sizeof(hostname
))==0)) {
199 if ((pwd
=getpwuid(port_user(port
))) == NULL
)
202 username
=pwd
->pw_name
;
203 iov
[1].iov_len
=snprintf(msg
,sizeof(msg
),"ipv%d %s mac=%02x:%02x:%02x:%02x:%02x:%02x port=%d vlan=%d user=%s",
204 (len
==4)?4:6, hostname
,
205 srcmac
[0], srcmac
[1], srcmac
[2], srcmac
[3], srcmac
[4], srcmac
[5],
206 port
, vlan
, username
);
207 for (epn
=0; (descr
=port_descr(port
,epn
)) != NULL
; epn
++) {
208 int len
=iov
[1].iov_len
;
209 int descrlen
=snprintf(msg
+len
,sizeof(msg
)-len
," \"%s\"",descr
);
210 iov
[1].iov_len
+=descrlen
;
212 if (logfilefd
>= 0) {
213 time_t ntime
=time(&ntime
);
214 ctime_r(&ntime
,stime
);
215 writev(logfilefd
,iov
,3);
216 } else if (logfilefd
!= -1)
217 syslog(LOG_INFO
, "%s", msg
);
218 DBGOUT(D_LOGIP_NEWIP
,"%s",msg
);
223 /* pass through the hash table and execute function f for each element */
224 static void ip_for_all_hash(void (*f
)(struct ip_hash_entry
*, void *), void *arg
)
227 struct ip_hash_entry
*e
, *next
;
229 for(i
= 0; i
< IP_HASH_SIZE
; i
++){
230 for(e
= iph
[i
]; e
; e
= next
){
237 /* delete a hash table entry */
238 static inline void delete_hash_entry(struct ip_hash_entry
*old
)
240 *((old
)->prev
)=(old
)->next
;
241 if((old
)->next
!= NULL
) (old
)->next
->prev
= (old
)->prev
;
246 #define IP_GC_INTERVAL 10
247 #define IP_GC_EXPIRE 360
248 static int ip_gc_interval
=IP_GC_INTERVAL
;
249 static int ip_gc_expire
=IP_GC_EXPIRE
;
250 static unsigned int ip_gc_timerno
;
252 /* clean from the hash table entries older than IP_GC_EXPIRE seconds, given that
253 * 'now' points to a time_t structure describing the current time */
254 static void ip_gc(struct ip_hash_entry
*e
, void *expiretime
)
256 if(e
->last_seen
<= *((time_t *)expiretime
))
257 delete_hash_entry(e
);
260 /* clean old entries in the hash table 'h', and prepare the timer to be called
261 * again between GC_INTERVAL seconds */
262 static void ip_hash_gc(void *arg
)
264 time_t t
= qtime() - ip_gc_expire
;
265 ip_for_all_hash(ip_gc
, &t
);
268 /* upcall from vde: new incomping packet */
269 #define UINT32(X) (((uint32_t *)&(X)))
270 static int iplog_pktin(struct dbgcl
*event
,void *arg
,va_list v
)
273 int port
=va_arg(v
,int);
274 unsigned char *buf
=va_arg(v
,unsigned char *);
275 //int len=va_arg(v,int);
276 struct header
*ph
=(struct header
*) buf
;
277 union body
*pb
=(union body
*)(ph
+1);
278 //fprintf(stderr,"packet from port %d len %d\n",port,len);
279 if (ph
->proto
[0]==0x81 && ph
->proto
[1]==0x00) { /*VLAN*/
280 vlan
=((pb
->vlan
.priovlan
[0] << 8) + pb
->vlan
.priovlan
[1]) & 0xfff;
281 ph
=(struct header
*)(((char *)ph
)+4);
282 pb
=(union body
*)(((char *)pb
)+4);
284 if (ph
->proto
[0]==0x08 && ph
->proto
[1]==0x00 &&
285 pb
->v4
.version
== 0x45) {
287 struct ip4logaddr
*ip4scan
;
288 /* is the packet in one of the logged ranges? */
289 for (ip4scan
=ip4loghead
; ip4scan
!=NULL
; ip4scan
=ip4scan
->next
) {
290 /*printf("%x %x %x\n",UINT32(pb->v4.ip4src[0]) , ip4scan->mask ,
292 uint32_t *addr
=UINT32(pb
->v4
.ip4src
[0]);
293 if ((addr
[0] & ip4scan
->mask
) ==
295 ip_find_in_hash_update(4,pb
->v4
.ip4src
,ph
->src
,vlan
,port
);
300 else if (ph
->proto
[0]==0x86 && ph
->proto
[1]==0xdd &&
301 pb
->v4
.version
== 0x60) {
303 struct ip6logaddr
*ip6scan
;
304 /* is the packet in one of the logged ranges? */
305 for (ip6scan
=ip6loghead
; ip6scan
!=NULL
; ip6scan
=ip6scan
->next
) {
306 /*printf("%x %x %x:",UINT32(pb->v6.ip6src[0]) , ip6scan->mask[0] , ip6scan->addr[0]);
307 printf("%x %x %x:",UINT32(pb->v6.ip6src[4]) , ip6scan->mask[1] , ip6scan->addr[1]);
308 printf("%x %x %x:",UINT32(pb->v6.ip6src[8]) , ip6scan->mask[2] , ip6scan->addr[2]);
309 printf("%x %x %x:",UINT32(pb->v6.ip6src[12]) , ip6scan->mask[3] , ip6scan->addr[3]);
311 uint32_t *addr
=UINT32(pb
->v6
.ip6src
[0]);
313 ((addr
[0] & ip6scan
->mask
[0]) == ip6scan
->addr
[0]) &&
314 ((addr
[1] & ip6scan
->mask
[1]) == ip6scan
->addr
[1]) &&
315 ((addr
[2] & ip6scan
->mask
[2]) == ip6scan
->addr
[2]) &&
316 ((addr
[3] & ip6scan
->mask
[3]) == ip6scan
->addr
[3])
319 ip_find_in_hash_update(16,pb
->v6
.ip6src
,ph
->src
,vlan
,port
);
327 /* delete all ip address on a specific port (when the port is closed) */
328 static void port_gc(struct ip_hash_entry
*e
, void *arg
)
332 delete_hash_entry(e
);
335 /* upcall from vde: a port has been closed */
336 static int iplog_port_minus(struct dbgcl
*event
,void *arg
,va_list v
)
338 int port
=va_arg(v
,int);
339 ip_for_all_hash(&port_gc
, &port
);
343 /*user interface: showinfo */
344 static int ipshowinfo(FILE *fd
)
346 printoutc(fd
,"iplog: ip/port/user logging plugin");
349 printoutc(fd
,"log disabled");
351 printoutc(fd
,"log on syslog");
353 printoutc(fd
,"log on file %s",logfile
);
354 printoutc(fd
,"GC interval %d secs",ip_gc_interval
);
355 printoutc(fd
,"GC expire %d secs",ip_gc_expire
);
359 /* close the old log file */
360 static void closelogfile(void)
368 /* change the log file */
369 static int iplogfile(char *arg
)
372 if (strcmp(arg
,"-")==0) {
378 fd
=open(arg
,O_CREAT
|O_WRONLY
|O_APPEND
,0600);
380 char abspath
[PATH_MAX
];
383 vde_realpath(arg
,abspath
);
384 logfile
=strdup(abspath
);
396 /* add a v4 range (recursive) */
397 static int iplog4radd(struct ip4logaddr
**ph
, uint32_t addr
, uint32_t mask
)
400 *ph
=malloc(sizeof(struct ip4logaddr
));
410 if ((*ph
)->addr
==addr
&& (*ph
)->mask
==mask
)
413 return iplog4radd(&((*ph
)->next
),addr
,mask
);
417 /* add a v6 range (recursive) */
418 static int iplog6radd(struct ip6logaddr
**ph
, uint32_t addr
[4], uint32_t mask
[4])
421 *ph
=malloc(sizeof(struct ip6logaddr
));
426 memcpy((void *)((*ph
)->addr
),addr
,16);
427 memcpy((void *)((*ph
)->mask
),mask
,16);
431 if (memcmp(&((*ph
)->addr
),addr
,16) == 0 &&
432 memcmp(&((*ph
)->mask
),mask
,16) == 0)
435 return iplog6radd(&((*ph
)->next
),addr
,mask
);
439 /* delete a v4 range (recursive) */
440 static int iplog4rdel(struct ip4logaddr
**ph
, uint32_t addr
, uint32_t mask
)
445 if ((*ph
)->addr
==addr
&& (*ph
)->mask
==mask
) {
446 struct ip4logaddr
*this=*ph
;
451 return iplog4rdel(&((*ph
)->next
),addr
,mask
);
455 /* delete a v6 range (recursive) */
456 static int iplog6rdel(struct ip6logaddr
**ph
, uint32_t addr
[4], uint32_t mask
[4])
461 if (memcmp(&((*ph
)->addr
),addr
,16) == 0 &&
462 memcmp(&((*ph
)->mask
),mask
,16) == 0) {
463 struct ip6logaddr
*this=*ph
;
468 return iplog6rdel(&((*ph
)->next
),addr
,mask
);
472 /* create a mask from the number of bits */
473 static void n2mask(int len
,int n
, uint32_t *out
)
477 for (i
=0;i
<len
;i
++,n
-=8) {
481 m
[i
]=~((1<<(8-n
))-1);
485 len
=(len
+sizeof(uint32_t)-1)/sizeof(uint32_t);
487 out
[i
]=*(((uint32_t *)m
)+i
);
490 /* compute the number of bits from a mask */
491 static int mask2n(int len
, void *addr
)
496 for (i
=0;i
<len
;i
++) {
497 for (sm
=0x80;sm
!=0;sm
>>=1) {
507 /* convert an ipv4 or ipv6 address into addr/mask */
508 static int char2addr_mask(char *arg
, uint32_t *addr
, uint32_t *mask
)
511 char *smask
=strrchr(arg
,'/');
517 if (getaddrinfo(arg
,NULL
,NULL
,&ai
) != 0)
520 if (ai
->ai_family
== AF_INET
) {
521 struct sockaddr_in
*ip4addr
=(struct sockaddr_in
*) ai
->ai_addr
;
524 n2mask(len
,atoi(smask
),mask
);
527 addr
[0]=ip4addr
->sin_addr
.s_addr
& mask
[0];
528 } else if (ai
->ai_family
== AF_INET6
) {
530 struct sockaddr_in6
*ip6addr
=(struct sockaddr_in6
*) ai
->ai_addr
;
533 n2mask(len
,atoi(smask
),mask
);
535 n2mask(len
,128,mask
);
537 addr
[i
]=*(((uint32_t *)ip6addr
->sin6_addr
.s6_addr
)+i
) & mask
[i
];
545 /* user interface: add an ipv4 or ipv6 range */
546 static int iplogadd(char *arg
)
548 uint32_t addr
[4],mask
[4];
549 int len
=char2addr_mask(arg
,addr
,mask
);
551 return iplog4radd(&ip4loghead
,addr
[0],mask
[0]);
553 return iplog6radd(&ip6loghead
,addr
,mask
);
558 /* user interface: delete an ipv4 or ipv6 range */
559 static int iplogdel(char *arg
)
561 uint32_t addr
[4],mask
[4];
562 int len
=char2addr_mask(arg
,addr
,mask
);
564 return iplog4rdel(&ip4loghead
,addr
[0],mask
[0]);
566 return iplog6rdel(&ip6loghead
,addr
,mask
);
571 /* list the ipv4 ranges */
572 static void iplog4rlist(struct ip4logaddr
*ph
, FILE *fd
)
576 if (ip42string(&ph
->addr
,hostname
,sizeof(hostname
)) == 0)
577 printoutc(fd
," ipv4: %s/%d",hostname
,mask2n(4,&ph
->mask
));
578 iplog4rlist(ph
->next
,fd
);
582 /* list the ipv6 ranges */
583 static void iplog6rlist(struct ip6logaddr
*ph
, FILE *fd
)
587 if (ip62string(ph
->addr
,hostname
,sizeof(hostname
)) == 0)
588 printoutc(fd
," ipv6: %s/%d",hostname
,mask2n(16,&ph
->mask
));
589 iplog6rlist(ph
->next
,fd
);
593 /* user interfaces list the ip ranges (v4 and v6)*/
594 static int iploglist(FILE *fd
)
596 iplog4rlist(ip4loghead
,fd
);
597 iplog6rlist(ip6loghead
,fd
);
601 /* user interfaces set the garbage collection interval*/
602 int iplog_set_gc_interval(int p
)
604 qtimer_del(ip_gc_timerno
);
606 ip_gc_timerno
=qtimer_add(ip_gc_interval
,0,ip_hash_gc
,NULL
);
610 /* user interfaces set the expire interval*/
611 int iplog_set_gc_expire(int e
)
617 /* print an item of the recent ip hash table */
618 static void iplog_iplist_item(struct ip_hash_entry
*e
, void *arg
)
622 if ((e
->len
==4 && ip42string((uint32_t *)e
->ipaddr
,hostname
,sizeof(hostname
))==0) ||
623 (e
->len
==16 && ip62string((uint32_t *)e
->ipaddr
,hostname
,sizeof(hostname
))==0)) {
626 if ((pwd
=getpwuid(port_user(e
->port
))) == NULL
)
629 username
=pwd
->pw_name
;
630 printoutc(fd
,"ipv%d %s port=%d user=%s", (e
->len
==4)?4:6, hostname
, e
->port
, username
);
634 /* user interface: list all the ip addresses in the hash table */
635 static int iplog_iplist(FILE *fd
)
637 ip_for_all_hash(iplog_iplist_item
, fd
);
641 /* user interface: list the ip addresses on a specific port */
647 static void iplog_ipport_item(struct ip_hash_entry
*e
, void *arg
)
649 struct ipport_data
*pipd
=arg
;
650 if (e
->port
== pipd
->port
)
651 iplog_iplist_item(e
,pipd
->fd
);
654 static int iplog_ipport(FILE *fd
,int port
)
656 struct ipport_data ipd
={fd
, port
};
657 ip_for_all_hash(iplog_ipport_item
, &ipd
);
661 /* user interface: list the ip addresses of a specific user */
667 static void iplog_ipuser_item(struct ip_hash_entry
*e
, void *arg
)
669 struct ipuser_data
*piud
=arg
;
670 if (port_user(e
->port
) == piud
->user
)
671 iplog_iplist_item(e
,piud
->fd
);
674 static int iplog_ipuser(FILE *fd
,char *user
)
677 struct ipuser_data iud
={.fd
=fd
};
678 if (user
==NULL
|| *user
==0)
681 pwd
=getpwuid(atoi(user
));
686 iud
.user
=pwd
->pw_uid
;
687 ip_for_all_hash(iplog_ipuser_item
, &iud
);
691 /* user interface: search an ip address in the hash table */
692 static void iplog_ipsearch_item(int len
,unsigned char *addr
, FILE *fd
)
694 struct ip_hash_entry
*e
;
695 int k
= ip_hash(len
, addr
);
696 for(e
= iph
[k
]; e
&& memcmp(e
->ipaddr
, addr
, len
) && e
->len
== len
; e
= e
->next
)
699 iplog_iplist_item(e
,fd
);
702 static int iplog_ipsearch(FILE *fd
,char *addr
)
706 if (addr
==NULL
|| *addr
==0)
708 if (getaddrinfo(addr
,NULL
,NULL
,&ai
) != 0)
710 if (ai
->ai_family
== AF_INET
) {
711 struct sockaddr_in
*ip4addr
=(struct sockaddr_in
*) ai
->ai_addr
;
712 iplog_ipsearch_item(4, (unsigned char *) &ip4addr
->sin_addr
.s_addr
, fd
);
713 } else if (ai
->ai_family
== AF_INET6
) {
714 struct sockaddr_in6
*ip6addr
=(struct sockaddr_in6
*) ai
->ai_addr
;
715 iplog_ipsearch_item(16, ip6addr
->sin6_addr
.s6_addr
, fd
);
723 static struct comlist cl
[]={
724 {"iplog","============","IP/Mac/User Logging",NULL
,NOARG
},
725 {"iplog/showinfo","","Show info on logging",ipshowinfo
,NOARG
|WITHFILE
},
726 {"iplog/logfile","pathname","Set the logfile",iplogfile
,STRARG
},
727 {"iplog/ipadd","ipaddr/mask","add an ipv4/v6 range",iplogadd
,STRARG
},
728 {"iplog/ipdel","ipaddr/mask","del an ipv6/v6 range",iplogdel
,STRARG
},
729 {"iplog/list","","list ip ranges",iploglist
,NOARG
|WITHFILE
},
730 {"iplog/setgcint","N","change garbage collector interval",iplog_set_gc_interval
,INTARG
},
731 {"iplog/setexpire","N","change iplog entries expire time",iplog_set_gc_expire
,INTARG
},
732 {"iplog/iplist","","list active IP",iplog_iplist
,NOARG
|WITHFILE
},
733 {"iplog/ipport","port","list active IP on a port",iplog_ipport
,INTARG
|WITHFILE
},
734 {"iplog/ipuser","user","list active IP of a user",iplog_ipuser
,STRARG
|WITHFILE
},
735 {"iplog/ipsearch","ipaddr","search an IP address",iplog_ipsearch
,STRARG
|WITHFILE
},
738 static int iplog_hup(struct dbgcl
*event
,void *arg
,va_list v
)
740 if (logfilefd
>= 0) {
743 char *prehup
="SIGHUP: closing file";
744 char *posthup
="SIGHUP: opening file";
745 struct iovec preiov
[]={{stime
+4,16},{prehup
,strlen(prehup
)},{lf
,1}};
746 struct iovec postiov
[]={{stime
+4,16},{posthup
,strlen(posthup
)},{lf
,1}};
747 time_t ntime
=time(&ntime
);
748 ctime_r(&ntime
,stime
);
749 writev(logfilefd
,preiov
,3);
751 logfilefd
=open(logfile
,O_CREAT
|O_WRONLY
|O_APPEND
,0600);
752 writev(logfilefd
,postiov
,3);
758 __attribute__ ((constructor
))
761 iph
=calloc(IP_HASH_SIZE
,sizeof(struct ip_hash_entry
*));
764 ip_gc_timerno
=qtimer_add(ip_gc_interval
,0,ip_hash_gc
,NULL
);
765 eventadd(iplog_hup
, "sig/hup", NULL
);
766 eventadd(iplog_pktin
, "packet/in", NULL
);
767 eventadd(iplog_port_minus
, "port/-", NULL
);
771 __attribute__ ((destructor
))
776 eventdel(iplog_port_minus
, "port/-", NULL
);
777 eventdel(iplog_pktin
, "packet/in", NULL
);
778 eventdel(iplog_hup
, "sig/hup", NULL
);
779 qtimer_del(ip_gc_timerno
);
782 ip_for_all_hash(ip_gc
, &t
);