libpcap包头部信息

怎样使用(处理)packet指针变量呢?一个packet指针所指的结构包含了很多属性,它并不是一个真正的字符串,而是多个结构组成的集合(比如:一个TCP/IP数据包包括以太网头、IP包头、TCP头和数据包中有效的数据负载)。首先需要定义这些结构:

/* Ethernet header */
struct sniff_ethernet {
   u_char ether_dhost[ETHER_ADDR_LEN]; /* Destination host address */
   u_char ether_shost[ETHER_ADDR_LEN]; /* Source host address */
   u_short ether_type; /* IP? ARP? RARP? etc */
};
/* IP header */
struct sniff_ip {
   #if BYTE_ORDER == LITTLE_ENDIAN
   u_int ip_hl:4, /* header length */
   ip_v:4; /* version */
   #if BYTE_ORDER == BIG_ENDIAN
   u_int ip_v:4, /* version */
   ip_hl:4; /* header length */
   #endif
   #endif /* not _IP_VHL */
   u_char ip_tos; /* type of service */
   u_short ip_len; /* total length */
   u_short ip_id; /* identification */
   u_short ip_off; /* fragment offset field */
   #define IP_RF 0x8000 /* reserved fragment flag */
   #define IP_DF 0x4000 /* dont fragment flag */
   #define IP_MF 0x2000 /* more fragments flag */
   #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
   u_char ip_ttl; /* time to live */
   u_char ip_p; /* protocol */
   u_short ip_sum; /* checksum */
   struct in_addr ip_src,ip_dst; /* source and dest address */
};
/* TCP header */
struct sniff_tcp {
   u_short th_sport; /* source port */
   u_short th_dport; /* destination port */
   tcp_seq th_seq; /* sequence number */
   tcp_seq th_ack; /* acknowledgement number */
   #if BYTE_ORDER == LITTLE_ENDIAN
   u_int th_x2:4, /* (unused) */
   th_off:4; /* data offset */
   #endif
   #if BYTE_ORDER == BIG_ENDIAN
   u_int th_off:4, /* data offset */
   th_x2:4; /* (unused) */
   #endif
   u_char th_flags;
   #define TH_FIN 0x01
   #define TH_SYN 0x02
   #define TH_RST 0x04
   #define TH_PUSH 0x08
   #define TH_ACK 0x10
   #define TH_URG 0x20
   #define TH_ECE 0x40
   #define TH_CWR 0x80
   #define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
   u_short th_win; /* window */
   u_short th_sum; /* checksum */
   u_short th_urp; /* urgent pointer */
};

注:这些结构定义在不同的系统实现中可能存在差异,请查阅相关文档。

另:搞不懂作者什么意思,为什么非要自己定义这些结构,干嘛不用系统自己定义的实现呢?



省略了原作者关于定义这些结构的描述…



假设我们通过以太网处理TCP/IP数据包(其他的物理网络类似),如下代码将packet指针所指的结构分解为不同的结构体:

const struct sniff_ethernet *ethernet; /* The ethernet header */
const struct sniff_ip *ip; /* The IP header */
const struct sniff_tcp *tcp; /* The TCP header */
const char *payload; /* Packet payload */
/* For readability, we'll make variables for the sizes of each of the structures */
int size_ethernet = sizeof(struct sniff_ethernet);
int size_ip = sizeof(struct sniff_ip);
int size_tcp = sizeof(struct sniff_tcp);

And now we do our magical typecasting:
ethernet = (struct sniff_ethernet*)(packet);
ip = (struct sniff_ip*)(packet + size_ethernet);
tcp = (struct sniff_tcp*)(packet + size_ethernet + size_ip);
payload = (u_char *)(packet + size_ethernet + size_ip + size_tcp);

如果packet值(该指针变量所指的地址)为X,则上面所述的结构在内存中的布局如下所示:

Variable
Location (in bytes)

sniff_ethernet
X

sniff_ip
X + 14

sniff_tcp
X + 14 + 20

payload
X + 14 + 20 + 20




Wrapping up

