监听所有rtp/rtcp包

来源:互联网 发布:ubuntu修改ssh端口 编辑:程序博客网 时间:2024/06/05 09:34

       监听所有的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 alphabetelse 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