Bläddra i källkod

modules:sipcapture: fixed mirroring mode, added BPF functionality.

Alexandr Dubovikov 14 år sedan
förälder
incheckning
81cd64fe78

+ 23 - 6
modules/sipcapture/README

@@ -39,7 +39,8 @@ Alexandr Dubovikov
               3.10. raw_interface (string)
               3.11. raw_sock_children (integer)
               3.12. promiscuous_on (integer)
-              3.13. capture_node (str)
+              3.13. raw_moni_bpf_on (integer)
+              3.14. capture_node (str)
 
         4. Exported MI Functions
 
@@ -62,7 +63,8 @@ Alexandr Dubovikov
    1.10. Set raw_socket_listen parameter
    1.11. Set raw_socket_listen parameter
    1.12. Set hep_capture_on parameter
-   1.13. Set capture_node parameter
+   1.13. Set raw_moni_bpf_on parameter
+   1.14. Set capture_node parameter
 
 Chapter 1. Admin Guide
 
@@ -88,7 +90,8 @@ Chapter 1. Admin Guide
         3.10. raw_interface (string)
         3.11. raw_sock_children (integer)
         3.12. promiscuous_on (integer)
-        3.13. capture_node (str)
+        3.13. raw_moni_bpf_on (integer)
+        3.14. capture_node (str)
 
    4. Exported MI Functions
 
@@ -143,7 +146,8 @@ Chapter 1. Admin Guide
    3.10. raw_interface (string)
    3.11. raw_sock_children (integer)
    3.12. promiscuous_on (integer)
-   3.13. capture_node (str)
+   3.13. raw_moni_bpf_on (integer)
+   3.14. capture_node (str)
 
 3.1. db_url (str)
 
@@ -286,13 +290,26 @@ modparam("sipcapture", "raw_sock_children", 6)
 modparam("sipcapture", "promiscuous_on", 1)
 ...
 
-3.13. capture_node (str)
+3.13. raw_moni_bpf_on (integer)
+
+   Activate BPF on the mirroring interface. The structure is defined in
+   sipcapture.h. The default BPF accept a portrange from the
+   raw_socket_listen param.
+
+   Default value is "0".
+
+   Example 1.13. Set raw_moni_bpf_on parameter
+...
+modparam("sipcapture", "raw_moni_bpf_on", 1)
+...
+
+3.14. capture_node (str)
 
    Name of the capture node.
 
    Default value is "homer01".
 
-   Example 1.13. Set capture_node parameter
+   Example 1.14. Set capture_node parameter
 ...
 modparam("sipcapture", "capture_node", "homer03")
 ...

+ 21 - 0
modules/sipcapture/doc/sipcapture_admin.xml

@@ -315,6 +315,27 @@ modparam("sipcapture", "promiscuous_on", 1)
 </programlisting>
                 </example>
         </section>
+        <section>
+                <title><varname>raw_moni_bpf_on</varname> (integer)</title>
+                <para>
+                Activate BPF on the mirroring interface. The structure is
+                defined in sipcapture.h. The default BPF accept a portrange
+                from the raw_socket_listen param.
+                </para>
+                <para>
+                <emphasis>
+                        Default value is "0".
+                </emphasis>
+                </para>
+                <example>
+                <title>Set <varname>raw_moni_bpf_on</varname> parameter</title>
+                <programlisting format="linespecific">
+...
+modparam("sipcapture", "raw_moni_bpf_on", 1)
+...
+</programlisting>
+                </example>
+        </section>        
 	<section>
 		<title><varname>capture_node</varname> (str)</title>
 		<para>

+ 6 - 4
modules/sipcapture/examples/kamailio.cfg

@@ -49,6 +49,8 @@ modparam("sipcapture", "raw_sock_children", 4)
 #modparam("sipcapture", "db_insert_mode", 1)
 /* Promiscious mode RAW socket. Mirroring port. Linux only */
 #modparam("sipcapture", "promiscious_on", 1)
