pcap(Packet capture library)的简单应用

来源:互联网 发布:previous java 编辑:程序博客网 时间:2024/05/20 18:02

pcap目前是一个应用广泛的针对网络接口数据包抓取的共享库。
该系统对数据抓取提供一个高层的接口,获得指定网络的全部
数据包,包括那些目的地址不是本机的数据包。

下面的程序是一个简单的实验用例。它只是使用了pcap的一小
部分主要功能,实现对默认网络接口数据包的抓取,并实现了
简陋的不完全的分析。它可以被视为一个残疾版或毛坯版的
tcpdump。

由于我认识上的不足和我在编程时图省事,所以不可避免的出
现一些错误和不适当的地方。

程序在Fedora 11上测试过。

编译:
    g++ -g -W -Wall -Wextra -o mytest -lpcap main.cpp

执行举例(必须使用root账户):
    ./mytest
    ./mytest "tcp and dst port 80"
    ./mytest ""
    ./mytest "tcp or udp"
    我在程序中只对选项做了简单处理。所以引号是必须的,
    否则shell会将引号中的选项展开。

参考:
    pcap(3), www.tcpdump.org上的文档


main.cpp:
============================================================================
// 2011年 03月 14日 星期一 14:24:24 CST
// author: 李小丹(Li Shao Dan) 字 殊恒(shuheng)
// K.I.S.S
// S.P.O.T
/*
*
* Copyright © 2011 李小丹(Li Shao Dan)
*
* Permission to use, copy, modify, distribute and sell this software
* and its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear
* in supporting documentation. 李小丹(Li Shao Dan) makes no
* representations about the suitability of this software for any
* purpose.  It is provided "as is" without express or implied warranty.
*/

#include <cstdio>
#include <cstdlib>
#include <cctype>

#include <errno.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>
#include <net/ethernet.h>
#include <netinet/if_ether.h>
#include <netinet/ether.h>
#include <pcap.h>


#define BUFSIZE 4096
#define PACKET_NUMBER 333
#define FILTER_EXP "ip"


static void filter_packet(unsigned char *,
        const struct pcap_pkthdr *, const unsigned char *);

static void take_ether(const unsigned char *);
static void take_ip(const unsigned char *);
static void take_tcp(const unsigned char *, size_t);
static void take_udp(const unsigned char *);
static void take_icmp(const unsigned char *);

static void print_payload(const unsigned char *, size_t);


int main(int argc, char *argv[])
{
    char *dev, errbuf[PCAP_ERRBUF_SIZE];
    dev = pcap_lookupdev(errbuf);
    if(!dev) {
        fprintf(stderr, "Can't find default device: %s/n", errbuf);
        exit(1);
    }
    printf("Device %s/n", dev);

    bpf_u_int32 mask;
    bpf_u_int32 net;
    if(pcap_lookupnet(dev, &net, &mask, errbuf) < 0) {
        fprintf(stderr, "Can't get netmask for device %s/n", dev);
        mask = 0;
        net = 0;
    }

    pcap_t *handle;
    if(!(handle = pcap_open_live(dev, BUFSIZE, 1, 1000, errbuf))) {
        fprintf(stderr, "Can't find device(%s):: %s/n", dev, errbuf);
        exit(2);
    }

    const char *filter_exp = argc == 2 ? argv[1] : FILTER_EXP;
    struct bpf_program fp;
    if(pcap_compile(handle, &fp, filter_exp, 0, net) < 0) {
        fprintf(stderr, "Couldn't parse filter %s: %s/n",
                filter_exp, pcap_geterr(handle));
        exit(3);
    }
    if(pcap_setfilter(handle, &fp) < 0) {
        fprintf(stderr, "Couldn't install filter %s: %s/n",
                filter_exp, pcap_geterr(handle));
        exit(4);
    }
    pcap_loop(handle, PACKET_NUMBER, filter_packet, 0);

    pcap_freecode(&fp);
    pcap_close(handle);

    return 0;
}



static void filter_packet(unsigned char *args __attribute__ ((unused)),
        const struct pcap_pkthdr *header __attribute__ ((unused)),
        const unsigned char *packet)
{
    static int packet_count = 1;
    printf("Number %d packet/n", packet_count++);

    take_ether(packet);

    take_ip(packet);

    printf("=================================================================/n");

    fflush(stdout);
}

static void take_ether(const unsigned char *packet)
{
    struct ether_header *ehdr = (struct ether_header *)packet;
    char mac_saddr[32];
    ether_ntoa_r((struct ether_addr *)ehdr->ether_shost, mac_saddr);
    char mac_daddr[32];
    ether_ntoa_r((struct ether_addr *)ehdr->ether_dhost, mac_daddr);
    printf("Frome MAC addr %s to MAC addr %s/n",
            mac_saddr, mac_daddr);
}

