监听所有rtp/rtcp包

       监听所有的udp包,需要在数据链路层做,如果在网络层,不是发往本地的IP包都上不来; 另外,网络监听一个问题是上来的数据包太多, 处理不过来,会导致丢包,需要过滤。如果在应用层做,会有内核空间拷贝到用户空间的开销,最好内核里面做, linux已经原生提供了Linux Packet Filter。表示方式采用BPF。

      BPF 原始格式看起来像汇编语言, tcpdump -dd可以把如下的编译成BPF:

src host 192.168.2.1 and tcp port 8000-9000 #端口范围

'(((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'  #ip包长度-ip头长度-tcp头长度
'tcp[20:2]=0x4745 or tcp[20:2]=0x4854' #tcp内容开头为"GE"或"HT", http get/http ok

然后SO_ATTACH_FILTER到socket就开始过滤了。

    rtp包的特征是udp包地9个字节是0x80, rtcp是0x81, 我们根据这个来作为过滤条件。测试发现dns也可能是0x81开头,

81 93 01 00 //dns query, Transaction ID:0x8193, dst.port = 53
81 93 81 80 //dns response, srt.port = 53

因此还要加上端口号的判断[3]。

下面的代码参考[1], 删掉了tcp,icmp相关部分:

#include<errno.h>
#include<netdb.h>
#include<stdio.h> //For standard things
#include<stdlib.h>    //malloc
#include<string.h>    //strlen
 
#include<netinet/in.h>
#include<netinet/ip_icmp.h>   //Provides declarations for icmp header
#include<netinet/udp.h>   //Provides declarations for udp header
#include<netinet/tcp.h>   //Provides declarations for tcp header
#include<netinet/ip.h>    //Provides declarations for ip header
#include<netinet/if_ether.h>  //For ETH_P_ALL
#include<net/ethernet.h>  //For ether_header
#include<sys/socket.h>
#include<arpa/inet.h>
#include<sys/ioctl.h>
#include<sys/time.h>
#include<sys/types.h>
#include<unistd.h>
#include <linux/filter.h> //struct sock_fprog
#include<netpacket/packet.h> //struct packet_mreq
#include<net/if.h> //struct ifreq
 
static FILE *logfile;

void PrintData (unsigned char* data , int Size)
{
	int i , j;
	for(i=0 ; i < Size ; i++)
	{
		if( i!=0 && i%16==0)   //if one line of hex printing is complete...
		{
			fprintf(logfile , "         ");
			for(j=i-16 ; j<i ; j++)
			{
				if(data[j]>=32 && data[j]<=128)
					fprintf(logfile , "%c",(unsigned char)data[j]); //if its a number or alphabet
				
				else fprintf(logfile , "."); //otherwise print a dot
			}
			fprintf(logfile , "\n");
		} 
		
		if(i%16==0) fprintf(logfile , "   ");
			fprintf(logfile , " %02X",(unsigned int)data[i]);
				
		if( i==Size-1)  //print the last spaces
		{
			for(j=0;j<15-i%16;j++) 
			{
			  fprintf(logfile , "   "); //extra spaces
			}
			
			fprintf(logfile , "         ");
			
			for(j=i-i%16 ; j<=i ; j++)
			{
				if(data[j]>=32 && data[j]<=128) 
				{
				  fprintf(logfile , "%c",(unsigned char)data[j]);
				}
				else 
				{
				  fprintf(logfile , ".");
				}
			}
			
			fprintf(logfile ,  "\n" );
		}
	}
}

void print_udp_packet(unsigned char *Buffer , int Size)
{
     
    unsigned short iphdrlen;
    struct iphdr *iph = (struct iphdr *)(Buffer +  sizeof(struct ethhdr));
    iphdrlen = iph->ihl*4;
    struct udphdr *udph = (struct udphdr*)(Buffer + iphdrlen  + sizeof(struct ethhdr));
    int header_size =  sizeof(struct ethhdr) + iphdrlen + sizeof(*udph);
	
	char source[16], dest[16];
	inet_ntop(AF_INET, &iph->saddr, source, sizeof(source));
	inet_ntop(AF_INET, &iph->daddr, dest, sizeof(dest));

	fprintf(logfile, "UDP  %s:%u --> %s:%u len %u:%u\n", 
			source,ntohs(udph->source), 
			dest,ntohs(udph->dest), ntohs(udph->len), Size - header_size);

	//PrintData(Buffer + header_size , (Size - header_size) );
}