+/* activate BPF on mirroring interface */
+#modparam("sipcapture", "raw_moni_bpf_on", 1)
 
 
 # Main SIP request routing logic
@@ -56,9 +58,9 @@ modparam("sipcapture", "raw_sock_children", 4)
 route {
 
 	#For example, you can capture only needed methods...
-	if (method =~ "^(INVITE|UPDATE|NOTIFY|SUBSCRIBE|OPTIONS|REGISTER)") {
+	#if (method =~ "^(INVITE|UPDATE|NOTIFY|SUBSCRIBE|OPTIONS|REGISTER|BYE)") {
 		sip_capture();
-	}
+	#}
 	exit;
 }
 
@@ -66,8 +68,8 @@ onreply_route {
 
 	#And replies of request methods
 	#if(status =~ "^(1[0-9][0-9]|[3[0-9][0-9]|4[0-9]|[56][0-9][0-9])") {
-	if($rm =~ "^(INVITE|UPDATE|NOTIFY|SUBSCRIBE|OPTIONS|REGISTER)") {
+	#if($rm =~ "^(INVITE|UPDATE|NOTIFY|SUBSCRIBE|OPTIONS|REGISTER|BYE)") {
 		sip_capture();
-	}	
+	#}	
 	exit;
 }

+ 206 - 49
modules/sipcapture/sipcapture.c

@@ -40,6 +40,13 @@
 #include <net/if.h> 
 #include <netdb.h>
 
+#ifndef __USE_BSD
+#define __USE_BSD  /* on linux use bsd version of iphdr (more portable) */
+#endif /* __USE_BSD */
+#include <netinet/ip.h>
+#define __FAVOR_BSD /* on linux use bsd version of udphdr (more portable) */
+#include <netinet/udp.h>
+
 #include "../../sr_module.h"
 #include "../../dprint.h"
 #include "../../events.h"
@@ -62,13 +69,9 @@
 #include "../../raw_sock.h"
 #include "../../raw_listener.h"
 #include "../../resolve.h"
+#include "../../receive.h"
 #include "sipcapture.h"
 
-#ifdef __OS_linux
-	#include <sys/ioctl.h>       
-	#include <net/if.h>
-#endif
-
 #ifdef STATISTICS
 #include "../../lib/kcore/statistics.h"
 #endif
@@ -130,6 +133,9 @@ static int sip_capture(struct sip_msg *msg, char *s1, char *s2);
 int hep_msg_received(void *data);
 int init_rawsock_children(void);
 int extract_host_port(void);
+int raw_moni_socket(str* iface, int port_start, int port_end);
+int raw_moni_rcv_loop(int rsock, int port1, int port2);
+
 
 
 static struct mi_root* sip_capture_mi(struct mi_root* cmd, void* param );
@@ -189,6 +195,7 @@ int *capture_on_flag = NULL;
 int db_insert_mode = 0;
 int partitioning_mode = 1;
 int promisc_on = 0;
+int bpf_on = 0;
 
 str raw_socket_listen = { 0, 0 };
 str raw_interface = { 0, 0 };
@@ -263,6 +270,7 @@ static param_export_t params[] = {
         {"partitioning_mode",  		INT_PARAM, &partitioning_mode  },
 	{"raw_interface",     		STR_PARAM, &raw_interface.s   },
         {"promiscious_on",  		INT_PARAM, &promisc_on   },		
+        {"raw_moni_bpf_on",  		INT_PARAM, &bpf_on   },		
 	{0, 0, 0}
 };
 