static void take_ip(const unsigned char *packet)
{
    struct iphdr *ip = (struct iphdr *)(packet + sizeof(struct ether_header));
    char ip_saddr[32];
    inet_ntop(AF_INET, &ip->saddr, ip_saddr, sizeof(ip_saddr));
    char ip_daddr[32];
    inet_ntop(AF_INET, &ip->daddr, ip_daddr, sizeof(ip_daddr));
    printf("Frome ip addr %s to ip addr %s; ttl = %d/n",
            ip_saddr, ip_daddr, ip->ttl);

    const unsigned char *p = (unsigned char *)ip + sizeof(struct iphdr);
    switch(ip->protocol) {
    case IPPROTO_TCP:
        printf("Protocol is TCP/n");
        take_tcp(p, ntohs(ip->tot_len) - 20);
        break;
    case IPPROTO_UDP:
        printf("Protocol is UDP/n");
        take_udp(p);
        break;
    case IPPROTO_ICMP:
        printf("Protocol is ICMP/n");
        take_icmp(p);
        break;
    case IPPROTO_IP:
        printf("Protocol is IP/n");
        break;
    case IPPROTO_IGMP:
        printf("Protocol is IGMP/n");
        break;
    case IPPROTO_SCTP:
        printf("Protocol is SCTP/n");
        break;
    default:
        printf("Unkown Protocol is %d/n", ip->protocol);
        break;
    }
}


static void take_tcp(const unsigned char *packet, size_t s)
{
    struct tcphdr *tcp = (struct tcphdr *)packet;
    printf("src port is %d, dst port is %d;/n",
            ntohs(tcp->source), ntohs(tcp->dest));

    size_t tcpl = tcp->doff << 2;
    if((s -= tcpl) > 0) {
        printf("Recv Tcp packet len is %u", s);
        print_payload((packet + tcpl), s);
    }
}

static void take_icmp(const unsigned char *p)
{
    struct icmphdr *icmp = (struct icmphdr *)p;
    switch(icmp->type) {
    case ICMP_ECHOREPLY:
        printf("ICMP Type is /'echo reply/'./n");
        break;
    case ICMP_DEST_UNREACH:
        printf("ICMP Type is /'Destination Unreachable/'./n");
        break;
    case ICMP_SOURCE_QUENCH:
        printf("ICMP Type is /'Source Quench/'./n");
        break;
    case ICMP_REDIRECT:
        printf("ICMP Type is /'Redirect (change route)/'./n");
        break;
    case ICMP_ECHO:
        printf("ICMP Type is /'Echo Request/'./n");
        break;
    case ICMP_TIME_EXCEEDED:
        printf("ICMP Type is /'Time Exceeded/'./n");
        break;
    case ICMP_PARAMETERPROB:
        printf("ICMP Type is /'Parameter Problem/'./n");
        break;
    case ICMP_TIMESTAMP:
        printf("ICMP Type is /'Timestamp Request/'./n");
        break;
    case ICMP_TIMESTAMPREPLY:
        printf("ICMP Type is /'Timestamp Reply/'./n");
        break;
    case ICMP_INFO_REQUEST:
        printf("ICMP Type is /'Information Request/'./n");
        break;
    case ICMP_INFO_REPLY:
        printf("ICMP Type is /'Information Reply/'./n");
        break;
    case ICMP_ADDRESS:
        printf("ICMP Type is /'Address Mask Request/'./n");
        break;
    case ICMP_ADDRESSREPLY:
        printf("ICMP Type is /'Address Mask Reply/'./n");
        break;
    default:
        printf("Unknown type is %d!/n", icmp->type);
        break;
    }
    printf("ICMP Code is /'%d/'/n", icmp->code);
}

static void take_udp(const unsigned char *p)
{
    struct udphdr *udp = (struct udphdr *)p;
    printf("Source port is %d, dest port is %d/n", ntohs(udp->source), ntohs(udp->dest));

    size_t udpl = ntohs(udp->len);
    size_t s = udpl - sizeof(struct udphdr);
    if(s > 0) {
        printf("Recv udp packet len is %u", s);
        print_payload(p + sizeof(struct udphdr), s);
    }
}

static void print_payload(const unsigned char *packet, size_t s)
{
    for(size_t i = 0; i < s; i += 10) {
        if(!(i % 10))
            printf("/n%07d   ", i);

        for(size_t j = i; j < i + 10; ++j) {
            if(j < s)
                printf("%02x ", packet[j]);
            else
                printf("   ");
        }
        printf("   ");

        for(size_t j = i; j < s && j < i + 10; ++j) {
            if(isprint(packet[j]))
                printf("%c", packet[j]);
            else
                printf(".");
        }
    }
    printf("/n");
}

原创粉丝点击