void ProcessPacket(unsigned char* buffer, int size)
{
	static int tcp=0,udp=0,icmp=0,others=0,igmp=0,total=0; 
    struct iphdr *iph = (struct iphdr*)(buffer + sizeof(struct ethhdr));
    ++total;
    switch (iph->protocol){
        case 1:
            ++icmp;
            break;
        case 2:
            ++igmp;
            break;
        case 6:
            ++tcp;
            break;
        case 17: 
            ++udp;
            print_udp_packet(buffer , size);
            break;
         
        default: //Some Other Protocol like ARP etc.
            ++others;
            break;
    }
    //printf("TCP : %d   UDP : %d   ICMP : %d   IGMP : %d   Others : %d   Total : %d\r", tcp , udp , icmp , igmp , others , total);
}
 
int get_iface_index(int fd, const char* iface_name)
{
	struct ifreq ifr;
	if (iface_name == NULL){
		return -1;
	}
	memset(&ifr, 0, sizeof(ifr));
	strcpy(ifr.ifr_name, iface_name);
	if(ioctl(fd, SIOCGIFINDEX, &ifr) == -1){
		printf("get index error\n");
		return -1;
	}
	return ifr.ifr_ifindex;
}

int set_iface_promisc(int fd, int dev_id)
{
	struct packet_mreq mr;
	memset(&mr,0,sizeof(mr));
	mr.mr_ifindex = dev_id;
	mr.mr_type = PACKET_MR_PROMISC;
	if(setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0){
		fprintf(stderr,"set promisc failed");
		return -1;
	}
	return 0;
}
 
int main(int ac, char **av)
{
    int saddr_size , data_size;
    struct sockaddr saddr;
    unsigned char *buffer = (unsigned char *)malloc(65536); 
	char *iface_name = ac > 1 ? av[1] : "eth0";
    
	if(ac > 2){
		logfile = fopen(av[2],"w");
	}else{
		logfile = stdout;
	}
    
    if(!logfile){
        printf("Unable to create log.txt file.");
		return -1;
    }
    
	/*ETH_P_IP |ETH_P_ARP -- cant receive multicast*/	
    int sock_raw = socket( AF_PACKET , SOCK_RAW , htons(ETH_P_ALL)); 
    if(sock_raw < 0){
        perror("Socket Error");
        return 1;
    }

	int iface_idx = get_iface_index(sock_raw, iface_name);
    setsockopt(sock_raw , SOL_SOCKET , SO_BINDTODEVICE , iface_name , strlen(iface_name)+1);
	set_iface_promisc(sock_raw, iface_idx);

	struct sock_filter BPF_code[] = {
//sudo tcpdump -s 65535 -dd 'udp && udp[0:2] > 1024 && udp[2:2] > 1024 && (udp[8] == 0x80 || udp[8] == 0x81)'
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 14, 0, 0x000086dd },
{ 0x15, 0, 13, 0x00000800 },
{ 0x30, 0, 0, 0x00000017 },
{ 0x15, 0, 11, 0x00000011 },
{ 0x28, 0, 0, 0x00000014 },
{ 0x45, 9, 0, 0x00001fff },
{ 0xb1, 0, 0, 0x0000000e },
{ 0x48, 0, 0, 0x0000000e },
{ 0x25, 0, 6, 0x00000400 },
{ 0x48, 0, 0, 0x00000010 },
{ 0x25, 0, 4, 0x00000400 },
{ 0x50, 0, 0, 0x00000016 },
{ 0x15, 1, 0, 0x00000080 },
{ 0x15, 0, 1, 0x00000081 },
{ 0x6, 0, 0, 0x0000ffff },
{ 0x6, 0, 0, 0x00000000 },
	};
     
	struct sock_fprog sfilter = {
		.len = sizeof(BPF_code)/sizeof(BPF_code[0]),
		.filter = BPF_code,
	};

	int ret = setsockopt(sock_raw, SOL_SOCKET, SO_ATTACH_FILTER, &sfilter, sizeof(sfilter));
	if(ret < 0){
		perror("attach filter");
		return -1;
	}

    while(1){
        saddr_size = sizeof saddr;
        data_size = recvfrom(sock_raw , buffer , 65536 , 0 , &saddr, (socklen_t*)&saddr_size);
        if(data_size < 0){
            printf("failed to get packets\n");
            return 1;
        }
        ProcessPacket(buffer, data_size);
    }
    close(sock_raw);
    return 0;
}


windows下只能从IP层开始过滤[2], 如果非要统一linux和windows, 目前可选择libpcap/winpcap。网上说linux libpcap 默认的mem map方式可能会丢包, windows版的有一些限制,暂时不打算统一搞。

[1] http://www.binarytides.com/packet-sniffer-code-in-c-using-linux-sockets-bsd-part-2/

[2] http://blog.csdn.net/cyberhero/article/details/5785158

[3] https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers#Well-known_ports

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值