@@ -428,12 +436,13 @@ static int mod_init(void) {
 		{		
 			LM_ERR("sipcapture mod_init: bad RAW IP: %.*s\n", raw_socket_listen.len, raw_socket_listen.s); 
 			return -1;
-		}			
-
-		
-		raw_sock_desc = raw_socket(ipip_capture_on ? IPPROTO_IPIP : IPPROTO_UDP , 
-				raw_socket_listen.len ? ip : 0, raw_interface.len ? &raw_interface : 0, 0);
-		
+		}		
+			
+		raw_sock_desc = ipip_capture_on ? raw_socket( IPPROTO_IPIP, raw_socket_listen.len ? ip : 0, 
+						raw_interface.len ? &raw_interface : 0, 0) : 
+						 raw_moni_socket(raw_interface.len ? &raw_interface : 0, 
+						         moni_port_start, moni_port_end ? moni_port_end : moni_port_start);				
+						
 		if(raw_sock_desc < 0) {
 			LM_ERR("could not initialize raw udp socket:"
                                          " %s (%d)\n", strerror(errno), errno);
@@ -451,6 +460,8 @@ static int mod_init(void) {
 
 			 memset(&ifr, 0, sizeof(ifr));
 			 memcpy(ifr.ifr_name, raw_interface.s, raw_interface.len);
+
+
 #ifdef __OS_linux			 			 
 			 if(ioctl(raw_sock_desc, SIOCGIFFLAGS, &ifr) < 0) {
 				LM_ERR("could not get flags from interface [%.*s]:"
@@ -535,8 +546,10 @@ int init_rawsock_children(void)
                         ERR("Unable to fork: %s\n", strerror(errno));
                         return -1;
                 } else if (pid == 0) { /* child */
-			raw_udp4_rcv_loop(raw_sock_desc, moni_capture_on ? moni_port_start : 0, 
-					moni_capture_on ? moni_port_end : 0);
+			if(moni_capture_on) 
+				raw_moni_rcv_loop(raw_sock_desc, moni_port_start, moni_port_end);
+			else 
+				raw_udp4_rcv_loop(raw_sock_desc, 0, 0); 
                 }
                 /* Parent */
         }
@@ -731,6 +744,7 @@ static int sip_capture_store(struct _sipcapture_object *sco)
 	char tmptable[TABLE_LEN];
         int ret = 0;
         struct tm *t;
+        str dbtable;
 	               	
 	if(sco==NULL)
 	{
@@ -755,7 +769,7 @@ static int sip_capture_store(struct _sipcapture_object *sco)
 	db_keys[2] = &micro_ts_column;			
         db_vals[2].type = DB1_BIGINT;
         db_vals[2].nul = 0;
-        db_vals[2].val.ll_val = (unsigned long long)(tvb.tv_sec*1000000+tvb.tv_usec); /* micro ts */
+        db_vals[2].val.ll_val = (unsigned long long)tvb.tv_sec*1000000+tvb.tv_usec; /* micro ts */
 	
 	db_keys[3] = &method_column;
 	db_vals[3].type = DB1_STR;
@@ -927,6 +941,7 @@ static int sip_capture_store(struct _sipcapture_object *sco)
 	db_vals[36].nul = 0;
 	db_vals[36].val.blob_val = sco->msg;	
 		
+	DBG("TABLE =====================: [%.*s]\n", table_name.len, table_name.s);		
 	if(!partitioning_mode) {
                 ret = snprintf(tmptable, TABLE_LEN, "%.*s_%02d_%02d",
                         table_name.len, table_name.s, (t->tm_wday == 0) ? 7 : t->tm_wday , t->tm_hour);
@@ -935,14 +950,17 @@ static int sip_capture_store(struct _sipcapture_object *sco)
                         goto error;
                 }
 
-                table_name.s = tmptable;
-                table_name.len = strlen(tmptable);
+                dbtable.s = tmptable;
+                dbtable.len = strlen(tmptable);
         }
-
-	db_funcs.use_table(db_con, &table_name);
+        else { 
+           dbtable =  table_name;
+        }
+        
+	db_funcs.use_table(db_con, &dbtable);
 
 	LM_DBG("storing info...\n");
-
+	
 	if(db_insert_mode==1 && db_funcs.insert_delayed!=NULL) {
                 if (db_funcs.insert_delayed(db_con, db_keys, db_vals, NR_KEYS) < 0) {
                 	LM_ERR("failed to insert delayed into database\n");
@@ -1053,38 +1071,29 @@ static int sip_capture(struct sip_msg *msg, char *s1, char *s2)
         	EMPTY_STR(sco.to_tag);
         }
 	
+	/* Call-id */
+	if(msg->callid) sco.callid = msg->callid->body;
+	else { EMPTY_STR(sco.callid); }
+	
 	/* P-Asserted-Id */
-	if(msg->pai) {
-
-	     if(parse_pai_header(msg)==-1)
-             {
-		LM_DBG("no P-Asserted-Identity header\n");
-		return -1;
-	     }
+	if(msg->pai && (parse_pai_header(msg) == 0)) {
 
 	     if (parse_uri(get_pai(msg)->uri.s, get_pai(msg)->uri.len, &pai)<0){
-             	LOG(L_ERR, "ERROR: do_action: bad pai dropping"" packet\n");
-                return -1;
+             	LM_DBG("DEBUG: do_action: bad pai: method:[%.*s] CID: [%.*s]\n", sco.method.len, sco.method.s, sco.callid.len, sco.callid.s);
+             }
+             else {
+	        LM_DBG("PARSE PAI: (%.*s)\n",get_pai(msg)->uri.len, get_pai(msg)->uri.s);
+	        sco.pid_user = pai.user;                          
              }
-             
-             LM_DBG("PARSE PAI: (%.*s)\n",get_pai(msg)->uri.len, get_pai(msg)->uri.s);
-
-             sco.pid_user = pai.user;                          
 	}	
-	else if(msg->ppi) {
-
-	     if(parse_ppi_header(msg)==-1)
-             {
-		LM_DBG("no P-Preferred-Identity header\n");
-		return -1;
-	     }
+	else if(msg->ppi && (parse_ppi_header(msg) == 0)) {
 		
 	     if (parse_uri(get_ppi(msg)->uri.s, get_ppi(msg)->uri.len, &pai)<0){
-             	LOG(L_ERR, "ERROR: do_action: bad ppi dropping"" packet\n");
-                return -1;
+             	LM_DBG("DEBUG: do_action: bad ppi: method:[%.*s] CID: [%.*s]\n", sco.method.len, sco.method.s, sco.callid.len, sco.callid.s);
+             }
+             else {
+	        sco.pid_user = pai.user;
              }
-             
-             sco.pid_user = pai.user;
         }
         else { EMPTY_STR(sco.pid_user); }
 	
@@ -1115,11 +1124,6 @@ static int sip_capture(struct sip_msg *msg, char *s1, char *s2)
               }
         }
 
-	/* Call-id */
-	if(msg->callid) sco.callid = msg->callid->body;
-	else { EMPTY_STR(sco.callid); }
-	
-
 	/* get header x-cid: */
 	/* callid_aleg X-CID */
 	if((tmphdr[0] = get_hdr_by_name(msg,"X-CID", 5)) != NULL) {
@@ -1167,7 +1171,6 @@ static int sip_capture(struct sip_msg *msg, char *s1, char *s2)
 	}
 	
 	/* X-OIP */	
-	//if(msg->xoip) {
 	if((tmphdr[2] = get_hdr_by_name(msg,"X-OIP", 5)) != NULL) {
 		sco.originator_ip = tmphdr[2]->body;
 		/* Originator port. Should be parsed from XOIP header as ":" param */
@@ -1293,3 +1296,157 @@ static struct mi_root* sip_capture_mi(struct mi_root* cmd_tree, void* param )
 	}
 }
 
+int raw_moni_socket(str* iface, int port_start, int port_end)
+{
+	int sock;
+	
+#if defined (SO_BINDTODEVICE)
+	char short_ifname[sizeof(int)];
+	int ifname_len;
+	char* ifname;
+#endif /* SO_BINDTODEVICE */
+ 
+ 	//0x0003 - all packets
+	sock = socket(PF_PACKET, SOCK_RAW, htons(0x0800));
+
+	if (sock==-1)
+		goto error;
+
+	/* set socket options */
+	if (iface && iface->s){
+#if defined (SO_BINDTODEVICE)
+		/* workaround for linux bug: arg to setsockopt must have at least
+		 * sizeof(int) size or EINVAL would be returned */
+		if (iface->len<sizeof(int)){
+			memcpy(short_ifname, iface->s, iface->len);
+			short_ifname[iface->len]=0; /* make sure it's zero term */
+			ifname_len=sizeof(short_ifname);
+			ifname=short_ifname;
+		}else{
+			ifname_len=iface->len;
+			ifname=iface->s;
+		}
+		if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifname, ifname_len)
+						<0){
+				ERR("raw_socket: could not bind to %.*s: %s [%d]\n",
+							iface->len, ZSW(iface->s), strerror(errno), errno);
+				goto error;
+		}
+#else /* !SO_BINDTODEVICE */
+		/* SO_BINDTODEVICE is linux specific => cannot bind to a device */
+		ERR("raw_socket: bind to device supported only on linux\n");
+		goto error;
+#endif /* SO_BINDTODEVICE */
+	}
+
+	if(bpf_on) {
+	
+        	/* Start PORT */
+        	BPF_code[5]  = (struct my_sock_filter)BPF_JUMP(0x35, port_start, 0, 1);
+        	BPF_code[8] = (struct my_sock_filter)BPF_JUMP(0x35, port_start, 11, 13);
+        	BPF_code[16] = (struct my_sock_filter)BPF_JUMP(0x35, port_start, 0, 1);
+        	BPF_code[19] = (struct my_sock_filter)BPF_JUMP(0x35, port_start, 0, 2);
+        	/* Stop PORT */
+        	BPF_code[6]  = (struct my_sock_filter)BPF_JUMP(0x25, port_end, 0, 14);
+        	BPF_code[17] = (struct my_sock_filter)BPF_JUMP(0x25, port_end, 0, 3);	
+        	BPF_code[20] = (struct my_sock_filter)BPF_JUMP(0x25, port_end, 1, 0);			                                                
+	
+        	/* Attach the filter to the socket */
+        	if(setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &Filter, sizeof(Filter)) < 0 ) {
+                        ERR(" setsockopt filter: [%s] [%d]\n", strerror(errno), errno);
+                }		
+        }
+	
+	return sock;
+error:
+	if (sock!=-1) close(sock);
+	return -1;
+}
+
+int raw_moni_rcv_loop(int rsock, int port1, int port2) {
+
+	static char buf [BUF_SIZE+1];
+	union sockaddr_union from;
+	union sockaddr_union to;
+        struct receive_info ri;
+	int len;
+	struct ip *iph;
+        struct udphdr *udph;
+	int offset = 0; 
+	char* end;
+	unsigned short dst_port;
+	unsigned short src_port;
+	struct ip_addr dst_ip, src_ip;
+
+	for(;;){
+
+		len = recvfrom(rsock, buf, BUF_SIZE, 0x20, 0, 0);
+
+		if (len<0){
+                        if (len==-1){
+                                LOG(L_ERR, "ERROR: raw_moni_rcv_loop:recvfrom: %s [%d]\n",
+                                                strerror(errno), errno);
+                                if ((errno==EINTR)||(errno==EWOULDBLOCK))
+                                        continue;
+                        }else{
+                                DBG("raw_moni_rcv_loop: recvfrom error: %d\n", len);
+                                continue;
+                        }
+                }
+
+		end=buf+len;
+		
+		
+		if (unlikely(len<(sizeof(struct ip)+sizeof(struct udphdr) + ETHHDR))) {
+			LOG(L_ERR, "ERROR: small packet: %d\n",len);
+                	continue;
+        	}
+
+        	offset = ETHHDR;
+		
+		iph = (struct ip*) (buf + offset);				
+		offset+=iph->ip_hl*4;
+		
+		udph = (struct udphdr*)(buf + offset);
+		offset +=sizeof(struct udphdr);
+
+        	if (unlikely((buf+offset)>end)){
+                	continue;
+	                
+        	}
+        									
+		/*FIL IPs*/
+		dst_ip.af=AF_INET;
+	        dst_ip.len=4;
+        	dst_ip.u.addr32[0]=iph->ip_dst.s_addr;
+	        /* fill dst_port */
+        	dst_port=ntohs(udph->uh_dport);
+	        ip_addr2su(&to, &dst_ip, dst_port);
+        	/* fill src_port */
+	        src_port=ntohs(udph->uh_sport);
+                src_ip.af=AF_INET;
+ 	        src_ip.len=4;
+                src_ip.u.addr32[0]=iph->ip_src.s_addr;
+                ip_addr2su(&from, &src_ip, src_port);
+	        su_setport(&from, src_port);
+		
+		ri.src_su=from;
+                su2ip_addr(&ri.src_ip, &from);
+                ri.src_port=src_port;
+                su2ip_addr(&ri.dst_ip, &to);
+                ri.dst_port=dst_port;
+
+		/* cut off the offset */
+	        len -= offset;
+
+		if (len<MIN_UDP_PACKET){
+                        DBG("raw_udp4_rcv_loop: probing packet received from\n");
+                        continue;
+                }
+
+		if((src_port >= port1 && src_port <= port2) || (dst_port >= port1 && dst_port <= port2))
+		                          receive_msg(buf+offset, len, &ri);
+	}
+
+	return 0;
+}