到此为止,我们已经可以用pcap编写一个sniffer应用程序了。我们已经了解了pcap编程的基础知识,包括打开一个pcap会话句柄,处理 pcap会话句柄的属性,监听数据包,应用过滤规则,并使用回调函数定义我们自己的处理过程。随原文提供的示例程序:sniffer.c



This document is Copyright 2002 Tim Carstens. All rights reserved. Redistribution and use, with or without modification, are permitted provided that the following conditions are met: 1. Redistribution must retain the above copyright notice and this list of conditions. 2. The name of Tim Carstens may not be used to endorse or promote products derived from this document without specific prior written permission.
/* Insert 'wh00t' for the BSD license here */



附录:例用pcap编写的示例程序

/*
   编译:gcc –Wall –o example example.c -lpcap
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pcap.h>
#include <sys/types.h>
#include <netinet/ip.h>
#include <netinet/ether.h>
#include <net/ethernet.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <arpa/inet.h>


/* MACRO to print debug info */
#define DEBUG 1
#ifdef DEBUG
#define debug(stderr, msg) fprintf(stderr, msg)
#define _ ,
#else /* if no define DEBUG */
#define debug(stderr, msg)
#endif /* end of BEBUG */


#define LOOKUPDEV_ERR -1
#define OPEN_LIVE_ERR -2
#define COMPILE_ERR -3


/* protocol ID's */
#define IPPRO 8 /* IP protocol */


/* call back function invoke by pcap_loop, major process for ourselves */
void
got_packet(u_char *args, const struct pcap_pkthdr *header,
           const u_char *packet);

/* handle ethernet header */
u_int16_t
handle_ethernet(u_char *args,const struct pcap_pkthdr* pkthdr,
                const u_char* packet);

/* handle IP header */
void
handle_IP(u_char *args,const struct pcap_pkthdr* pkthdr,const u_char* packet);


int
main(int argc, char *argv[])
{
    char *dev = NULL; /* device to sniff on */
    char errbuf[PCAP_ERRBUF_SIZE]; /* buffer to store error msg */
    pcap_t *handle = NULL; /* pcap session handle */

    struct bpf_program filter; //compiled filter expression
    char filter_app[] = "port 80"; /* filter ruler for sniffing */
    bpf_u_int32 mask; //netmask of our sniffing device
    bpf_u_int32 net; //the ip of our sniffing device

    int num = 0; /* number of packets captured */

    /* variables for getopt */
    long total = -1; /* total packets to sniff */
    char *flter = filter_app; /* filter ruler for sniffing */
    int c; /* temprory char variable */

    while ((c = getopt(argc, argv, "n:f:")) != -1) {
        switch(c) {
        case 'n':
            total = atoi(optarg);
            break;
        case 'f':
            flter = optarg;
            break;
        case '?':
            fprintf(stderr, "Usage: %s -n <num> -f <filter string>\n", argv[0]);
            exit(1);
        default:
            fprintf(stdout, "Using fitler: port 80 and sniffing util interrupt by console!\n");
        }
    }

    if (NULL == (dev = pcap_lookupdev(errbuf))) {
        fprintf(stderr, "pcap_lookupdev() error: %s\n", errbuf);
        exit(LOOKUPDEV_ERR);
    }
    fprintf(stdout, "Sniffing on device: %s\n\n", dev);


    pcap_lookupnet(dev, &net, &mask, errbuf);


    /* open a new pcap session */
    if (NULL == (handle = pcap_open_live(dev, BUFSIZ, 1, 0, errbuf))) {
        fprintf(stderr, "pcap_open_live() error: %s\n", errbuf);
        exit(OPEN_LIVE_ERR);
    }

    /* compile capture rule */
    if (-1 == pcap_compile(handle, &filter, flter, 1, net)) {
        fprintf(stderr, "pcap_compile() error!\n");
        exit(COMPILE_ERR);
    }

    pcap_setfilter(handle, &filter);

/* using while + pcap_next instead of pcap_loop or pcap_dispatch */

/* while (1) {
   debug(stderr, "in pcap_next while\n");
   packet = pcap_next(handle, &header);
   printf("Captured a packet with lengthen of [%d]\n", header.len);
   debug(stderr, "The packet captured: %s\n" _ packet + header.caplen);
   }
*/

    num = pcap_loop(handle, total, got_packet, NULL);
    if (-1 == num) {
        pcap_perror(handle, "pcap_loop error: ");
    }  
    if (-2 == num) {
        pcap_perror(handle, "pcap_loop break by pcap_breakloop: ");
    }


    pcap_close(handle);

    return 0;

}