+ 55 - 0
modules/sipcapture/sipcapture.h

@@ -44,3 +44,58 @@ struct hep_ip6hdr {
         struct in6_addr hp6_dst;        /* destination address */
 };
 #endif
+
+#define ETHHDR 14 /* sizeof of ethhdr structure */
+
+/* Make it independed */
+/* Copied from linux/filter.h */
+struct my_sock_filter      /* Filter block */
+{
+        __u16   code;   /* Actual filter code */
+        __u8    jt;     /* Jump true */
+        __u8    jf;     /* Jump false */
+        __u32   k;      /* Generic multiuse field */
+};
+
+struct my_sock_fprog       /* Required for SO_ATTACH_FILTER. */
+{
+        unsigned short          len;    /* Number of filter blocks */
+        struct my_sock_filter __user *filter;
+};
+
+#ifndef BPF_JUMP
+#define BPF_JUMP(code, k, jt, jf) { (unsigned short)(code), jt, jf, k }
+#endif
+
+/* tcpdump -s 0 udp and portrange 5060-5090 -dd */
+static struct my_sock_filter BPF_code[] = {
+		{ 0x28, 0, 0, 0x0000000c },
+		{ 0x15, 0, 7, 0x000086dd },
+		{ 0x30, 0, 0, 0x00000014 },
+		{ 0x15, 0, 18, 0x00000011 },
+		{ 0x28, 0, 0, 0x00000036 },
+		{ 0x35, 0, 1, 0x000013c4 },
+		{ 0x25, 0, 14, 0x000013e2 },
+		{ 0x28, 0, 0, 0x00000038 },
+		{ 0x35, 11, 13, 0x000013c4 },
+		{ 0x15, 0, 12, 0x00000800 },
+		{ 0x30, 0, 0, 0x00000017 },
+		{ 0x15, 0, 10, 0x00000011 },
+		{ 0x28, 0, 0, 0x00000014 },
+		{ 0x45, 8, 0, 0x00001fff },
+		{ 0xb1, 0, 0, 0x0000000e },
+		{ 0x48, 0, 0, 0x0000000e },
+		{ 0x35, 0, 1, 0x000013c4 },
+		{ 0x25, 0, 3, 0x000013e2 },
+		{ 0x48, 0, 0, 0x00000010 },
+		{ 0x35, 0, 2, 0x000013c4 },
+		{ 0x25, 1, 0, 0x000013e2 },
+		{ 0x6, 0, 0, 0x0000ffff },
+		{ 0x6, 0, 0, 0x00000000 },
+};
+
+
+static struct my_sock_fprog Filter = {
+           .len = sizeof(BPF_code) / sizeof(BPF_code[0]),
+           .filter = (struct my_sock_filter *) BPF_code,
+};