void
got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
    u_int16_t type;

    type = handle_ethernet(args, header, packet);

    debug(stderr, "protocol type: %i\n" _ type);

    switch(type) {
    case IPPRO:
        debug(stderr, "protocol type: IP\n");
        handle_IP(args, header, packet);
        break;
    case ETHERTYPE_ARP:
        /* handle arp protocol */
        break;
    case ETHERTYPE_REVARP:
        /* handle rarp protocol */
        break;
    default:
        fprintf(stdout, "Protocol is ignored\n");
    }

    fprintf(stdout,"\n");
    return;
} //end of got_packet


u_int16_t
handle_ethernet(u_char *args,const struct pcap_pkthdr* pkthdr,
                const u_char* packet)
{
    struct ether_header *eptr; /* net/ethernet.h */

    /* lets start with the ether header... */
    eptr = (struct ether_header *) packet;

    fprintf(stdout,"ETH: %s --> "
            ,ether_ntoa((struct ether_addr *)(eptr->ether_shost)));
    fprintf(stdout,"%s "
            ,ether_ntoa((struct ether_addr *)(eptr->ether_dhost)));

    /* check to see if we have an ip packet */
    if (ntohs (eptr->ether_type) == ETHERTYPE_IP)
    {
        fprintf(stdout,"(IP)");
    }else if (ntohs (eptr->ether_type) == ETHERTYPE_ARP)
    {
        fprintf(stdout,"(ARP)");
    }else if (ntohs (eptr->ether_type) == ETHERTYPE_REVARP)
    {
        fprintf(stdout,"(RARP)");
    }else {
        fprintf(stdout,"(?)");
        exit(1);
    }

    return eptr->ether_type;

} //end of handle_ethernet


void
handle_IP(u_char *args,const struct pcap_pkthdr* pkthdr,const u_char* packet)
{
    const struct iphdr *ip = (const struct iphdr *)(packet + sizeof(struct ether_header));

    u_int length = pkthdr->len;
    u_int hlen,off,version;
    int len;
    struct in_addr in;

    debug(stderr, "total pcap pkt length: %i\n" _ length);
    debug(stderr, "total pcap pkt header length: %i\n" _ pkthdr->caplen);

    /* jump pass the ethernet header */
    length =- sizeof(struct ether_header);

    /* check to see we have a packet of valid length */
    if (length < sizeof(struct iphdr))
    {
        printf("truncated ip %d",length);
        return;
    }

    len   = ntohs(ip->tot_len);
    debug(stderr, "total ip pkt length: %i\n" _ len);
    hlen   = ip->ihl; /* header length */
    debug(stderr, "ip header length: %i\n" _ hlen);
    version = ip->version;/* ip version */
    debug(stderr, "ip version: %i\n" _ version);

    /* check version */
    if(version != 4)
    {
        fprintf(stdout,"Unknown version %d\n",version);
        return ;
    }

    /* check header length */
    if(hlen < 5 )
    {
        fprintf(stdout,"bad-hlen %d \n",hlen);
    }

    /* see if we have as much packet as we should */
    if(length < len)
        printf("\ntruncated IP - %d bytes missing\n",len - length);

    /* Check to see if we have the first fragment */
    off = ntohs(ip->frag_off);
    if((off & 0x1fff) == 0 )/* aka no 1's in first 13 bits */
    {/* print SOURCE DESTINATION hlen version len offset */
        fprintf(stdout,"\nIP: ");
        in.s_addr = ip->saddr;
        fprintf(stdout,"%s ",inet_ntoa(in));
        in.s_addr = ip->daddr;
        fprintf(stdout,"%s %d %d %d %d\n",
                inet_ntoa(in),
                hlen,version,len,off);
    }
    return;
}

posted on 2010-04-22 16:44  tankzhouqiang  阅读(859)  评论(0编辑  收藏  举报